From de11f3987e7032fc583a6e1b785138abc85eea4b Mon Sep 17 00:00:00 2001 From: Zhengke Zhou Date: Fri, 19 Jul 2024 06:35:51 +0800 Subject: [PATCH] [Feature] Introduce external link e2e test (#3888) --- .github/workflows/e2e.yml | 2 + .../service/impl/ExternalLinkServiceImpl.java | 8 +- .../e2e/cases/ExternalLinkPageTest.java | 129 +++++++++++++++ .../e2e/pages/setting/ExternalLinkPage.java | 152 ++++++++++++++++++ .../e2e/pages/setting/SettingPage.java | 15 +- 5 files changed, 299 insertions(+), 7 deletions(-) create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkPageTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/ExternalLinkPage.java diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index bae4755687..de945a34c1 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -110,6 +110,8 @@ jobs: strategy: matrix: case: + - name: ExternalLinkPageTest + class: org.apache.streampark.e2e.cases.ExternalLinkPageTest - name: YarnQueueTest class: org.apache.streampark.e2e.cases.YarnQueueTest - name: TokenManagementTest diff --git a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java index 0550cb0c4c..d2eae02af1 100644 --- a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java +++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java @@ -18,6 +18,7 @@ package org.apache.streampark.console.core.service.impl; import org.apache.streampark.common.util.AssertUtils; +import org.apache.streampark.console.base.exception.ApiAlertException; import org.apache.streampark.console.core.entity.Application; import org.apache.streampark.console.core.entity.ExternalLink; import org.apache.streampark.console.core.enums.PlaceholderTypeEnum; @@ -107,12 +108,11 @@ private boolean check(ExternalLink params) { if (result == null) { return true; } - AssertUtils.required( - !result.getBadgeName().equals(params.getBadgeName()), + ApiAlertException.throwIfTrue(result.getBadgeName().equals(params.getBadgeName()), String.format("The name: %s is already existing.", result.getBadgeName())); - AssertUtils.required( - !result.getLinkUrl().equals(params.getLinkUrl()), + ApiAlertException.throwIfTrue(result.getLinkUrl().equals(params.getLinkUrl()), String.format("The linkUrl: %s is already existing.", result.getLinkUrl())); + return false; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkPageTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkPageTest.java new file mode 100644 index 0000000000..ef28a06831 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkPageTest.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamPark; +import org.apache.streampark.e2e.pages.LoginPage; +import org.apache.streampark.e2e.pages.setting.ExternalLinkPage; +import org.apache.streampark.e2e.pages.setting.SettingPage; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.shaded.org.awaitility.Awaitility; + +import static org.assertj.core.api.Assertions.assertThat; + +@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +public class ExternalLinkPageTest { + + private static RemoteWebDriver browser; + + private static final String userName = "admin"; + + private static final String password = "streampark"; + + private static final String teamName = "default"; + + private static final String newLabel = "new_label"; + + private static final String editLabel = "edit_label"; + + private static final String newName = "new_name"; + + private static final String color = "#b54f4f"; + + private static final String newLink = "https://grafana/flink-monitoring?var-JobId=var-JobId=1"; + + @BeforeAll + public static void setup() { + new LoginPage(browser) + .login(userName, password, teamName) + .goToNav(SettingPage.class) + .goToTab(ExternalLinkPage.class); + } + + @Test + @Order(10) + void testCreateExternalLink() { + final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); + externalLinkPage.createExternalLink(newLabel, newName, color, newLink); + + Awaitility.await() + .untilAsserted( + () -> assertThat(externalLinkPage.externalLinkList()) + .as("External link list should contain newly-created link") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(newLabel)) + .anyMatch(it -> it.contains(newName)) + .anyMatch(it -> it.contains(newLink))); + } + + @Test + @Order(20) + void testCreateDuplicateExternalLink() { + final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); + externalLinkPage.createExternalLink(newLabel, newName, color, newLink); + + Awaitility.await() + .untilAsserted( + () -> assertThat(externalLinkPage.errorMessageList()) + .as("Name Duplicated Error message should be displayed") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains( + String.format("The name: %s is already existing.", newName)))); + + externalLinkPage.errorMessageConfirmButton().click(); + externalLinkPage.createExternalLinkForm().buttonCancel().click(); + } + + @Test + @Order(30) + void testEditExternalLink() { + final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); + String editName = "edit_name"; + String editLink = "https://grafana/flink-monitoring?var-Job=var-Job=edit"; + externalLinkPage.editExternalLink(newLabel, editLabel, editName, color, editLink); + + Awaitility.await() + .untilAsserted( + () -> assertThat(externalLinkPage.externalLinkList()) + .as("External link list should contain edited link") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(editLabel)) + .anyMatch(it -> it.contains(editName)) + .anyMatch(it -> it.contains(editLink))); + } + + @Test + @Order(40) + void testDeleteExternalLink() { + final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); + + externalLinkPage.deleteExternalLink(editLabel); + + Awaitility.await() + .untilAsserted( + () -> { + assertThat(externalLinkPage.externalLinkList()) + .noneMatch(it -> it.getText().contains(editLabel)); + }); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/ExternalLinkPage.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/ExternalLinkPage.java new file mode 100644 index 0000000000..cfe753b55e --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/ExternalLinkPage.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.streampark.e2e.pages.setting; + +import org.apache.streampark.e2e.pages.common.Constants; +import org.apache.streampark.e2e.pages.common.NavBarPage; + +import lombok.Getter; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.util.List; + +@Getter +public class ExternalLinkPage extends NavBarPage implements SettingPage.Tab { + + @FindBy(xpath = "//span[contains(., 'External Link')]/..//button[contains(@class, 'ant-btn-dashed')]/span[contains(text(), 'Add New')]") + private WebElement buttonCreateExternalLink; + + @FindBy(xpath = "//tbody[contains(@class, 'ant-table-tbody')]") + private List externalLinkList; + + @FindBy(className = "swal2-html-container") + private List errorMessageList; + + @FindBy(xpath = "//button[contains(text(), 'OK')]") + private WebElement errorMessageConfirmButton; + + @FindBy(xpath = "//button[contains(@class, 'ant-btn')]/span[contains(., 'OK')]") + private WebElement deleteConfirmButton; + + private final CreateExternalLinkForm createExternalLinkForm = new CreateExternalLinkForm(); + + public ExternalLinkPage(RemoteWebDriver driver) { + super(driver); + } + + public ExternalLinkPage createExternalLink(String label, String name, String color, String link) { + waitForPageLoading(); + + new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION) + .until(ExpectedConditions.elementToBeClickable(buttonCreateExternalLink)); + buttonCreateExternalLink.click(); + createExternalLinkForm.inputLabel.sendKeys(label); + createExternalLinkForm.inputName.sendKeys(name); + createExternalLinkForm.inputColor.sendKeys(color); + createExternalLinkForm.inputLink.sendKeys(link); + + createExternalLinkForm.buttonSubmit().click(); + return this; + } + + public ExternalLinkPage editExternalLink(String label, String editLabel, String editName, String color, + String editLink) { + waitForPageLoading(); + + externalLinkList().stream() + .filter(it -> it.getText().contains(label)) + .flatMap( + it -> it.findElements(By.xpath("//button[contains(@class, 'ant-btn-link')]//span[text()='Edit']/..")) + .stream()) + .filter(WebElement::isDisplayed) + .findFirst() + .orElseThrow(() -> new RuntimeException("No edit button in external link list")) + .click(); + + createExternalLinkForm.inputLabel().clear(); + createExternalLinkForm.inputLabel().sendKeys(editLabel); + createExternalLinkForm.inputName().clear(); + createExternalLinkForm.inputName().sendKeys(editName); + createExternalLinkForm.inputColor().clear(); + createExternalLinkForm.inputColor().sendKeys(color); + createExternalLinkForm.inputLink().clear(); + createExternalLinkForm.inputLink().sendKeys(editLink); + + createExternalLinkForm.buttonSubmit().click(); + return this; + } + + public ExternalLinkPage deleteExternalLink(String label) { + waitForPageLoading(); + + externalLinkList().stream() + .filter(it -> it.getText().contains(label)) + .flatMap( + it -> it + .findElements(By.xpath("//button[contains(@class, 'ant-btn-dangerous')]//span[text()='Delete']/..")) + .stream()) + .filter(WebElement::isDisplayed) + .findFirst() + .orElseThrow(() -> new RuntimeException("No delete button in External link list")) + .click(); + + new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION) + .until(ExpectedConditions.elementToBeClickable(deleteConfirmButton)); + + deleteConfirmButton.click(); + + return this; + } + + private void waitForPageLoading() { + new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION) + .until(ExpectedConditions.urlContains("/setting/extlink")); + } + + @Getter + public class CreateExternalLinkForm { + + CreateExternalLinkForm() { + PageFactory.initElements(driver, this); + } + + @FindBy(id = "form_item_badgeLabel") + private WebElement inputLabel; + + @FindBy(id = "form_item_badgeName") + private WebElement inputName; + + @FindBy(id = "form_item_badgeColor") + private WebElement inputColor; + + @FindBy(id = "form_item_linkUrl") + private WebElement inputLink; + + @FindBy(xpath = "//button[contains(@class, 'ant-btn')]//span[contains(., 'Submit')]") + private WebElement buttonSubmit; + + @FindBy(xpath = "//button[contains(@class, 'ant-btn')]//span[contains(., 'Cancel')]") + private WebElement buttonCancel; + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/SettingPage.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/SettingPage.java index 1883191624..c3ead14a2e 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/SettingPage.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/setting/SettingPage.java @@ -17,6 +17,7 @@ package org.apache.streampark.e2e.pages.setting; +import org.apache.streampark.e2e.pages.common.Constants; import org.apache.streampark.e2e.pages.common.NavBarPage; import lombok.Getter; @@ -26,26 +27,34 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; -import java.time.Duration; - @Getter public class SettingPage extends NavBarPage implements NavBarPage.NavBarItem { @FindBy(xpath = "//span[contains(@class, 'streampark-simple-menu-sub-title') and contains(text(), 'Yarn Queue')]//..") private WebElement menuYarnQueueManagement; + @FindBy(xpath = "//span[contains(@class, 'streampark-simple-menu-sub-title') and contains(text(), 'External Link')]//..") + private WebElement menuExternalLinkManagement; + public SettingPage(RemoteWebDriver driver) { super(driver); } public T goToTab(Class tab) { if (tab == YarnQueuePage.class) { - new WebDriverWait(driver, Duration.ofSeconds(10)) + new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION) .until(ExpectedConditions.elementToBeClickable(menuYarnQueueManagement)); menuYarnQueueManagement.click(); return tab.cast(new YarnQueuePage(driver)); } + if (tab == ExternalLinkPage.class) { + new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION) + .until(ExpectedConditions.elementToBeClickable(menuExternalLinkManagement)); + menuExternalLinkManagement.click(); + return tab.cast(new ExternalLinkPage(driver)); + } + throw new UnsupportedOperationException("Unknown tab: " + tab.getName()); }