You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by ka...@apache.org on 2021/07/02 16:19:56 UTC

[submarine] branch master updated: SUBMARINE-882. fix datadictIT

This is an automated email from the ASF dual-hosted git repository.

kaihsun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new 1fa0a76  SUBMARINE-882. fix datadictIT
1fa0a76 is described below

commit 1fa0a762e17ce7d8ae12c51b387dacdaa05015a7
Author: noidname01 <ti...@gmail.com>
AuthorDate: Fri Jul 2 22:07:43 2021 +0800

    SUBMARINE-882. fix datadictIT
    
    ### What is this PR for?
    In travis.ci, this IT will have undeterministic behavior, it is because of the original way to realize the waiting is fluent wait, but it lacks of expected condition.
    The other reason of unstable behavior of this test is because of the wrong interaction with components.
    
    https://user-images.githubusercontent.com/55401762/124241557-6e561780-db4e-11eb-8ab4-53a04ffc583e.mp4
    
    As you can see, this drop-down list need to be hovered. But this IT clicked it. This might be the source of unstable.
    So it need to be modify with the functions that use explicit wait in AbstractSubmarineIT.
    Also found that the window need to set larger to prevent element from disappearing in viewpoint, which makes headless driver can not interact.
    Note: the default of size of headless window maximize is (600, 800), when driver hover the target out of this area, it will cause MoveTargetOutOfBounds exception.
    
    ### What type of PR is it?
    [Bug Fix | Refactoring]
    
    ### Todos
    
    ### What is the Jira issue?
    
    https://issues.apache.org/jira/projects/SUBMARINE/issues/SUBMARINE-882
    
    ### How should this be tested?
    ```bash
    # Step1: Run Submarine workbench on 127.0.0.1:8080 by operator
    # Step2: Run testcase
    cd submarine-cloud-v2
    
    # Usage: ./hack/run_frontend_e2e.sh ${testcase}
    # Example:
    ./hack/run_frontend_e2e.sh datadictIT
    ```
    ### Screenshots (if appropriate)
    
    https://user-images.githubusercontent.com/55401762/124000807-8c186500-da06-11eb-8289-faf054a85650.mp4
    
    ### Questions:
    * Do the license files need updating? No
    * Are there breaking changes for older versions? No
    * Does this need new documentation? No
    
    Author: noidname01 <ti...@gmail.com>
    
    Signed-off-by: Kai-Hsun Chen <ka...@apache.org>
    
    Closes #627 from noidname01/SUBMARINE-882 and squashes the following commits:
    
    e736ecce [noidname01] add Hover, HoverAndClick
    277ff426 [noidname01] set screen bigger
    b9f90cb4 [noidname01] add scroll
    6fa22aa2 [noidname01] try to fix bug
    e64d9ab7 [noidname01] found where unstable-moreBtn
    bae44f03 [noidname01] merge with 878
    b3c72257 [noidname01] add more comment to increase readability
    5d22c2b1 [noidname01] datadictIT refactor
    c329d788 [noidname01] datadictIT refactor
---
 .../org/apache/submarine/AbstractSubmarineIT.java  |  16 ++
 .../org/apache/submarine/WebDriverManager.java     |   5 +-
 .../apache/submarine/integration/datadictIT.java   | 211 +++++++++++++--------
 .../submarine/integration/pages/DataDictPage.java  |  95 ++++++++++
 4 files changed, 244 insertions(+), 83 deletions(-)

diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java
index a64042c..b10a438 100644
--- a/submarine-test/test-e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java
@@ -37,6 +37,7 @@ import org.openqa.selenium.support.ui.Wait;
 import org.openqa.selenium.support.ui.WebDriverWait;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.openqa.selenium.interactions.Actions;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -48,6 +49,7 @@ import java.util.concurrent.TimeUnit;
 
 abstract public class AbstractSubmarineIT {
   protected static WebDriver driver;
+  protected static Actions action;
 
   protected final static Logger LOG = LoggerFactory.getLogger(AbstractSubmarineIT.class);
   protected static final long MIN_IMPLICIT_WAIT = 5;
@@ -124,6 +126,20 @@ abstract public class AbstractSubmarineIT {
     return button;
   }
 
+  protected WebElement Hover(final By locator, final long timeWait) {
+    waitToPresent(locator, timeWait);
+    WebElement item = buttonCheck(locator, timeWait);
+    action.moveToElement(item).build().perform();
+    return item;
+  }
+
+  protected WebElement HoverAndClick(final By locator, final long timeWait) {
+    waitToPresent(locator, timeWait);
+    WebElement item = buttonCheck(locator, timeWait);
+    action.moveToElement(item).click().build().perform();
+    return item;
+  }
+
   protected void takeScreenShot(final String path) {
     File scrFile1 = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
     try {
diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/WebDriverManager.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/WebDriverManager.java
index beebbde..9bdd241 100644
--- a/submarine-test/test-e2e/src/test/java/org/apache/submarine/WebDriverManager.java
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/WebDriverManager.java
@@ -29,6 +29,7 @@ import org.openqa.selenium.support.ui.ExpectedCondition;
 import org.openqa.selenium.support.ui.WebDriverWait;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.openqa.selenium.Dimension;
 
 
 public class WebDriverManager {
@@ -111,8 +112,8 @@ public class WebDriverManager {
     if (loaded == false) {
       fail();
     }
-
-    driver.manage().window().maximize();
+    Dimension d = new Dimension(1920, 1080);
+    driver.manage().window().setSize(d);
     return driver;
   }
 
diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/datadictIT.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/datadictIT.java
index 638a97c..306658a 100644
--- a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/datadictIT.java
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/datadictIT.java
@@ -20,6 +20,7 @@ package org.apache.submarine.integration;
 import org.apache.submarine.AbstractSubmarineIT;
 import org.apache.submarine.integration.pages.LoginPage;
 import org.apache.submarine.integration.components.Sidebars;
+import org.apache.submarine.integration.pages.DataDictPage;
 import org.apache.submarine.WebDriverManager;
 import org.openqa.selenium.By;
 import org.openqa.selenium.support.ui.ExpectedConditions;
@@ -29,15 +30,22 @@ import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
+
 
 public class datadictIT extends AbstractSubmarineIT {
 
   public final static Logger LOG = LoggerFactory.getLogger(datadictIT.class);
 
+
   @BeforeClass
   public static void startUp(){
     LOG.info("[Testcase]: datadictIT");
     driver =  WebDriverManager.getWebDriver();
+    action = new Actions(driver);
   }
 
   @AfterClass
@@ -45,111 +53,152 @@ public class datadictIT extends AbstractSubmarineIT {
     driver.quit();
   }
 
-  // @Test TODO(kevin85421): Due to the undeterministic behavior of travis, I decide to comment it.
+  @Test
   public void dataDictTest() throws Exception {
+    DataDictPage dataDictPage = new DataDictPage();
     String URL = getURL("http://127.0.0.1", 8080);
-
     Sidebars sidebars = new Sidebars(URL);
     LoginPage loginPage = new LoginPage();
+    String dictCode, newItemCode, newItemName, newPostfix;
 
     // Login
     LOG.info("Login");
     loginPage.Login();
-    
+
     // Start Routing & Navigation in data-dict
     LOG.info("Start Routing & Navigation in data-dict");
     sidebars.gotoDataDict();
-    WebDriverWait wait = new WebDriverWait( driver, 60);
-    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//span[@class='ant-breadcrumb-link ng-star-inserted']")));
-    Assert.assertEquals(driver.getCurrentUrl(), URL.concat("/workbench/manager/dataDict"));
 
     // Add button
     LOG.info("[TEST] Add button");
-    // Add --> Ok --> required feedback
-    pollingWait(By.cssSelector("form > nz-form-item:nth-child(3) > nz-form-control > div > span > button.ant-btn.ant-btn-default"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//div[contains(text(), \"Add\")]")).size(), 1);
-    pollingWait(By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//div[contains(text(), \"Add\")]")).size(), 1);
-    // Add --> Close
-    pollingWait(By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-default']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//div[contains(text(), \"Add\")]")).size(), 0);
-    // Add --> set input --> close
-    pollingWait(By.cssSelector("form > nz-form-item:nth-child(3) > nz-form-control > div > span > button.ant-btn.ant-btn-default"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//input[@id='inputNewDictCode']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict code");
-    pollingWait(By.xpath("//input[@id='inputNewDictName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict name");
-    pollingWait(By.xpath("//input[@id='inputNewDictDescription']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict description");
-    pollingWait(By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-default']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[@id='dataDictCodetest new dict code']")).size(), 0);
-    // Add --> set input --> ok --> new dict
-    pollingWait(By.cssSelector("form > nz-form-item:nth-child(3) > nz-form-control > div > span > button.ant-btn.ant-btn-default"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//input[@id='inputNewDictCode']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict code");
-    pollingWait(By.xpath("//input[@id='inputNewDictName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict name");
-    pollingWait(By.xpath("//input[@id='inputNewDictDescription']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("test new dict description");
-    pollingWait(By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[@id='dataDictCodetest new dict code']")).size(), 1);
+    // // 1. Add --> Ok --> required feedback --> close
+    // Add
+    Click(dataDictPage.addBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // check if the modal doesn't disappear
+    Assert.assertEquals(driver.findElements(By.xpath("//div[contains(text(), \"Add\")]")).size(), 1);
+    // Close
+    Click(dataDictPage.closeBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // check if the model disappear
+    Assert.assertEquals(driver.findElements(By.xpath("//div[contains(text(), \"Add\")]")).size(), 0);
+
+    // 2. Add --> set input --> close
+    // Add
+    Click(dataDictPage.addBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // Set input
+    SendKeys(dataDictPage.dictCodeInput, MAX_BROWSER_TIMEOUT_SEC, "test new dict code");
+    SendKeys(dataDictPage.dictNameInput, MAX_BROWSER_TIMEOUT_SEC, "test new dict name");
+    SendKeys(dataDictPage.dictDescription, MAX_BROWSER_TIMEOUT_SEC, "test new dict description");
+    // close
+    Click(dataDictPage.closeBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // check there is no new dict
+    Assert.assertEquals(driver.findElements(By.xpath("//td[@id='dataDictCodetest new dict code']")).size(), 0);
+
+    // 3. Add --> set input --> ok --> new dict
+    // dataDictPage.addNewDict("test new dict code", "test new dict name", "test new dict description", false);
+    // Add
+    Click(dataDictPage.addBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // Set input
+    SendKeys(dataDictPage.dictCodeInput, MAX_BROWSER_TIMEOUT_SEC, "test new dict code");
+    SendKeys(dataDictPage.dictNameInput, MAX_BROWSER_TIMEOUT_SEC, "test new dict name");
+    SendKeys(dataDictPage.dictDescription, MAX_BROWSER_TIMEOUT_SEC, "test new dict description");
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // check there is new dict
+    Assert.assertEquals(driver.findElements(By.xpath("//td[@id='dataDictCodetest new dict code']")).size(), 1);
 
     // Configuration button
     LOG.info("[TEST] PROJECT_TYPE Configuration button");
-    // old dict --> More --> Configuration --> Add --> set input --> OK
-    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@id='dataDictMorePROJECT_TYPE']")));
-    pollingWait(By.xpath("//a[@id='dataDictMorePROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictConfigurationPROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//button[@id='dataDictItemAddPROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//span[@class='ant-cascader-picker-label']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@title='unavailable']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//input[@id='newItemCodePROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("qqq");
-    pollingWait(By.xpath("//input[@id='newItemNamePROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("www");
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-default ant-btn-sm']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"qqq\")]")).size(), 1);
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    // Check old dict
-    pollingWait(By.xpath("//a[@id='dataDictMorePROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictConfigurationPROJECT_TYPE']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"qqq\")]")).size(), 1);
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
+
+    // 3. old dict --> More --> Configuration --> Add --> set input --> OK
+    dictCode = "PROJECT_TYPE";
+    newItemCode = "qqq";
+    newItemName = "www";
+    // More
+    Hover(dataDictPage.moreBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Configuration
+    HoverAndClick(dataDictPage.configBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Add Item
+    waitToPresent(dataDictPage.itemAddBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.itemAddBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Set Input
+    Click(dataDictPage.statusDropDown, MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.statusUnavailable, MAX_BROWSER_TIMEOUT_SEC);
+    SendKeys(dataDictPage.itemCodeInput(dictCode), MAX_BROWSER_TIMEOUT_SEC, newItemCode);
+    SendKeys(dataDictPage.itemNameInput(dictCode), MAX_BROWSER_TIMEOUT_SEC, newItemName);
+    Click(dataDictPage.addActionBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
+
+    // check if add successful
+    Hover(dataDictPage.moreBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    HoverAndClick(dataDictPage.configBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    Assert.assertEquals(driver.findElements(By.xpath(String.format("//td[contains(text(), \"%s\")]", newItemCode))).size(), 1);
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
 
     // Edit button
     LOG.info("[TEST] Edit button");
-    // Edit dict --> Update --> OK --> More --> Configuration
-    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@id='dataDictEditPROJECT_VISIBILITY']")));
-    pollingWait(By.xpath("//a[@id='dataDictEditPROJECT_VISIBILITY']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//input[@id='inputNewDictCode']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("123");
-    pollingWait(By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[@id='dataDictCodePROJECT_VISIBILITY123']")).size(), 1);
-    pollingWait(By.xpath("//a[@id='dataDictMorePROJECT_VISIBILITY123']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictConfigurationPROJECT_VISIBILITY123']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"PROJECT_VISIBILITY_PRIVATE\")]")).size(), 1);
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"PROJECT_VISIBILITY_TEAM\")]")).size(), 1);
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"PROJECT_VISIBILITY_PUBLIC\")]")).size(), 1);
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
+
+    // 4. Edit dict --> Update --> OK --> More --> Configuration --> OK
+    dictCode = "PROJECT_VISIBILITY";
+    newPostfix = "123";
+    // Edit
+    waitToPresent(dataDictPage.editBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.editBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Update
+    SendKeys(dataDictPage.dictCodeInput, MAX_BROWSER_TIMEOUT_SEC, newPostfix);
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // More
+    Hover(dataDictPage.moreBtn(dictCode.concat(newPostfix)), MAX_BROWSER_TIMEOUT_SEC);
+    // Configuration
+    HoverAndClick(dataDictPage.configBtn(dictCode.concat(newPostfix)), MAX_BROWSER_TIMEOUT_SEC);
+
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
 
     LOG.info("[TEST] test new dict code Configuration button");
-    // new dict --> More --> Configuration --> Add --> set input --> OK
-    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@id='dataDictMoretest new dict code']")));
-    pollingWait(By.xpath("//a[@id='dataDictMoretest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictConfigurationtest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//button[@id='dataDictItemAddtest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//span[@class='ant-cascader-picker-label']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@title='available']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//input[@id='newItemCodetest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("aaa");
-    pollingWait(By.xpath("//input[@id='newItemNametest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("bbb");
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-default ant-btn-sm']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"aaa\")]")).size(), 1);
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    // Check new dict
-    pollingWait(By.xpath("//a[@id='dataDictMoretest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictConfigurationtest new dict code']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[contains(text(), \"aaa\")]")).size(), 1);
-    pollingWait(By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
+
+    // 5. new dict --> More --> Configuration --> Add --> set input --> OK
+    dictCode = "test new dict code";
+    newItemCode = "aaa";
+    newItemName = "bbb";
+
+    // More
+    Hover(dataDictPage.moreBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Configuration
+    HoverAndClick(dataDictPage.configBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Add Item
+    waitToPresent(dataDictPage.itemAddBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.itemAddBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Set Input
+    Click(dataDictPage.statusDropDown, MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.statusAvailable, MAX_BROWSER_TIMEOUT_SEC);
+    SendKeys(dataDictPage.itemCodeInput(dictCode), MAX_BROWSER_TIMEOUT_SEC, newItemCode);
+    SendKeys(dataDictPage.itemNameInput(dictCode), MAX_BROWSER_TIMEOUT_SEC, newItemName);
+    Click(dataDictPage.addActionBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // Ok
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
+
+    // check if add successful
+    Hover(dataDictPage.moreBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    HoverAndClick(dataDictPage.configBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    Assert.assertEquals(driver.findElements(By.xpath(String.format("//td[contains(text(), \"%s\")]", newItemCode))).size(), 1);
+    Click(dataDictPage.okBtn, MAX_BROWSER_TIMEOUT_SEC);
 
     // Delete button
     LOG.info("[TEST] Delete button");
-    // More --> Delete
-    Assert.assertEquals( driver.findElements(By.xpath("//td[@id='dataDictCodeSYS_USER_SEX']")).size(), 1);
-    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@id='dataDictMoreSYS_USER_SEX']")));
-    pollingWait(By.xpath("//a[@id='dataDictMoreSYS_USER_SEX']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//li[@id='dataDictDeleteSYS_USER_SEX']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    pollingWait(By.xpath("//span[text()='Ok']/ancestor::button[@ng-reflect-nz-type='primary']"), MAX_BROWSER_TIMEOUT_SEC).click();
-    Assert.assertEquals( driver.findElements(By.xpath("//td[@id='dataDictCodeSYS_USER_SEX']")).size(), 0);
+
+    // 6. More --> Delete
+    dictCode = "SYS_USER_SEX";
+    // More
+    Hover(dataDictPage.moreBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    // Delete
+    HoverAndClick(dataDictPage.deleteBtn(dictCode), MAX_BROWSER_TIMEOUT_SEC);
+    waitToPresent(dataDictPage.okDeleteBtn, MAX_BROWSER_TIMEOUT_SEC);
+    Click(dataDictPage.okDeleteBtn, MAX_BROWSER_TIMEOUT_SEC);
+    // check if delete successfully
+    Assert.assertEquals(driver.findElements(By.xpath(String.format("//td[@id='dataDictCode%s']", dictCode))).size(), 0);
   }
 }
diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/pages/DataDictPage.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/pages/DataDictPage.java
new file mode 100644
index 0000000..676056c
--- /dev/null
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/pages/DataDictPage.java
@@ -0,0 +1,95 @@
+/*
+ * 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.submarine.integration.pages;
+
+import org.apache.submarine.AbstractSubmarineIT;
+import org.testng.Assert;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.PageFactory;
+import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.Select;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataDictPage extends AbstractSubmarineIT {
+
+    public By addBtn = By.cssSelector("form > nz-form-item:nth-child(3) > nz-form-control > div > span > button.ant-btn.ant-btn-default");
+
+    public By okBtn = By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-primary']");
+
+    public By okDeleteBtn = By.xpath("//span[text()='Ok']/ancestor::button[@ng-reflect-nz-type='primary']");
+
+    public By closeBtn = By.cssSelector("button[class='ant-btn ng-star-inserted ant-btn-default']");
+
+    public By dictCodeInput = By.xpath("//input[@id='inputNewDictCode']");
+
+    public By dictNameInput = By.xpath("//input[@id='inputNewDictName']");
+
+    public By dictDescription = By.xpath("//input[@id='inputNewDictDescription']");
+
+    // for configuration jump out modal
+    public By statusDropDown = By.xpath("//span[@class='ant-cascader-picker-label']");
+
+    public By statusAvailable = By.xpath("//li[@title='available']");
+
+    public By statusUnavailable = By.xpath("//li[@title='unavailable']");
+
+    public By addActionBtn = By.xpath("//button[@class='ant-btn ng-star-inserted ant-btn-default ant-btn-sm']");
+
+    public By moreBtn(String dict_code) {
+        String xpath = String.format("//a[@id='dataDictMore%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By deleteBtn(String dict_code) {
+        String xpath = String.format("//li[@id='dataDictDelete%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By configBtn(String dict_code) {
+        String xpath = String.format("//li[@id='dataDictConfiguration%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By editBtn(String dict_code) {
+        String xpath = String.format("//a[@id='dataDictEdit%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By itemAddBtn(String dict_code) {
+        String xpath = String.format("//button[@id='dataDictItemAdd%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By itemCodeInput(String dict_code) {
+        String xpath = String.format("//input[@id='newItemCode%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+    public By itemNameInput(String dict_code) {
+        String xpath = String.format("//input[@id='newItemName%s']", dict_code);
+        return By.xpath(xpath);
+    }
+
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org