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

liuxiaochun pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 1cadb1a  Add e2e automated testing (#2005)
1cadb1a is described below

commit 1cadb1a2511174f59c28ebffb6223989865c3c19
Author: xingchun-chen <[email protected]>
AuthorDate: Wed Feb 26 11:08:55 2020 +0800

    Add e2e automated testing (#2005)
    
    * add ui-test
    
    * add e2e license and notice
    
    * e2e delete dolphinscheduler parent project dependency
    
    * pom.xml delete e2e dependency
    
    Co-authored-by: chenxingchun 
<[email protected]>
---
 dolphinscheduler-dist/pom.xml                      |   2 -
 e2e/pom.xml                                        | 137 ++++++++
 .../dolphinscheduler/constant/TestConstant.java    |  37 +++
 .../dolphinscheduler/util/PropertiesReader.java    |  49 +++
 .../apache/dolphinscheduler/util/RedisUtil.java    | 204 ++++++++++++
 .../apache/dolphinscheduler/base/BaseDriver.java   | 133 ++++++++
 .../org/apache/dolphinscheduler/base/BaseTest.java | 107 +++++++
 .../dolphinscheduler/common/BrowserCommon.java     | 353 +++++++++++++++++++++
 .../apache/dolphinscheduler/common/PageCommon.java |  50 +++
 .../apache/dolphinscheduler/data/LoginData.java    |  43 +++
 .../data/project/CreatProjectData.java             |  26 ++
 .../data/project/CreatWorkflowData.java            |  36 +++
 .../data/security/TenantManageData.java            |  55 ++++
 .../data/security/UserManageData.java              |  34 ++
 .../dolphinscheduler/locator/LoginLocator.java     |  33 ++
 .../locator/project/CreateProjectLocator.java      |  36 +++
 .../locator/project/CreateWorkflowLocator.java     |  97 ++++++
 .../locator/security/TenantManageLocator.java      |  33 ++
 .../locator/security/UserManageLocator.java        |  45 +++
 .../apache/dolphinscheduler/page/LoginPage.java    |  65 ++++
 .../page/project/CreateProjectPage.java            |  58 ++++
 .../page/project/CreateWorkflowPage.java           | 108 +++++++
 .../page/security/TenantManagePage.java            |  69 ++++
 .../page/security/UserManagePage.java              |  67 ++++
 .../dolphinscheduler/testcase/LoginTest.java       |  37 +++
 .../testcase/project/CreateProjectTest.java        |  36 +++
 .../testcase/project/CreateWorkflowTest.java       |  34 ++
 .../testcase/security/TenantManageTest.java        |  34 ++
 .../testcase/security/UserManageTest.java          |  34 ++
 e2e/src/test/resources/config/config.properties    |  58 ++++
 e2e/suite.xml                                      |  18 ++
 e2e/testng.xml                                     |  22 ++
 pom.xml                                            |   1 -
 33 files changed, 2148 insertions(+), 3 deletions(-)

diff --git a/dolphinscheduler-dist/pom.xml b/dolphinscheduler-dist/pom.xml
index b43daff..80ddfec 100644
--- a/dolphinscheduler-dist/pom.xml
+++ b/dolphinscheduler-dist/pom.xml
@@ -41,7 +41,6 @@
             <groupId>org.apache.dolphinscheduler</groupId>
             <artifactId>dolphinscheduler-api</artifactId>
         </dependency>
-
     </dependencies>
 
     <profiles>
@@ -97,7 +96,6 @@
                             </execution>
                         </executions>
                     </plugin>
-
                 </plugins>
             </build>
         </profile>
diff --git a/e2e/pom.xml b/e2e/pom.xml
new file mode 100644
index 0000000..ac9cea9
--- /dev/null
+++ b/e2e/pom.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.dolphinscheduler-e2e</groupId>
+    <artifactId>dolphinscheduler-e2e</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <testng.version>6.14.3</testng.version>
+        <selenium.version>3.141.59</selenium.version>
+        <guava.version>22.0</guava.version>
+        <commons-io.version>2.6</commons-io.version>
+        <reportng.version>1.1.4</reportng.version>
+        <commons-pool2.version>2.8.0</commons-pool2.version>
+        <inject.version>4.2.1</inject.version>
+        <xml.file>testng.xml</xml.file>
+    </properties>
+
+    <dependencies>
+        <!-- selenium -->
+        <dependency>
+            <groupId>org.seleniumhq.selenium</groupId>
+            <artifactId>selenium-java</artifactId>
+            <version>${selenium.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <version>${inject.version}</version>
+        </dependency>
+        <!-- testng -->
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>${testng.version}</version>
+        </dependency>
+        <!-- Depends on reportNg, associated with testNg-->
+        <dependency>
+            <groupId>org.uncommons</groupId>
+            <artifactId>reportng</artifactId>
+            <version>${reportng.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.testng</groupId>
+                    <artifactId>testng</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>${commons-io.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.jedis</artifactId>
+            <version>2.6.0_2-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+            <version>${commons-pool2.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.0</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.20.1</version>
+                <configuration>
+                    <suiteXmlFiles>
+                        <suiteXmlFile>${xml.file}</suiteXmlFile>
+                    </suiteXmlFiles>
+                    <argLine>-Dfile.encoding=UTF-8</argLine>
+                    <properties>
+                        <property>
+                            <name>usedefaultlisteners</name>
+                            <!--Set listen false, Do not use the default 
report testng, use reportng-->
+                            <value>false</value>
+                        </property>
+                        <property>
+                            <name>listener</name>
+                            
<value>org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter</value>
+                        </property>
+                    </properties>
+                    <!--<workingDirectory>target/</workingDirectory>-->
+                    <!--<forkMode>always</forkMode>-->
+                </configuration>
+            </plugin>
+
+
+            <!--<plugin>-->
+                <!--<groupId>org.apache.maven.plugins</groupId>-->
+                <!--<artifactId>maven-surefire-plugin</artifactId>-->
+                <!--<version>2.5</version>-->
+                <!--<configuration>-->
+                    <!--<properties>-->
+                        <!--<property>-->
+                            <!--<name>usedefaultlisteners</name>-->
+                            <!--&lt;!&ndash;Set listen false, Do not use the 
default report testng, use reportng&ndash;&gt;-->
+                            <!--<value>false</value>-->
+                        <!--</property>-->
+                        <!--<property>-->
+                            <!--<name>listener</name>-->
+                            
<!--<value>org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter</value>-->
+                        <!--</property>-->
+                    <!--</properties>-->
+                    <!--<workingDirectory>target/</workingDirectory>-->
+                    <!--<forkMode>always</forkMode>-->
+                <!--</configuration>-->
+            <!--</plugin>-->
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git 
a/e2e/src/main/java/org/apache/dolphinscheduler/constant/TestConstant.java 
b/e2e/src/main/java/org/apache/dolphinscheduler/constant/TestConstant.java
new file mode 100644
index 0000000..8a3b8eb
--- /dev/null
+++ b/e2e/src/main/java/org/apache/dolphinscheduler/constant/TestConstant.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dolphinscheduler.constant;
+
+public class TestConstant {
+    /**
+     * 1000
+     */
+    public static final int ONE_THOUSANG = 1000;
+
+
+
+    /**
+     * 3000
+     */
+    public static final int THREE_THOUSANG = 3000;
+
+    /**
+     * 10000
+     */
+    public static final int TEN_THOUSANG = 10000;
+
+}
diff --git 
a/e2e/src/main/java/org/apache/dolphinscheduler/util/PropertiesReader.java 
b/e2e/src/main/java/org/apache/dolphinscheduler/util/PropertiesReader.java
new file mode 100644
index 0000000..cbf7d07
--- /dev/null
+++ b/e2e/src/main/java/org/apache/dolphinscheduler/util/PropertiesReader.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dolphinscheduler.util;
+
+import java.io.*;
+import java.util.Properties;
+
+/**
+ * read properties
+ */
+public class PropertiesReader {
+    private static Properties properties = new Properties();
+
+    /**
+     * @param propertiesPath properties path
+     * @return Properties
+     * @throws IOException IOException
+     */
+    public static Properties readProperties(String propertiesPath) throws 
IOException {
+        System.out.println("read properties ");
+        InputStream inputStream = new FileInputStream(propertiesPath);
+        InputStreamReader inputStreamReader = new 
InputStreamReader(inputStream, "UTF-8");
+        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+        properties.load(bufferedReader);
+        return properties;
+    }
+
+    /***
+     * @param key key
+     * @return value
+     */
+    public static String getKey(String key) {
+        return properties.getProperty(key);
+    }
+}
diff --git a/e2e/src/main/java/org/apache/dolphinscheduler/util/RedisUtil.java 
b/e2e/src/main/java/org/apache/dolphinscheduler/util/RedisUtil.java
new file mode 100644
index 0000000..7f9340f
--- /dev/null
+++ b/e2e/src/main/java/org/apache/dolphinscheduler/util/RedisUtil.java
@@ -0,0 +1,204 @@
+/*
+ * 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.dolphinscheduler.util;
+
+import org.apache.dolphinscheduler.constant.TestConstant;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * redis util
+ */
+public class RedisUtil {
+    /**
+     * redis  ip
+     */
+    private static String redisIp;
+
+    /**
+     * redis port
+     */
+    private static int redisPort;
+
+    /**
+     * redis password
+     */
+    private static String redisPwd;
+
+    /**
+     * redis pool config
+     */
+    private static JedisPoolConfig jedisPoolConfig;
+
+    /**
+     * redis pool
+     */
+    private static JedisPool jedisPool;
+
+    /**
+     * jedis connection
+     */
+    private Jedis jedis;
+
+    /**
+     * jedis expire time
+     */
+    private int jedisExpireTime;
+
+    /**
+     * jedis max total
+     */
+    private static int jedisPoolMaxTotal;
+
+    /**
+     * jedis max idle
+     */
+    private static int jedisPoolMaxIdle;
+
+    /**
+     * jedis max wait time
+     */
+    private static int jedisPoolMaxWaitMillis;
+
+    /**
+     * Whether to perform a valid check when calling the borrowObject method
+     */
+    private static boolean jedisPoolTestOnBorrow;
+
+    /**
+     * Whether to perform a valid check when calling the returnObject method
+     */
+    private static boolean jedisPoolTestOnReturn;
+
+    /**
+     * storage local thread
+     */
+    public static ThreadLocal<Jedis> threadLocal = new ThreadLocal<>();
+
+    /*
+     * redis init
+     */
+    static {
+        // redis properties
+        redisIp = PropertiesReader.getKey("redis.ip");
+        redisPort = Integer.valueOf(PropertiesReader.getKey("redis.port"));
+        redisPwd = PropertiesReader.getKey("redis.pwd");
+        //redis pool  properties
+        jedisPoolMaxTotal = 
Integer.valueOf(PropertiesReader.getKey("jedis.pool.maxTotal"));
+        jedisPoolMaxIdle = 
Integer.valueOf(PropertiesReader.getKey("jedis.pool.maxIdle"));
+        jedisPoolMaxWaitMillis = 
Integer.valueOf(PropertiesReader.getKey("jedis.pool.maxWaitMillis"));
+        jedisPoolTestOnBorrow = 
Boolean.valueOf(PropertiesReader.getKey("jedis.pool.testOnBorrow"));
+        jedisPoolTestOnReturn = 
Boolean.valueOf(PropertiesReader.getKey("jedis.pool.testOnReturn"));
+        // redis pool start properties
+        jedisPoolConfig = new JedisPoolConfig();
+        jedisPoolConfig.setMaxTotal(jedisPoolMaxTotal);
+        jedisPoolConfig.setMaxIdle(jedisPoolMaxIdle);
+        jedisPoolConfig.setMaxWaitMillis(jedisPoolMaxWaitMillis);
+        jedisPoolConfig.setTestOnBorrow(jedisPoolTestOnBorrow);
+        jedisPoolConfig.setTestOnReturn(jedisPoolTestOnReturn);
+        // connect redis
+        try {
+            System.out.println("redis init");
+            if (redisPwd.isEmpty())
+                jedisPool = new JedisPool(jedisPoolConfig, redisIp, redisPort, 
TestConstant.THREE_THOUSANG);
+            else {
+                jedisPool = new JedisPool(jedisPoolConfig, redisIp, redisPort, 
TestConstant.TEN_THOUSANG, redisPwd);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("redis connect fail");
+        }
+    }
+
+    /**
+     * get redis pool
+     *
+     * @return redis pool
+     */
+    public static JedisPool getJedisPool() {
+        return jedisPool;
+    }
+
+    /**
+     * get jedis connection
+     *
+     * @return jedis connection
+     */
+    public Jedis getNewJedis() {
+        Jedis newJedis = null;
+        try {
+            newJedis = jedisPool.getResource();
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("redis connection fail");
+        }
+        System.out.println("redis connection success");
+        return newJedis;
+    }
+
+    /**
+     * get jedis connection
+     *
+     * @return jedis connection
+     */
+    public Jedis getJedis() {
+        return jedis;
+    }
+
+    public void setJedisAndExpire(Jedis jedis) {
+        this.jedis = jedis;
+        threadLocal.set(jedis);
+        // jedis expire time(s)
+        jedisExpireTime = 
Integer.valueOf(PropertiesReader.getKey("jedis.expireTime"));
+        System.out.println("redisUtil sets up a redis connection");
+    }
+
+    /**
+     * set key
+     *
+     * @param key key
+     * @param value value
+     *
+     */
+
+    public void setKey(String key, String value) {
+        jedis.set(key, value);
+        // set expire time 1h
+        jedis.expire(key, jedisExpireTime);
+    }
+
+    /**
+     * get key
+     *
+     * @param key key
+     * @return value
+     */
+    public String getKey(String key) {
+        return jedis.get(key);
+    }
+
+    /**
+     * Return jedis connection
+     */
+    public void returnJedis() {
+        if (jedis != null) {
+            jedis.close();
+        }
+        System.out.println("jedis has been returned");
+    }
+}
diff --git a/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseDriver.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseDriver.java
new file mode 100644
index 0000000..c1397b6
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseDriver.java
@@ -0,0 +1,133 @@
+/*
+ * 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.dolphinscheduler.base;
+
+
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.util.PropertiesReader;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.chrome.ChromeDriver;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * base driver class
+ */
+public class BaseDriver {
+    /**
+     * driver
+     */
+    private WebDriver driver;
+
+    /**
+     * chrome driver path
+     */
+    private String chromeDriverPath;
+
+
+    /**
+     * implicitly wait time(s)
+     */
+    private long implicitlyWait;
+
+    /**
+     * page load timeout(s)
+     */
+    private long pageLoadTimeout;
+
+    /**
+     * script Timeout(s)
+     */
+    private long setScriptTimeout;
+
+
+    /**
+     * Local thread storage is used to store the driver
+     */
+    public static ThreadLocal<WebDriver> threadLocal = new ThreadLocal<>();
+
+    /**
+     *Initialization parameters
+     */
+    public BaseDriver() throws IOException {
+        /* driver test class path */
+        chromeDriverPath = PropertiesReader.getKey("driver.chromeDriver");
+
+        /* wait time */
+        implicitlyWait = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.implicitlyWait"));
+        pageLoadTimeout = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.pageLoadTimeout"));
+        setScriptTimeout = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.setScriptTimeout"));
+    }
+
+
+    /**
+     * start chrome browser
+     */
+    public void startBrowser() throws Exception {
+        // set chrome driver
+        System.setProperty("webdriver.chrome.driver", chromeDriverPath);
+        driver = new ChromeDriver();
+
+        /* driver setting wait time */
+        // implicitly wait time
+        driver.manage().timeouts().implicitlyWait(implicitlyWait, 
TimeUnit.SECONDS);
+        // page load timeout
+        driver.manage().timeouts().pageLoadTimeout(pageLoadTimeout, 
TimeUnit.SECONDS);
+        // script timeout
+        driver.manage().timeouts().setScriptTimeout(setScriptTimeout, 
TimeUnit.SECONDS);
+
+        // window maximize
+        driver.manage().window().maximize();
+
+        // set threadLocal
+        threadLocal.set(driver);
+    }
+
+    /**
+     * get webDriver
+     *
+     * @return driver
+     */
+    public WebDriver getDriver() {
+        return driver;
+    }
+
+    /**
+     * set webDriver
+     *
+     * @param driver driver
+     */
+    public void setDriver(WebDriver driver) {
+        this.driver = driver;
+        // Thread local storage
+        threadLocal.set(driver);
+    }
+
+    /**
+     * close browser
+     */
+    public void closeBrowser() throws InterruptedException {
+        // JS Show a pop-up box to indicate the end of the test
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+        ((JavascriptExecutor) driver).executeScript("alert('Test completed, 
browser closes after 3s')");
+        Thread.sleep(TestConstant.THREE_THOUSANG);
+        if (driver != null) {
+            driver.quit();
+        }
+    }
+}
diff --git a/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseTest.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseTest.java
new file mode 100644
index 0000000..c12c19f
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/base/BaseTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dolphinscheduler.base;
+
+
+import org.apache.dolphinscheduler.page.LoginPage;
+import org.apache.dolphinscheduler.util.PropertiesReader;
+import org.openqa.selenium.WebDriver;
+import org.testng.annotations.*;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ *  base test class
+ */
+public class BaseTest {
+    /**
+     * properties
+     */
+    private static Properties properties;
+
+
+    /**
+     * baseDriver
+     */
+    private BaseDriver baseDriver;
+
+    /**
+     * driver
+     */
+    public WebDriver driver;
+
+    /**
+     * Executed before executing a test suite 
+     * Read the test configuration file
+     *
+     * @param propertiesPath properties path
+     * @throws IOException IOException
+     */
+    @BeforeSuite(alwaysRun = true)
+    @Parameters({"propertiesPath"})
+    public void 
beforeSuite(@Optional("src/test/resources/config/config.properties") String 
propertiesPath) throws IOException {
+        // read properties
+        properties = PropertiesReader.readProperties(propertiesPath);
+    }
+
+    /**
+     * Executed before executing a testcase
+     */
+    @BeforeTest(alwaysRun = true)
+    public void beforeTest() throws Exception {
+        //base driver
+        baseDriver = new BaseDriver();
+        baseDriver.startBrowser();
+        driver = baseDriver.getDriver();
+    }
+
+    /**
+     * Executed before executing a class method in a test case
+     */
+    @BeforeClass(alwaysRun = true)
+    public void setUp() throws IOException, InterruptedException {
+        LoginPage loginPage = new LoginPage(driver);
+        loginPage.jumpPage();
+        loginPage.login();
+    }
+
+
+    /**
+     * Execute after executing a class method in a test case
+     */
+    @AfterClass(alwaysRun = true)
+    public void afterClass() {
+        // logout
+    }
+
+    /**
+     * Execute after executing a testcase
+    */
+    @AfterTest(alwaysRun = true)
+    public void afterTest() throws InterruptedException {
+        // close browser
+        baseDriver.closeBrowser();
+    }
+
+    /**
+     * Execute after executing a testsuite
+     */
+    @AfterSuite(alwaysRun = true)
+    public void afterSuite() {
+    }
+}
\ No newline at end of file
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/common/BrowserCommon.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/common/BrowserCommon.java
new file mode 100644
index 0000000..fb16162
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/common/BrowserCommon.java
@@ -0,0 +1,353 @@
+/*
+ * 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.dolphinscheduler.common;
+
+import org.apache.dolphinscheduler.util.PropertiesReader;
+import org.apache.dolphinscheduler.util.RedisUtil;
+import org.openqa.selenium.*;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import redis.clients.jedis.Jedis;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Encapsulate the most basic operations on the interface in the browser
+ */
+public class BrowserCommon {
+    /**
+     * driver
+     */
+    protected WebDriver driver;
+
+    /**
+     * actions
+     */
+    protected Actions actions;
+
+    /**
+     * Javascript
+     */
+    protected JavascriptExecutor je;
+
+    /**
+     * Show wait
+     */
+    protected WebDriverWait wait;
+
+    /**
+     * Jedis
+     */
+    protected Jedis jedis;
+
+    /**
+     * redis util
+     */
+    protected RedisUtil redisUtil;
+
+    /**
+     * @param driver driver
+     */
+    public BrowserCommon(WebDriver driver) {
+        this.driver = driver;
+        this.actions = new Actions(driver);
+        this.je = ((JavascriptExecutor) driver);
+        // show wait timeout
+        long timeout = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.webDriverWait"));
+        wait = new WebDriverWait(driver, timeout);
+    }
+
+    /**
+     * @param driver driver
+     * @param jedis jedis
+     */
+    public BrowserCommon(WebDriver driver, Jedis jedis) {
+        this.driver = driver;
+        this.actions = new Actions(driver);
+        this.je = ((JavascriptExecutor) driver);
+        // show wait timeout
+        long timeout = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.webDriverWait"));
+        wait = new WebDriverWait(driver, timeout);
+        this.jedis = jedis;
+    }
+
+    /**
+     * @param driver driver
+     * @param redisUtil redisUtil
+     */
+    public BrowserCommon(WebDriver driver, RedisUtil redisUtil) {
+        this.driver = driver;
+        this.actions = new Actions(driver);
+        this.je = ((JavascriptExecutor) driver);
+        // show wait timeout
+        long timeout = 
Long.valueOf(PropertiesReader.getKey("driver.timeouts.webDriverWait"));
+        wait = new WebDriverWait(driver, timeout);
+//        this.redisUtil = redisUtil;
+//        this.jedis = redisUtil.getJedis();
+    }
+
+
+    /**
+     * Get WebElement element object through element positioning
+     *
+     * @param locator By
+     * @return WebElement
+     */
+
+    public WebElement locateElement(By locator) {
+        return 
wait.until(ExpectedConditions.presenceOfElementLocated(locator));
+    }
+
+    /**
+     * Click button element
+     * @param locator By
+     * @return clickButton
+     */
+    public WebElement clickButton(By locator) {
+        WebElement buttonElement = locateElement(locator);
+        wait.until(ExpectedConditions.elementToBeClickable(locator));
+        ExpectedConditions.elementToBeClickable(locator);
+        buttonElement.click();
+        return buttonElement;
+    }
+
+    /**
+     * Click element
+     *
+     * @param locator By
+     * @return inputElement
+     */
+    public WebElement clickElement(By locator) {
+        WebElement clickElement = locateElement(locator);
+        clickElement.click();
+        return clickElement;
+    }
+
+    /**
+     * input element
+     *
+     * @param locator By
+     * @param content Input content
+     * @return inputElement
+     */
+    public WebElement sendInput(By locator, String content) {
+        WebElement inputElement = locateElement(locator);
+        inputElement.clear();
+        inputElement.sendKeys(content);
+        return inputElement;
+    }
+    /**
+     * clear element
+     *
+     * @param locator By
+     */
+    public void clearInput(By locator) {
+        WebElement clearElement = locateElement(locator);
+        clearElement.click();
+        clearElement.clear();
+        clearElement.sendKeys(Keys.chord(Keys.CONTROL, "a"));
+        clearElement.sendKeys(Keys.DELETE);
+    }
+
+
+    /**
+     * move to element
+     *
+     * @param locator BY
+     * @return actions
+     */
+    public Actions moveToElement(By locator) {
+        return actions.moveToElement(locateElement(locator));
+    }
+
+    /**
+     * mouse drag  element
+     *
+     * @param source_locator BY
+     * @param target_locator BY
+     * @param X  X-axis
+     * @param Y Y-axis
+     */
+    public void dragAndDropBy(By source_locator, By target_locator, int X, int 
Y) {
+        WebElement sourcetElement = locateElement(source_locator);
+        WebElement targetElement = locateElement(target_locator);
+        actions.dragAndDrop(sourcetElement, 
targetElement).moveToElement(targetElement, X, Y).perform();
+        actions.release();
+    }
+
+
+    /**
+     * jump page
+     *
+     * @param url url
+     */
+    public void jumpPage(String url) {
+        driver.get(url);
+    }
+
+
+    /**
+     * Find the next handle, recommended for two windows
+     *
+     * @return driver
+     */
+    public WebDriver switchNextHandle() {
+        // Current window handle
+        String currentHandle = driver.getWindowHandle();
+        // All window handle
+        Set<String> allHandles = driver.getWindowHandles();
+        // Finding the next handle
+        for (String handle : allHandles) {
+            if (!handle.equals(currentHandle)) {
+                return driver.switchTo().window(handle);
+            }
+        }
+        return driver;
+    }
+
+    /**
+     * Multi-window switch handle, according to the handle number passed in
+     *
+     * @param num Number starts from 1
+     * @return driver
+     */
+    public WebDriver switchHandle(int num) {
+        // current handle
+        String currentHandle = driver.getWindowHandle();
+        // all handle
+        Set<String> allHandlesSet = driver.getWindowHandles();
+        List<String> allHandlesList = new ArrayList<>(allHandlesSet);
+        // switch handle
+        return driver.switchTo().window(allHandlesList.get(num - 1));
+    }
+
+    /**
+     * Switch frame structure
+     *
+     * @param locator frame
+     * @return driver
+     */
+    public WebDriver switchFrame(By locator) {
+        return driver.switchTo().frame(locateElement(locator));
+    }
+
+    /**
+     * Switch parent frame structure
+     *
+     * @return driver
+     */
+    public WebDriver switchParentFrame() {
+        return driver.switchTo().parentFrame();
+    }
+
+    /**
+     * Switch out of frame structure
+     *
+     * @return driver
+     */
+    public WebDriver switchOutOfFrame() {
+        return driver.switchTo().defaultContent();
+    }
+
+
+    /**
+     * execute JS Script
+     *
+     * @param script JS script
+     */
+    public void executeScript(String script) {
+        je.executeScript(script);
+    }
+
+    /**
+     * execute JS Script
+     *
+     * @param script JS script
+     * @param args   Object element array
+     */
+    public void executeScript(String script, Object... args) {
+        je.executeScript(script, args);
+    }
+
+    /**
+     * Page slide to top
+     */
+    public void scrollToTop() {
+        executeScript("window.scrollTo(0, 0)");
+    }
+
+    /**
+     * Page slides to the bottom
+     */
+    public void scrollToBottom() {
+        executeScript("window.scrollTo(0, document.body.scrollHeight)");
+    }
+
+    /**
+     * Page swipe makes the top of the element align with the top of the page
+     *
+     * @param by Elements that need to be aligned with the top of the page
+     */
+    public void scrollElementTopToTop(By by) {
+        executeScript("arguments[0].scrollIntoView(true);", 
driver.findElement(by));
+    }
+
+    /**
+     * Page sliding makes the bottom of the element aligned with the bottom of 
the page
+     *
+     * @param by Elements that need to be aligned with the bottom of the page
+     */
+    public void scrollElementBottomToBottom(By by) {
+        executeScript("arguments[0].scrollIntoView(false);", 
driver.findElement(by));
+    }
+
+
+    /**
+     * Determine if the current page title is the specified title
+     *
+     * @param title  title
+     * @return boolean
+     */
+
+    public boolean ifTitleIs(String title) {
+        return wait.until(ExpectedConditions.titleIs(title));
+    }
+
+    /**
+     * Determines whether the current page title contains the specified text
+     *
+     * @param text text
+     * @return boolean
+     */
+    public boolean ifTitleContains(String text) {
+        return wait.until(ExpectedConditions.titleContains(text));
+    }
+
+    /**
+     * Determines whether the text value of an element on the current page is 
the specified text
+     *
+     * @param locator By
+     * @param text text
+     * @return boolean
+     */
+    public boolean ifTextExists(By locator, String text) {
+        return 
wait.until(ExpectedConditions.textToBePresentInElementLocated(locator, text));
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/common/PageCommon.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/common/PageCommon.java
new file mode 100644
index 0000000..72dc03a
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/common/PageCommon.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dolphinscheduler.common;
+
+import org.apache.dolphinscheduler.util.RedisUtil;
+import org.openqa.selenium.WebDriver;
+import redis.clients.jedis.Jedis;
+
+
+/**
+ * Encapsulate the operation methods that can be used for each module page
+ */
+public class PageCommon extends BrowserCommon {
+    /**
+     * @param driver driver
+     */
+    public PageCommon(WebDriver driver) {
+        super(driver);
+    }
+
+    /**
+     * @param driver driver
+     * @param jedis jedis
+     */
+    public PageCommon(WebDriver driver, Jedis jedis) {
+        super(driver, jedis);
+    }
+
+    /**
+     * @param driver driver
+     * @param redisUtil redisUtil
+     */
+    public PageCommon(WebDriver driver, RedisUtil redisUtil) {
+        super(driver, redisUtil);
+    }
+}
diff --git a/e2e/src/test/java/org/apache/dolphinscheduler/data/LoginData.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/LoginData.java
new file mode 100644
index 0000000..e56df5a
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/data/LoginData.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dolphinscheduler.data;
+
+import org.apache.dolphinscheduler.util.PropertiesReader;
+
+/**
+ * Landing page object: data
+ */
+
+public class LoginData {
+    /**
+     * Login URL
+     */
+    public static final String URL = PropertiesReader.getKey("LOGIN_URL");
+
+
+    /**
+     * Login username
+     */
+    public static final String USER = PropertiesReader.getKey("USER_NAME");
+
+    /**
+     * Login password
+     */
+    public static final String PASSWORD = PropertiesReader.getKey("PASSWORD");
+
+    public static final String TENANT = "租户管理 - DolphinScheduler";
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatProjectData.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatProjectData.java
new file mode 100644
index 0000000..8f6c9c8
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatProjectData.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dolphinscheduler.data.project;
+
+public class CreatProjectData {
+    // create project name
+    public static final String PROJECT_NAME = "selenium_project";
+    // create project description
+    public static final String DESCRIPTION = "test create project description";
+    // project page title
+    public static final String PROJECT_TITLE = "项目 - DolphinScheduler";
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatWorkflowData.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatWorkflowData.java
new file mode 100644
index 0000000..1e45d47
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/project/CreatWorkflowData.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dolphinscheduler.data.project;
+
+public class CreatWorkflowData {
+    //input shell task name
+    public static final String SHELL_TASK_NAME = "shell task description test";
+
+    //input shell task description
+    public static final String SHELL_TASK_DESCRIPTION = "shell task 
description test";
+
+    //input timeout
+    public static final String INPUT_TIMEOUT = "60";
+
+    //input shell script
+    public static final String SHELL_SCRIPT = "echo 1111111";
+
+    public static final String WORKFLOW_TITLE = "创建流程定义 - DolphinScheduler";
+
+
+
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/data/security/TenantManageData.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/security/TenantManageData.java
new file mode 100644
index 0000000..b791c07
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/security/TenantManageData.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/**
+ * Tenant page object: data
+ */
+package org.apache.dolphinscheduler.data.security;
+
+import org.apache.dolphinscheduler.data.LoginData;
+
+public class TenantManageData {
+    /**
+     * Tenant URL
+     */
+    public static final String TENANAT_URL =  LoginData.URL + 
"/ui/#/security/tenant";
+
+    /**
+     * Tenant Code
+     */
+    public static final String TENANAT_CODE = "dolphinscheduler_tenant_code2";
+
+    /**
+     * Tenant Name
+     */
+    public static final String TENANAT_NAME = "dolphinscheduler_tenant_Name";
+
+    /**
+     * Queue
+     */
+    public static final String QUEUE = "default";
+
+    /**
+     * Description
+     */
+    public static final String DESCRIPTION = "creat tenant test";
+
+    public static final String TENANAT_MANAGE = "租户管理 - DolphinScheduler";
+
+
+
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/data/security/UserManageData.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/security/UserManageData.java
new file mode 100644
index 0000000..03c985f
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/data/security/UserManageData.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.data.security;
+
+import org.apache.dolphinscheduler.data.LoginData;
+
+public class UserManageData  {
+    public static final String USER_URL = LoginData.URL + 
"/ui/#/security/users";
+
+    public static final String USERNAME = "selenium111";
+
+    public static final String PASSWORD = "123456qwe";
+
+    public static final String EMAIL = "[email protected]";
+
+    public static final String PHONE = "15811112222";
+
+    public static final String USER_MANAGE = "用户管理 - DolphinScheduler";
+
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/locator/LoginLocator.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/LoginLocator.java
new file mode 100644
index 0000000..32a82bb
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/locator/LoginLocator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dolphinscheduler.locator;
+
+import org.openqa.selenium.By;
+
+/**
+ * Page object: element positioning
+ */
+
+public class LoginLocator {
+    public static final By LOGIN_INPUT_USER = 
By.xpath("//input[@class='input-element suffix']");
+
+    public static final By LOGIN_INPUT_PASSWORD = 
By.xpath("//input[@class='input-element suffix']");
+
+    public static final By LOGIN_BUTTON = By.xpath("//button");
+
+    public static final By LOGIN_BUTTON_MOVE = By.xpath("//button[contains(.,' 
Loading...')]");
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateProjectLocator.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateProjectLocator.java
new file mode 100644
index 0000000..d2c0d84
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateProjectLocator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dolphinscheduler.locator.project;
+
+import org.openqa.selenium.By;
+
+public class CreateProjectLocator {
+    //click project manage
+    public static final By PROJECT_MANAGE = By.xpath("//div[2]/div/a/span");
+
+    //click create project button
+    public static final By CREATE_PROJECT_BUTTON = By.xpath("//button/span");
+
+    //input project name
+    public static final By PROJECT_NAME = 
By.xpath("//div[2]/div/div/div[2]/div/input");
+
+    //input project description
+    public static final By PROJECT_DESCRIPTION = By.xpath("//textarea");
+
+    //submit button
+    public static final By SUBMIT_BUTTON = By.xpath("//div[3]/button[2]/span");
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateWorkflowLocator.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateWorkflowLocator.java
new file mode 100644
index 0000000..3bea648
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/project/CreateWorkflowLocator.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dolphinscheduler.locator.project;
+
+import org.openqa.selenium.By;
+
+public class CreateWorkflowLocator {
+    // click project manage
+    public static final By CLICK_PROJECT_MANAGE = 
By.xpath("//div[2]/div/a/span");
+
+    // click project name
+    public static final By CLICK_PROJECT_NAME = By.xpath("//span/a");
+
+    // click workflow define
+    public static final By CLICK_WORKFLOW_DEFINE = By.xpath("//li/span");
+
+    // click create workflow button
+    public static final By CLICK_CREATE_WORKFLOW_BUTTON = 
By.xpath("//button/span");
+
+    //mouse down at shell
+    public static final By MOUSE_DOWN_AT_SHELL = 
By.xpath("//*[@id='SHELL']/div/div");
+
+    //mouse down at spark
+    public static final By MOUSE_DOWN_AT_SPARK = By.xpath("//div[5]/div/div");
+
+    //mouse move at DAG
+    public static final By MOUSE_MOVE_SHELL_AT_DAG = 
By.xpath("//div[2]/div/div[2]/div[2]/div/div");
+
+//    //click shell task
+//    public static final By CLICK_SHELL_TASK = 
By.xpath("//div[2]/div/div[2]/div[2]/div/div");
+
+    //input shell task _name
+    public static final By INPUT_SHELL_TASK_NAME = By.xpath("//input");
+
+    //click stop run type
+    public static final By CLICK_STOP_RUN_TYPE = 
By.xpath("//label[2]/span/input");
+
+    //click normal run type
+    public static final By CLICK_NORMAL_RUN_TYPE = By.xpath("//span/input");
+
+    //input shell task description
+    public static final By INPUT_SHELL_TASK_DESCRIPTION = 
By.xpath("//label/div/textarea");
+
+    //click task priority
+    public static final By CLICK_TASK_PRIORITY = 
By.xpath("//span/div/div/div/div/div");
+
+    //select task priority
+    public static final By SELECT_TASK_PRIORITY = By.xpath("//li[2]/li/span");
+
+    //click work group
+    public static final By CLICK_WORK_GROUP = By.xpath("//div/div/input");
+
+    //select work group
+    public static final By SELECT_WORK_GROUP = 
By.xpath("//div[4]/div[2]/div/div[1]/div/input");
+
+    //select number of failed retries
+    public static final By SELECT_FAIL_RETRIES_NUMBER = 
By.xpath("//div[5]/div[2]/div[1]/div[1]/div/input");
+
+    //select failed retry interval
+    public static final By SELECT_FAIL_RETRIES_INTERVAL = 
By.xpath("//div[5]/div[2]/div[2]/div[1]/div/input");
+
+    //click timeout alarm
+    public static final By CLICK_TIMEOUT_ALARM = 
By.xpath("//label/div/span/span");
+
+    //select timeout fail
+    public static final By SELECT_TIMEOUT_FAIL = 
By.xpath("//div/div/label[2]/span/input");
+
+    //cancel timeout alarm
+    public static final By CANCEL_TIMEOUT_ALARM = 
By.xpath("//div/div/label/span/input");
+
+    //select timeout alarm
+    public static final By SELECT_TIMEOUT_ALARM = 
By.xpath("//div/div/label/span/input");
+
+    //input timeout
+    public static final By SELECT_TIMEOUT = 
By.xpath("//div[3]/div[2]/label/div/input");
+
+    //input script
+    public static final By INPUT_SCRIPT = 
By.xpath("//div[2]/div/div/div/div/div/textarea");
+
+
+
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/TenantManageLocator.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/TenantManageLocator.java
new file mode 100644
index 0000000..7d9c8a5
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/TenantManageLocator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dolphinscheduler.locator.security;
+
+import org.openqa.selenium.By;
+
+public class TenantManageLocator{
+    public static final By CREATE_TENANT_BUTTON = 
By.xpath("//button[@class='ans-btn ans-btn-ghost ans-btn-small']");
+
+    public static final By TENANT_INPUT_CODE = 
By.xpath("//div[2]/div/div/div[2]/div/input");
+
+    public static final By TENANT_INPUT_NAME = 
By.xpath("//div[2]/div[2]/div/input");
+
+    public static final By QUEUE = By.xpath("//textarea");
+
+    public static final By DESCRIPTION = By.xpath("//textarea");
+
+    public static final By SUBMIT_BUTTON = By.xpath("//div[3]/button[2]/span");
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/UserManageLocator.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/UserManageLocator.java
new file mode 100644
index 0000000..0d84692
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/locator/security/UserManageLocator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dolphinscheduler.locator.security;
+
+import org.openqa.selenium.By;
+
+public class UserManageLocator {
+
+    public static final By CLICK_USER_MANAGE = 
By.xpath("//div[3]/div/a/div/a/span");
+
+    public static final By CLICK_CREATE_USER_BUTTON = 
By.xpath("//span[contains(.,'创建用户')]");
+
+    public static final By INPUT_USERNAME = 
By.xpath("//div[2]/div/div/div[2]/div/input");
+
+    public static final By INPUT_PASSWORD = 
By.xpath("//div[2]/div[2]/div/input");
+
+    public static final By CLICK_TENANT = 
By.xpath("//div[3]/div[2]/div/div/div/input");
+
+    public static final By SELECT_TENANT = 
By.xpath("//div[3]/div[2]/div/div[2]/div/div/div/ul/li/span");
+
+    public static final By CLICK_QUEUE = 
By.xpath("//div[4]/div[2]/div/div/div/input");
+
+    public static final By SELECT_QUEUE = 
By.xpath("//div[4]/div[2]/div/div[2]/div/div/div/ul/li/span");
+
+    public static final By TENANT_INPUT_EMAIL = 
By.xpath("//div[5]/div[2]/div/input");
+
+    public static final By TENANT_INPUT_PHONE = 
By.xpath("//div[6]/div[2]/div/input");
+
+    public static final By SUBMIT = By.xpath("//div[3]/button[2]/span");
+
+}
diff --git a/e2e/src/test/java/org/apache/dolphinscheduler/page/LoginPage.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/LoginPage.java
new file mode 100644
index 0000000..cd6b318
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/page/LoginPage.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dolphinscheduler.page;
+
+import org.apache.dolphinscheduler.common.PageCommon;
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.data.LoginData;
+import org.apache.dolphinscheduler.locator.LoginLocator;
+import org.apache.dolphinscheduler.util.RedisUtil;
+import org.openqa.selenium.WebDriver;
+
+
+
+public class LoginPage extends PageCommon {
+    /**
+     * Unique constructor
+     * @param driver driver
+     */
+    public LoginPage(WebDriver driver) {
+        super(driver);
+    }
+
+
+    /**
+     * jump page
+     */
+    public void jumpPage() {
+        System.out.println("jump login page");
+        super.jumpPage(LoginData.URL);
+    }
+
+    /**
+     * login
+     *
+     * @return Whether to enter the specified page after searching
+     */
+    public boolean login() throws InterruptedException {
+        System.out.println("LoginPage");
+        // login data
+        sendInput(LoginLocator.LOGIN_INPUT_USER, LoginData.USER);
+        sendInput(LoginLocator.LOGIN_INPUT_PASSWORD, LoginData.PASSWORD);
+
+        // click login button
+        clickButton(LoginLocator.LOGIN_BUTTON);
+
+        moveToElement(LoginLocator.LOGIN_BUTTON_MOVE);
+
+        // Whether to enter the specified page after login
+        return ifTitleContains(LoginData.TENANT);
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateProjectPage.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateProjectPage.java
new file mode 100644
index 0000000..7961715
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateProjectPage.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dolphinscheduler.page.project;
+
+import org.apache.dolphinscheduler.common.PageCommon;
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.data.project.CreatProjectData;
+import org.apache.dolphinscheduler.locator.project.CreateProjectLocator;
+import org.openqa.selenium.WebDriver;
+
+public class CreateProjectPage extends PageCommon {
+    public CreateProjectPage(WebDriver driver) {
+        super(driver);
+    }
+    /**
+     * jump page
+     */
+    public void jumpPage() throws InterruptedException {
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+        clickElement(CreateProjectLocator.PROJECT_MANAGE);
+    }
+
+    /**
+     * creatTenant
+     *
+     * @return Whether to enter the specified page after creat tenant
+     */
+    public boolean createProject() throws InterruptedException {
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+        //click  create project
+        clickElement(CreateProjectLocator.CREATE_PROJECT_BUTTON);
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        // input create project data
+        sendInput(CreateProjectLocator.PROJECT_NAME, 
CreatProjectData.PROJECT_NAME);
+        sendInput(CreateProjectLocator.PROJECT_DESCRIPTION, 
CreatProjectData.DESCRIPTION);
+
+        // click submit  button
+        clickButton(CreateProjectLocator.SUBMIT_BUTTON);
+
+        // Whether to enter the specified page after submit
+        return ifTitleContains(CreatProjectData.PROJECT_TITLE);
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateWorkflowPage.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateWorkflowPage.java
new file mode 100644
index 0000000..827198b
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/project/CreateWorkflowPage.java
@@ -0,0 +1,108 @@
+/*
+ * 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.dolphinscheduler.page.project;
+
+import org.apache.dolphinscheduler.common.PageCommon;
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.data.project.CreatWorkflowData;
+import org.apache.dolphinscheduler.locator.project.CreateProjectLocator;
+import org.apache.dolphinscheduler.locator.project.CreateWorkflowLocator;
+import org.openqa.selenium.WebDriver;
+
+public class CreateWorkflowPage extends PageCommon {
+    public CreateWorkflowPage(WebDriver driver) {
+        super(driver);
+    }
+    /**
+     * jump page
+     */
+    public boolean CreateWorkflow() throws InterruptedException {
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+        // click project manage
+        clickElement(CreateProjectLocator.PROJECT_MANAGE);
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        // click project name
+        clickElement(CreateWorkflowLocator.CLICK_PROJECT_NAME);
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        // click workflow define
+        clickElement(CreateWorkflowLocator.CLICK_WORKFLOW_DEFINE);
+
+        // click create workflow button
+        clickElement(CreateWorkflowLocator.CLICK_CREATE_WORKFLOW_BUTTON);
+
+        //drag shell_task
+        
dragAndDropBy(CreateWorkflowLocator.MOUSE_DOWN_AT_SHELL,CreateWorkflowLocator.MOUSE_MOVE_SHELL_AT_DAG,
 3 ,6);
+
+        //input shell task _name
+        sendInput(CreateWorkflowLocator.INPUT_SHELL_TASK_NAME , 
CreatWorkflowData.SHELL_TASK_NAME);
+
+        //click stop run type
+        clickElement(CreateWorkflowLocator.CLICK_STOP_RUN_TYPE);
+
+
+        //click normal run type
+        clickElement(CreateWorkflowLocator.CLICK_NORMAL_RUN_TYPE);
+
+
+        //input shell task description
+        sendInput(CreateWorkflowLocator.INPUT_SHELL_TASK_DESCRIPTION , 
CreatWorkflowData.SHELL_TASK_DESCRIPTION);
+
+        //select task priority
+        clickElement(CreateWorkflowLocator.CLICK_TASK_PRIORITY);
+        clickElement(CreateWorkflowLocator.SELECT_TASK_PRIORITY);
+
+        //select work group
+        clickElement(CreateWorkflowLocator.CLICK_WORK_GROUP);
+        clickElement(CreateWorkflowLocator.SELECT_WORK_GROUP);
+
+        //select number of failed retries
+        clickElement(CreateWorkflowLocator.SELECT_FAIL_RETRIES_NUMBER);
+
+        //select failed retry interval
+        clickElement(CreateWorkflowLocator.SELECT_FAIL_RETRIES_INTERVAL);
+
+
+        //click timeout alarm
+        clickElement(CreateWorkflowLocator.CLICK_TIMEOUT_ALARM);
+
+
+        //select timeout fail
+        clickElement(CreateWorkflowLocator.SELECT_TIMEOUT_FAIL);
+
+
+        //cancel timeout alarm
+        clickElement(CreateWorkflowLocator.CANCEL_TIMEOUT_ALARM);
+
+
+        //select timeout alarm
+        clickElement(CreateWorkflowLocator.SELECT_TIMEOUT_ALARM);
+
+        //clear input
+        clearInput(CreateWorkflowLocator.SELECT_TIMEOUT);
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        //input timeout
+        
sendInput(CreateWorkflowLocator.SELECT_TIMEOUT,CreatWorkflowData.INPUT_TIMEOUT);
+
+        //input script
+        sendInput(CreateWorkflowLocator.INPUT_SCRIPT, 
CreatWorkflowData.SHELL_SCRIPT);
+
+        return ifTitleContains(CreatWorkflowData.WORKFLOW_TITLE);
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/page/security/TenantManagePage.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/security/TenantManagePage.java
new file mode 100644
index 0000000..4c88f65
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/security/TenantManagePage.java
@@ -0,0 +1,69 @@
+/*
+ * 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.dolphinscheduler.page.security;
+
+import org.apache.dolphinscheduler.common.PageCommon;
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.data.LoginData;
+import org.apache.dolphinscheduler.data.security.TenantManageData;
+import org.apache.dolphinscheduler.locator.LoginLocator;
+import org.apache.dolphinscheduler.locator.security.TenantManageLocator;
+import org.apache.dolphinscheduler.util.RedisUtil;
+import org.openqa.selenium.WebDriver;
+
+public class TenantManagePage extends PageCommon {
+    /**
+     * Unique constructor
+     * @param driver driver
+     */
+    public TenantManagePage(WebDriver driver) {
+        super(driver);
+    }
+
+
+    /**
+     * jump page
+     */
+    public void jumpPage() {
+        System.out.println("jump tenant page");
+        super.jumpPage(TenantManageData.TENANAT_URL);
+    }
+
+    /**
+     * createTenant
+     *
+     * @return Whether to enter the specified page after creat tenant
+     */
+    public boolean createTenant() throws InterruptedException {
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        //create tenant
+        clickButton(TenantManageLocator.CREATE_TENANT_BUTTON);
+
+        // tenant data
+        sendInput(TenantManageLocator.TENANT_INPUT_CODE, 
TenantManageData.TENANAT_CODE);
+        sendInput(TenantManageLocator.TENANT_INPUT_NAME, 
TenantManageData.TENANAT_NAME);
+        sendInput(TenantManageLocator.QUEUE, TenantManageData.QUEUE);
+        sendInput(TenantManageLocator.DESCRIPTION, 
TenantManageData.DESCRIPTION);
+
+        // click  button
+        clickButton(TenantManageLocator.SUBMIT_BUTTON);
+
+        // Whether to enter the specified page after submit
+        return ifTitleContains(TenantManageData.TENANAT_MANAGE);
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/page/security/UserManagePage.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/security/UserManagePage.java
new file mode 100644
index 0000000..3e2be2d
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/page/security/UserManagePage.java
@@ -0,0 +1,67 @@
+/*
+ * 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.dolphinscheduler.page.security;
+
+import org.apache.dolphinscheduler.common.PageCommon;
+import org.apache.dolphinscheduler.constant.TestConstant;
+import org.apache.dolphinscheduler.data.security.UserManageData;
+import org.apache.dolphinscheduler.locator.security.UserManageLocator;
+import org.openqa.selenium.WebDriver;
+
+public class UserManagePage extends PageCommon {
+    public UserManagePage(WebDriver driver) {
+        super(driver);
+    }
+    /**
+     * jump page
+     */
+    public void jumpPage() {
+        System.out.println("jump tenant page");
+        super.jumpPage(UserManageData.USER_URL);
+    }
+
+    /**
+     * creatTenant
+     *
+     * @return Whether to enter the specified page after creat tenant
+     */
+    public boolean createUser() throws InterruptedException {
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+        // click  user manage
+        clickElement(UserManageLocator.CLICK_USER_MANAGE);
+        Thread.sleep(TestConstant.ONE_THOUSANG);
+
+        // click  create user button
+        clickButton(UserManageLocator.CLICK_CREATE_USER_BUTTON);
+
+        // input user data
+        sendInput(UserManageLocator.INPUT_USERNAME, UserManageData.USERNAME);
+        sendInput(UserManageLocator.INPUT_PASSWORD, UserManageData.PASSWORD);
+        clickButton(UserManageLocator.CLICK_TENANT);
+        clickButton(UserManageLocator.SELECT_TENANT);
+        clickButton(UserManageLocator.CLICK_QUEUE);
+        clickButton(UserManageLocator.SELECT_QUEUE);
+        sendInput(UserManageLocator.TENANT_INPUT_EMAIL, UserManageData.EMAIL);
+        sendInput(UserManageLocator.TENANT_INPUT_PHONE, UserManageData.PHONE);
+
+        // click  button
+        clickButton(UserManageLocator.SUBMIT);
+
+        // Whether to enter the specified page after submit
+        return ifTitleContains(UserManageData.USER_MANAGE);
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/testcase/LoginTest.java 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/LoginTest.java
new file mode 100644
index 0000000..bd3c31d
--- /dev/null
+++ b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/LoginTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dolphinscheduler.testcase;
+
+import org.apache.dolphinscheduler.base.BaseTest;
+import org.apache.dolphinscheduler.page.LoginPage;
+import org.testng.annotations.Test;
+
+
+public class LoginTest extends BaseTest {
+    private LoginPage loginPage;
+    @Test(description = "LoginTest", priority = 1)
+    public void testLogin() throws InterruptedException {
+        // init login page
+        loginPage = new LoginPage(driver);
+
+        // enter login page
+        loginPage.jumpPage();
+
+        //assert login page
+        assert loginPage.login();
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateProjectTest.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateProjectTest.java
new file mode 100644
index 0000000..66af105
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateProjectTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dolphinscheduler.testcase.project;
+
+import org.apache.dolphinscheduler.base.BaseTest;
+import org.apache.dolphinscheduler.page.project.CreateProjectPage;
+import org.testng.annotations.Test;
+
+public class CreateProjectTest  extends BaseTest {
+    private CreateProjectPage createProjectPage;
+
+    @Test(description = "TenantTest", priority = 1)
+    public void testUserManage() throws InterruptedException {
+        createProjectPage = new CreateProjectPage(driver);
+        // enter user manage page
+        createProjectPage.jumpPage();
+        //assert user manage page
+        assert createProjectPage.createProject();
+    }
+
+}
+
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateWorkflowTest.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateWorkflowTest.java
new file mode 100644
index 0000000..ac1bfec
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/project/CreateWorkflowTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.testcase.project;
+
+import org.apache.dolphinscheduler.base.BaseTest;
+import org.apache.dolphinscheduler.page.project.CreateWorkflowPage;
+import org.testng.annotations.Test;
+
+public class CreateWorkflowTest  extends BaseTest {
+    private CreateWorkflowPage createWorkflowPage;
+
+    @Test(description = "CreateWorkflowTest", priority = 1)
+    public void CreateWorkflowTest() throws InterruptedException {
+        createWorkflowPage = new CreateWorkflowPage(driver);
+        // enter user manage page
+//        createWorkflowPage.jumpPage();
+        //assert user manage page
+        assert createWorkflowPage.CreateWorkflow();
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/TenantManageTest.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/TenantManageTest.java
new file mode 100644
index 0000000..7124b4e
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/TenantManageTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.testcase.security;
+
+import org.apache.dolphinscheduler.base.BaseTest;
+import org.apache.dolphinscheduler.page.security.TenantManagePage;
+import org.testng.annotations.Test;
+
+public class TenantManageTest extends BaseTest {
+    private TenantManagePage tenantManagePage;
+
+    @Test(description = "TenantTest", priority = 1)
+    public void testTenantManage() throws InterruptedException {
+        tenantManagePage = new TenantManagePage(driver);
+        // enter tenant manage page
+        tenantManagePage.jumpPage();
+        //assert tenant manage page
+        assert tenantManagePage.createTenant();
+    }
+}
diff --git 
a/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/UserManageTest.java
 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/UserManageTest.java
new file mode 100644
index 0000000..834ebdb
--- /dev/null
+++ 
b/e2e/src/test/java/org/apache/dolphinscheduler/testcase/security/UserManageTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.testcase.security;
+
+import org.apache.dolphinscheduler.base.BaseTest;
+import org.apache.dolphinscheduler.page.security.UserManagePage;
+import org.testng.annotations.Test;
+
+public class UserManageTest extends BaseTest {
+    private UserManagePage userManagePage;
+
+    @Test(description = "TenantTest", priority = 1)
+    public void testUserManage() throws InterruptedException {
+        userManagePage = new UserManagePage(driver);
+        // enter user manage page
+        userManagePage.jumpPage();
+        //assert user manage page
+        assert userManagePage.createUser();
+    }
+}
diff --git a/e2e/src/test/resources/config/config.properties 
b/e2e/src/test/resources/config/config.properties
new file mode 100644
index 0000000..e3ee61c
--- /dev/null
+++ b/e2e/src/test/resources/config/config.properties
@@ -0,0 +1,58 @@
+#
+# 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.
+#
+
+############### project ##############
+# login url
+LOGIN_URL=http://ark1:12345/dolphinscheduler
+#login username
+USER_NAME=admin
+#login password
+PASSWORD=123456
+
+############### web driver ##############
+# driver path
+driver.chromeDriver=/Users/chenxingchun/Documents/easyscheduler/selenium/chromeDriver
+# implicitly wait(s)
+driver.timeouts.implicitlyWait=10
+# show wait(s)
+driver.timeouts.webDriverWait=10
+# page load timeout(s)
+driver.timeouts.pageLoadTimeout=10
+# JS wait timeouts(s)
+driver.timeouts.setScriptTimeout=10
+
+
+############### redis ##############
+# redis ip
+redis.ip=127.0.0.1
+# redis port
+redis.port=6379
+# redis password
+redis.pwd=
+############### redis pool ##############
+# jedis expireTime(s)
+jedis.expireTime=3600
+# jedis maxTotal
+jedis.pool.maxTotal=3000
+# jedis maxIdle
+jedis.pool.maxIdle=1000
+# jedis maxWaitMillis
+jedis.pool.maxWaitMillis=10000
+# jedis Whether to perform a valid check when calling the borrowObject method
+jedis.pool.testOnBorrow=true
+# jedis Whether to perform a valid check when calling the returnObject method
+jedis.pool.testOnReturn=true
\ No newline at end of file
diff --git a/e2e/suite.xml b/e2e/suite.xml
new file mode 100644
index 0000000..d9d7ae6
--- /dev/null
+++ b/e2e/suite.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd";>
+<suite name="e2e Test Suite" parallel="tests" thread-count="2">
+    <!-- create tenant -->
+    <test name="create-tenant" preserve-order="true">
+        <classes>
+            <class 
name="org.apache.dolphinscheduler.testcase.security.TenantManageTest">
+            </class>
+        </classes>
+    </test>
+    <!-- create user -->
+    <test name="create-user" preserve-order="true">
+        <classes>
+            <class 
name="org.apache.dolphinscheduler.testcase.security.UserManageTest">
+            </class>
+        </classes>
+    </test>
+</suite>
\ No newline at end of file
diff --git a/e2e/testng.xml b/e2e/testng.xml
new file mode 100644
index 0000000..60d2541
--- /dev/null
+++ b/e2e/testng.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"; >
+<suite name="dolphinscheduler_e2e" parallel="false">
+    <test name="dolphinscheduler_test">
+        <!--<packages>-->
+            <!--&lt;!&ndash;<package 
name="org.apache.dolphinscheduler.testcase.security.TenantManageTest"/>&ndash;&gt;-->
+            <!--<package 
name="org.apache.dolphinscheduler.testcase.security.*"/>-->
+        <!--</packages>-->
+        <!--<classes>-->
+        <!--<class 
name="org.apache.dolphinscheduler.testcase.security.UserManageTest"></class>-->
+        <!--</classes>-->
+        </test>
+
+        <!--<suite-files>-->
+            <!--<suite-file path="suite.xml"/>-->
+        <!--</suite-files>-->
+
+    <listeners>
+        <listener class-name="org.uncommons.reportng.HTMLReporter" />
+        <listener class-name="org.uncommons.reportng.JUnitXMLReporter" />
+    </listeners>
+</suite>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ee2d808..076b856 100644
--- a/pom.xml
+++ b/pom.xml
@@ -242,7 +242,6 @@
                 <artifactId>dolphinscheduler-alert</artifactId>
                 <version>${project.version}</version>
             </dependency>
-
             <dependency>
                 <groupId>org.apache.curator</groupId>
                 <artifactId>curator-framework</artifactId>

Reply via email to