You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by ke...@apache.org on 2022/03/12 12:49:49 UTC

[dolphinscheduler] branch dev updated: [Feature-8823][E2E] Restore resource center e2e test cases in ui-next (#8840)

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

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


The following commit(s) were added to refs/heads/dev by this push:
     new 49b1a07  [Feature-8823][E2E] Restore resource center e2e test cases in ui-next (#8840)
49b1a07 is described below

commit 49b1a073dd075f011c931b9a938909c14a41de1f
Author: xiangzihao <46...@qq.com>
AuthorDate: Sat Mar 12 20:49:46 2022 +0800

    [Feature-8823][E2E] Restore resource center e2e test cases in ui-next (#8840)
---
 .github/workflows/e2e.yml                          |  12 +--
 .../e2e/cases/FileManageE2ETest.java               | 108 +++++++++----------
 .../e2e/cases/FunctionManageE2ETest.java           |  51 ++++-----
 .../e2e/cases/UdfManageE2ETest.java                |  38 ++++---
 .../dolphinscheduler/e2e/cases/UserE2ETest.java    |  42 ++++----
 .../e2e/pages/common/CodeEditor.java               |  17 ++-
 .../e2e/pages/resource/FileManagePage.java         | 120 +++++++++++++--------
 .../e2e/pages/resource/FunctionManagePage.java     |  84 +++++++++------
 .../e2e/pages/resource/ResourcePage.java           |  18 ++--
 .../e2e/pages/resource/UdfManagePage.java          |  63 ++++++-----
 .../e2e/pages/security/UserPage.java               |  15 ++-
 .../src/views/resource/file/create/index.tsx       |   6 +-
 .../src/views/resource/file/edit/index.tsx         |   2 +
 .../src/views/resource/file/folder/index.tsx       |   4 +
 .../src/views/resource/file/index.tsx              |   7 +-
 .../src/views/resource/file/rename/index.tsx       |   4 +
 .../src/views/resource/file/table/table-action.tsx |   5 +-
 .../src/views/resource/file/upload/index.tsx       |   5 +
 .../udf/function/components/function-modal.tsx     |   8 ++
 .../src/views/resource/udf/function/index.tsx      |   3 +-
 .../src/views/resource/udf/function/use-table.ts   |   4 +-
 .../udf/resource/components/folder-modal.tsx       |   4 +
 .../udf/resource/components/upload-modal.tsx       |   5 +
 .../src/views/resource/udf/resource/index.tsx      |   5 +-
 .../src/views/resource/udf/resource/use-table.ts   |   5 +-
 .../src/views/security/user-manage/use-columns.ts  |   4 +-
 26 files changed, 392 insertions(+), 247 deletions(-)

diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 0e74b46..5bcc6ac 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -87,12 +87,12 @@ jobs:
 #            class: org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest
 #          - name: WorkflowForSwitch
 #            class: org.apache.dolphinscheduler.e2e.cases.WorkflowSwitchE2ETest
-#          - name: FileManage
-#            class: org.apache.dolphinscheduler.e2e.cases.FileManageE2ETest
-#          - name: UdfManage
-#            class: org.apache.dolphinscheduler.e2e.cases.UdfManageE2ETest
-#          - name: FunctionManage
-#            class: org.apache.dolphinscheduler.e2e.cases.FunctionManageE2ETest
+          - name: FileManage
+            class: org.apache.dolphinscheduler.e2e.cases.FileManageE2ETest
+          - name: UdfManage
+            class: org.apache.dolphinscheduler.e2e.cases.UdfManageE2ETest
+          - name: FunctionManage
+            class: org.apache.dolphinscheduler.e2e.cases.FunctionManageE2ETest
 #          - name: MysqlDataSource
 #            class: org.apache.dolphinscheduler.e2e.cases.MysqlDataSourceE2ETest
 #          - name: ClickhouseDataSource
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FileManageE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FileManageE2ETest.java
index acf8fa2..b89cc72 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FileManageE2ETest.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FileManageE2ETest.java
@@ -84,20 +84,26 @@ public class FileManageE2ETest {
 
     @BeforeAll
     public static void setup() {
-//        TenantPage tenantPage = new LoginPage(browser)
-//                .login(user, password)
-//                .create(tenant);
-//
-//        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
-//                .as("Tenant list should contain newly-created tenant")
-//                .extracting(WebElement::getText)
-//                .anyMatch(it -> it.contains(tenant)));
-//
-//        tenantPage.goToNav(SecurityPage.class)
-//            .goToTab(UserPage.class)
-//            .update(user, user, password, email, phone)
-//            .goToNav(ResourcePage.class)
-//            .goToTab(FileManagePage.class);
+        TenantPage tenantPage = new LoginPage(browser)
+                .login(user, password)
+                .goToNav(SecurityPage.class)
+                .goToTab(TenantPage.class)
+                .create(tenant);
+
+        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
+                .as("Tenant list should contain newly-created tenant")
+                .extracting(WebElement::getText)
+                .anyMatch(it -> it.contains(tenant)));
+
+        UserPage userPage = tenantPage.goToNav(SecurityPage.class)
+            .goToTab(UserPage.class);
+
+        new WebDriverWait(userPage.driver(), 20).until(ExpectedConditions.visibilityOfElementLocated(
+                new By.ByClassName("name")));
+
+        userPage.update(user, user, password, email, phone, tenant)
+            .goToNav(ResourcePage.class)
+            .goToTab(FileManagePage.class);
     }
 
     @AfterAll
@@ -137,39 +143,39 @@ public class FileManageE2ETest {
             .anyMatch(it -> it.contains(testDirectoryName)));
     }
 
-    @Test
-    @Order(20)
-    void testCreateDuplicateDirectory() {
-        final FileManagePage page = new FileManagePage(browser);
-
-        page.createDirectory(testDirectoryName, "test_desc");
-
-        await().untilAsserted(() -> assertThat(browser.findElement(By.tagName("body")).getText())
-                .contains("resource already exists")
-        );
-
-        page.createDirectoryBox().buttonCancel().click();
-    }
-
-    @Test
-    @Order(21)
-    void testCreateSubDirectory() {
-        final FileManagePage page = new FileManagePage(browser);
-
-        page.createSubDirectory(testDirectoryName, testSubDirectoryName, "test_desc");
+//    @Test
+//    @Order(20)
+//    void testCreateDuplicateDirectory() {
+//        final FileManagePage page = new FileManagePage(browser);
+//
+//        page.createDirectory(testDirectoryName, "test_desc");
+//
+//        await().untilAsserted(() -> assertThat(browser.findElement(By.tagName("body")).getText())
+//                .contains("resource already exists")
+//        );
+//
+//        page.createDirectoryBox().buttonCancel().click();
+//    }
 
-        await().untilAsserted(() -> assertThat(page.fileList())
-            .as("File list should contain newly-created file")
-            .extracting(WebElement::getText)
-            .anyMatch(it -> it.contains(testSubDirectoryName)));
-    }
+//    @Test
+//    @Order(21)
+//    void testCreateSubDirectory() {
+//        final FileManagePage page = new FileManagePage(browser);
+//
+//        page.createSubDirectory(testDirectoryName, testSubDirectoryName, "test_desc");
+//
+//        await().untilAsserted(() -> assertThat(page.fileList())
+//            .as("File list should contain newly-created file")
+//            .extracting(WebElement::getText)
+//            .anyMatch(it -> it.contains(testSubDirectoryName)));
+//    }
 
     @Test
     @Order(22)
     void testRenameDirectory() {
         final FileManagePage page = new FileManagePage(browser);
 
-        page.rename(testSubDirectoryName, testRenameDirectoryName);
+        page.rename(testDirectoryName, testRenameDirectoryName);
 
         await().untilAsserted(() -> {
             browser.navigate().refresh();
@@ -188,7 +194,7 @@ public class FileManageE2ETest {
 
         page.goToNav(ResourcePage.class)
             .goToTab(FileManagePage.class)
-            .delete(testDirectoryName);
+            .delete(testRenameDirectoryName);
 
         await().untilAsserted(() -> {
             browser.navigate().refresh();
@@ -196,7 +202,7 @@ public class FileManageE2ETest {
             assertThat(
                     page.fileList()
             ).noneMatch(
-                    it -> it.getText().contains(testDirectoryName)
+                    it -> it.getText().contains(testRenameDirectoryName)
             );
         });
     }
@@ -265,25 +271,9 @@ public class FileManageE2ETest {
     }
 
     @Test
-    @Order(60)
-    void testUploadOver1GBFile() throws IOException {
-        final FileManagePage page = new FileManagePage(browser);
-
-        RandomAccessFile file = new RandomAccessFile(testOver1GBFilePath.toFile(), "rw");
-        file.setLength((long) (1.5 * 1024 * 1024 * 1024));
-
-        page.uploadFile(testOver1GBFilePath.toFile().getAbsolutePath());
-
-        await().untilAsserted(() ->
-            assertThat(browser.findElement(By.tagName("body")).getText())
-                .contains("Upload File size cannot exceed 1g")
-        );
-    }
-
-    @Test
     @Order(65)
     void testUploadUnder1GBFile() throws IOException {
-        final FileManagePage page = new FileManagePage(browser);
+        FileManagePage page = new FileManagePage(browser);
 
         browser.navigate().refresh();
 
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FunctionManageE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FunctionManageE2ETest.java
index 73e100a..a810d6b 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FunctionManageE2ETest.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/FunctionManageE2ETest.java
@@ -83,28 +83,32 @@ public class FunctionManageE2ETest {
     @BeforeAll
     @SneakyThrows
     public static void setup() {
-//        TenantPage tenantPage = new LoginPage(browser)
-//            .login(user, password)
-//            .create(tenant);
-//
-//        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
-//            .as("Tenant list should contain newly-created tenant")
-//            .extracting(WebElement::getText)
-//            .anyMatch(it -> it.contains(tenant)));
-//
-//        downloadFile("https://repo1.maven.org/maven2/org/apache/hive/hive-jdbc/3.1.2/hive-jdbc-3.1.2.jar", testUploadUdfFilePath.toFile().getAbsolutePath());
-//
-//        UdfManagePage udfManagePage = tenantPage.goToNav(SecurityPage.class)
-//            .goToTab(UserPage.class)
-//            .update(user, user, password, email, phone)
-//            .goToNav(ResourcePage.class)
-//            .goToTab(UdfManagePage.class)
-//            .uploadFile(testUploadUdfFilePath.toFile().getAbsolutePath());
-//
-//        new WebDriverWait(browser, 10).until(ExpectedConditions.invisibilityOfElementLocated(By.id("fileUpdateDialog")));
-//
-//        udfManagePage.goToNav(ResourcePage.class)
-//            .goToTab(FunctionManagePage.class);
+        TenantPage tenantPage = new LoginPage(browser)
+                .login(user, password)
+                .goToNav(SecurityPage.class)
+                .goToTab(TenantPage.class)
+                .create(tenant);
+
+        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
+                .as("Tenant list should contain newly-created tenant")
+                .extracting(WebElement::getText)
+                .anyMatch(it -> it.contains(tenant)));
+
+        downloadFile("https://repo1.maven.org/maven2/org/apache/hive/hive-jdbc/3.1.2/hive-jdbc-3.1.2.jar", testUploadUdfFilePath.toFile().getAbsolutePath());
+
+        UserPage userPage = tenantPage.goToNav(SecurityPage.class)
+                .goToTab(UserPage.class);
+
+        new WebDriverWait(userPage.driver(), 20).until(ExpectedConditions.visibilityOfElementLocated(
+                new By.ByClassName("name")));
+
+        UdfManagePage udfManagePage = userPage.update(user, user, password, email, phone, tenant)
+                .goToNav(ResourcePage.class)
+                .goToTab(UdfManagePage.class)
+                .uploadFile(testUploadUdfFilePath.toFile().getAbsolutePath());
+
+        udfManagePage.goToNav(ResourcePage.class)
+                .goToTab(FunctionManagePage.class);
     }
 
     @AfterAll
@@ -141,9 +145,6 @@ public class FunctionManageE2ETest {
     void testCreateUdfFunction() {
         FunctionManagePage page = new FunctionManagePage(browser);
 
-        new WebDriverWait(page.driver(), 10)
-            .until(ExpectedConditions.elementToBeClickable(By.id("btnCreateUdfFunction")));
-
         page.createUdfFunction(testUdfFunctionName, testClassName, testUploadUdfFileName, testDescription);
 
         await().untilAsserted(() -> assertThat(page.functionList())
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UdfManageE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UdfManageE2ETest.java
index b9214f1..adc1609 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UdfManageE2ETest.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UdfManageE2ETest.java
@@ -80,20 +80,26 @@ public class UdfManageE2ETest {
 
     @BeforeAll
     public static void setup() {
-//        TenantPage tenantPage = new LoginPage(browser)
-//            .login(user, password)
-//            .create(tenant);
-//
-//        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
-//            .as("Tenant list should contain newly-created tenant")
-//            .extracting(WebElement::getText)
-//            .anyMatch(it -> it.contains(tenant)));
-//
-//        tenantPage.goToNav(SecurityPage.class)
-//            .goToTab(UserPage.class)
-//            .update(user, user, password, email, phone)
-//            .goToNav(ResourcePage.class)
-//            .goToTab(UdfManagePage.class);
+        TenantPage tenantPage = new LoginPage(browser)
+            .login(user, password)
+            .goToNav(SecurityPage.class)
+            .goToTab(TenantPage.class)
+            .create(tenant);
+
+        await().untilAsserted(() -> assertThat(tenantPage.tenantList())
+            .as("Tenant list should contain newly-created tenant")
+            .extracting(WebElement::getText)
+            .anyMatch(it -> it.contains(tenant)));
+
+        UserPage userPage = tenantPage.goToNav(SecurityPage.class)
+            .goToTab(UserPage.class);
+
+        new WebDriverWait(userPage.driver(), 20).until(ExpectedConditions.visibilityOfElementLocated(
+                new By.ByClassName("name")));
+
+        userPage.update(user, user, password, email, phone, tenant)
+            .goToNav(ResourcePage.class)
+            .goToTab(UdfManagePage.class);
     }
 
     @AfterAll
@@ -113,7 +119,7 @@ public class UdfManageE2ETest {
         final UdfManagePage page = new UdfManagePage(browser);
 
         new WebDriverWait(page.driver(), 10)
-            .until(ExpectedConditions.urlContains("/#/resource/udf"));
+            .until(ExpectedConditions.urlContains("/resource-manage"));
 
         page.createDirectory(testDirectoryName, "test_desc");
 
@@ -168,8 +174,6 @@ public class UdfManageE2ETest {
 
         page.uploadFile(testUploadUdfFilePath.toFile().getAbsolutePath());
 
-        new WebDriverWait(browser, 10).until(ExpectedConditions.invisibilityOfElementLocated(By.id("fileUpdateDialog")));
-
         await().untilAsserted(() -> {
             assertThat(page.udfList())
                 .as("File list should contain newly-created file")
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java
index 7b77274..4414be9 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java
@@ -37,6 +37,8 @@ import org.junit.jupiter.api.Test;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.remote.RemoteWebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
 
 @DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
 class UserE2ETest {
@@ -115,7 +117,12 @@ class UserE2ETest {
     void testEditUser() {
         UserPage page = new UserPage(browser);
 
-        page.update(user, editUser, editPassword, editEmail, editPhone);
+        new WebDriverWait(browser, 20).until(ExpectedConditions.visibilityOfElementLocated(
+                new By.ByClassName("name")));
+
+        browser.navigate().refresh();
+
+        page.update(user, editUser, editPassword, editEmail, editPhone, tenant);
 
         await().untilAsserted(() -> {
             browser.navigate().refresh();
@@ -125,23 +132,22 @@ class UserE2ETest {
                 .anyMatch(it -> it.contains(editUser));
         });
     }
+    
+    @Test
+    @Order(40)
+    void testDeleteUser() {
+        final UserPage page = new UserPage(browser);
 
+        page.delete(editUser);
 
-//    @Test
-//    @Order(40)
-//    void testDeleteUser() {
-//        final UserPage page = new UserPage(browser);
-//
-//        page.delete(editUser);
-//
-//        await().untilAsserted(() -> {
-//            browser.navigate().refresh();
-//
-//            assertThat(
-//                page.userList()
-//            ).noneMatch(
-//                it -> it.getText().contains(user) || it.getText().contains(editUser)
-//            );
-//        });
-//    }
+        await().untilAsserted(() -> {
+            browser.navigate().refresh();
+
+            assertThat(
+                page.userList()
+            ).noneMatch(
+                it -> it.getText().contains(user) || it.getText().contains(editUser)
+            );
+        });
+    }
 }
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
index 5a8645d..2cff9bb 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
@@ -22,23 +22,34 @@ package org.apache.dolphinscheduler.e2e.pages.common;
 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.remote.RemoteWebDriver;
 import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.FindBys;
 import org.openqa.selenium.support.PageFactory;
 
 import lombok.Getter;
 
 @Getter
 public final class CodeEditor {
-    @FindBy(className = "CodeMirror")
+    @FindBys({
+        @FindBy(className = "monaco-editor"),
+        @FindBy(className = "view-line"),
+    })
     private WebElement editor;
 
+    private WebDriver driver;
+
     public CodeEditor(WebDriver driver) {
         PageFactory.initElements(driver, this);
+        this.driver = driver;
     }
 
     public CodeEditor content(String content) {
-        editor.findElement(By.className("CodeMirror-line")).click();
-        editor.findElement(By.tagName("textarea")).sendKeys(content);
+        editor.click();
+
+        Actions actions = new Actions(this.driver);
+        actions.moveToElement(editor).sendKeys(content).perform();
 
         return this;
     }
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FileManagePage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FileManagePage.java
index 9e5661d..395877d 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FileManagePage.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FileManagePage.java
@@ -26,12 +26,16 @@ import org.apache.dolphinscheduler.e2e.pages.common.CodeEditor;
 import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
 
 import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.remote.LocalFileDetector;
 import org.openqa.selenium.remote.RemoteWebDriver;
 import org.openqa.selenium.support.FindBy;
 import org.openqa.selenium.support.FindBys;
 import org.openqa.selenium.support.PageFactory;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
 
 import java.io.File;
 import java.util.List;
@@ -39,45 +43,46 @@ import java.util.List;
 
 @Getter
 public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
-    @FindBy(id = "btnCreateDirectory")
+    @FindBy(className = "btn-create-directory")
     private WebElement buttonCreateDirectory;
 
-    @FindBy(id = "btnCreateFile")
+    @FindBy(className = "btn-create-file")
     private WebElement buttonCreateFile;
 
-    @FindBy(id = "btnUploadFile")
+    @FindBy(className = "btn-upload-file")
     private WebElement buttonUploadFile;
 
     private final CreateDirectoryBox createDirectoryBox;
 
-    private final RenameDirectoryBox renameDirectoryBox;
+    private final RenameBox renameBox;
 
     private final CreateFileBox createFileBox;
 
     private final UploadFileBox uploadFileBox;
 
+    private final EditFileBox editFileBox;
+
     @FindBy(className = "items")
     private List<WebElement> fileList;
 
-    @FindBy(id = "delete")
-    private WebElement buttonDelete;
-
     @FindBys({
-        @FindBy(className = "el-popconfirm"),
-        @FindBy(className = "el-button--primary"),
+        @FindBy(className = "n-popconfirm__action"),
+        @FindBy(className = "n-button--primary-type"),
     })
-    private List<WebElement> buttonConfirm;
+    private WebElement buttonConfirm;
 
     public FileManagePage(RemoteWebDriver driver) {
         super(driver);
 
         createDirectoryBox = new CreateDirectoryBox();
 
-        renameDirectoryBox = new RenameDirectoryBox();
+        renameBox = new RenameBox();
 
         createFileBox = new CreateFileBox();
 
         uploadFileBox = new UploadFileBox();
+
+        editFileBox = new EditFileBox();
     }
 
     public FileManagePage createDirectory(String name, String description) {
@@ -104,15 +109,16 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
         fileList()
             .stream()
             .filter(it -> it.getText().contains(currentName))
-            .flatMap(it -> it.findElements(By.id("btnRename")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-rename")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No rename button in file manage list"))
             .click();
 
-        renameDirectoryBox().inputName().clear();
-        renameDirectoryBox().inputName().sendKeys(AfterName);
-        renameDirectoryBox().buttonSubmit().click();
+        renameBox().inputName().sendKeys(Keys.CONTROL + "a");
+        renameBox().inputName().sendKeys(Keys.BACK_SPACE);
+        renameBox().inputName().sendKeys(AfterName);
+        renameBox().buttonSubmit().click();
 
         return this;
     }
@@ -139,18 +145,13 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
         fileList()
             .stream()
             .filter(it -> it.getText().contains(name))
-            .flatMap(it -> it.findElements(By.id("delete")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-delete")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No delete button in file manage list"))
             .click();
 
-        buttonConfirm()
-            .stream()
-            .filter(WebElement::isDisplayed)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("No confirm button when deleting"))
-            .click();
+        ((JavascriptExecutor) driver).executeScript("arguments[0].click();", buttonConfirm());
 
         return this;
     }
@@ -169,14 +170,16 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
         fileList()
             .stream()
             .filter(it -> it.getText().contains(fileName))
-            .flatMap(it -> it.findElements(By.id("btnEdit")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-edit")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No edit button in file manage list"))
             .click();
 
-        createFileBox().codeEditor().content(scripts);
-        createFileBox().buttonSubmit().click();
+        new WebDriverWait(driver, 5).until(ExpectedConditions.urlContains("/edit"));
+
+        editFileBox().codeEditor().content(scripts);
+        editFileBox().buttonSubmit().click();
 
         return this;
     }
@@ -196,7 +199,7 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
         fileList()
             .stream()
             .filter(it -> it.getText().contains(fileName))
-            .flatMap(it -> it.findElements(By.id("btnDownload")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-download")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No download button in file manage list"))
@@ -211,35 +214,47 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputDirectoryName")
+        @FindBys({
+                @FindBy(className = "input-directory-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputDirectoryName;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
     @Getter
-    public class RenameDirectoryBox {
-        RenameDirectoryBox() {
+    public class RenameBox {
+        RenameBox() {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputName")
+        @FindBys({
+                @FindBy(className = "input-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputName;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
@@ -249,15 +264,33 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputFileName")
+        @FindBys({
+                @FindBy(className = "input-file-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputFileName;
 
         private final CodeEditor codeEditor = new CodeEditor(driver);
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
+        private WebElement buttonSubmit;
+
+        @FindBy(className = "btn-cancel")
+        private WebElement buttonCancel;
+    }
+
+    @Getter
+    public class EditFileBox {
+        EditFileBox() {
+            PageFactory.initElements(driver, this);
+        }
+
+        CodeEditor codeEditor = new CodeEditor(driver);
+
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
@@ -267,13 +300,16 @@ public class FileManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "btnUpload")
+        @FindBys({
+                @FindBy(className = "btn-upload"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement buttonUpload;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 }
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FunctionManagePage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FunctionManagePage.java
index b0800dc..02f71f1 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FunctionManagePage.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/FunctionManagePage.java
@@ -27,6 +27,8 @@ import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
 import java.util.List;
 
 import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.remote.RemoteWebDriver;
 import org.openqa.selenium.support.FindBy;
@@ -37,17 +39,17 @@ import org.openqa.selenium.support.ui.WebDriverWait;
 
 @Getter
 public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
-    @FindBy(id = "btnCreateUdfFunction")
+    @FindBy(className = "btn-create-udf-function")
     private WebElement buttonCreateUdfFunction;
 
-    @FindBy(className = "udf-function-items")
+    @FindBy(className = "items")
     private List<WebElement> functionList;
 
     @FindBys({
-        @FindBy(className = "el-popconfirm"),
-        @FindBy(className = "el-button--primary"),
+            @FindBy(className = "n-popconfirm__action"),
+            @FindBy(className = "n-button--primary-type"),
     })
-    private List<WebElement> buttonConfirm;
+    private WebElement buttonConfirm;
 
     private final CreateUdfFunctionBox createUdfFunctionBox;
 
@@ -64,6 +66,8 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
     public FunctionManagePage createUdfFunction(String udfFunctionName, String className, String udfResourceName, String description) {
         buttonCreateUdfFunction().click();
 
+        createUdfFunctionBox().radioFunctionType().click();
+
         createUdfFunctionBox().inputFunctionName().sendKeys(udfFunctionName);
 
         createUdfFunctionBox().inputClassName().sendKeys(className);
@@ -74,7 +78,7 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
 
         createUdfFunctionBox().selectUdfResource()
             .stream()
-            .filter(it -> it.getText().contains(udfResourceName))
+            .filter(it -> it.getAttribute("innerHTML").contains(udfResourceName))
             .findFirst()
             .orElseThrow(() -> new RuntimeException(String.format("No %s in udf resource list", udfResourceName)))
             .click();
@@ -88,16 +92,14 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
         functionList()
             .stream()
             .filter(it -> it.getText().contains(currentName))
-            .flatMap(it -> it.findElements(By.id("btnRename")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-edit")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No rename button in function manage list"))
             .click();
 
-        new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.id("createUdfDialog")));
-
-        renameUdfFunctionBox().inputFunctionName().clear();
-
+        renameUdfFunctionBox().inputFunctionName().sendKeys(Keys.CONTROL + "a");
+        renameUdfFunctionBox().inputFunctionName().sendKeys(Keys.BACK_SPACE);
         renameUdfFunctionBox().inputFunctionName().sendKeys(afterName);
 
         renameUdfFunctionBox.buttonSubmit().click();
@@ -109,18 +111,13 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
         functionList()
             .stream()
             .filter(it -> it.getText().contains(udfFunctionName))
-            .flatMap(it -> it.findElements(By.id("btnDelete")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-delete")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No delete button in udf resource list"))
             .click();
 
-        buttonConfirm()
-            .stream()
-            .filter(WebElement::isDisplayed)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("No confirm button when deleting in udf resource page"))
-            .click();
+        ((JavascriptExecutor) driver).executeScript("arguments[0].click();", buttonConfirm());
 
         return this;
     }
@@ -131,25 +128,43 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputFunctionName")
+        @FindBys({
+                @FindBy(className = "radio-function-type"),
+                @FindBy(tagName = "input"),
+        })
+        private WebElement radioFunctionType;
+
+        @FindBys({
+                @FindBy(className = "input-function-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputFunctionName;
 
-        @FindBy(id = "inputClassName")
+        @FindBys({
+                @FindBy(className = "input-class-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputClassName;
 
-        @FindBy(id = "btnUdfResourceDropDown")
+        @FindBys({
+                @FindBy(className = "btn-udf-resource-dropdown"),
+                @FindBy(className = "n-base-selection"),
+        })
         private WebElement buttonUdfResourceDropDown;
 
-        @FindBy(className = "vue-treeselect__menu")
+        @FindBy(className = "n-tree-node-content__text")
         private List<WebElement> selectUdfResource;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
@@ -159,19 +174,28 @@ public class FunctionManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputFunctionName")
+        @FindBys({
+                @FindBy(className = "input-function-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputFunctionName;
 
-        @FindBy(id = "inputClassName")
+        @FindBys({
+                @FindBy(className = "input-class-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputClassName;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 }
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/ResourcePage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/ResourcePage.java
index 7fc207b..8009775 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/ResourcePage.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/ResourcePage.java
@@ -32,13 +32,13 @@ import org.openqa.selenium.support.ui.WebDriverWait;
 
 @Getter
 public class ResourcePage extends NavBarPage implements NavBarPage.NavBarItem {
-    @FindBy(className = "tab-file-manage")
+    @FindBy(css = ".tab-vertical > .n-menu-item:nth-child(1) > .n-menu-item-content")
     private WebElement fileManageTab;
 
-    @FindBy(className = "tab-udf-resource-manage")
+    @FindBy(css = ".tab-vertical .n-submenu:nth-of-type(2) .n-menu-item:nth-of-type(1) > .n-menu-item-content")
     private WebElement udfManageTab;
 
-    @FindBy(className = "tab-function-resource-manage")
+    @FindBy(css = ".tab-vertical .n-submenu:nth-of-type(2) .n-menu-item:nth-of-type(2) > .n-menu-item-content")
     private WebElement functionManageTab;
 
     public ResourcePage(RemoteWebDriver driver) {
@@ -47,20 +47,20 @@ public class ResourcePage extends NavBarPage implements NavBarPage.NavBarItem {
 
     public <T extends ResourcePage.Tab> T goToTab(Class<T> tab) {
         if (tab == FileManagePage.class) {
-            WebElement fileManageTabElement = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(fileManageTab));
-            fileManageTabElement.click();
+            new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(fileManageTab));
+            fileManageTab.click();
             return tab.cast(new FileManagePage(driver));
         }
 
         if (tab == UdfManagePage.class) {
-            WebElement udfManageTabElement = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(udfManageTab));
-            udfManageTabElement.click();
+            new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(udfManageTab));
+            udfManageTab.click();
             return tab.cast(new UdfManagePage(driver));
         }
 
         if (tab == FunctionManagePage.class) {
-            WebElement functionManageTabElement = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(functionManageTab));
-            functionManageTabElement.click();
+            new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(functionManageTab));
+            functionManageTab.click();
             return tab.cast(new FunctionManagePage(driver));
         }
 
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/UdfManagePage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/UdfManagePage.java
index ae867e1..092f407 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/UdfManagePage.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/resource/UdfManagePage.java
@@ -27,6 +27,7 @@ import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
 import java.util.List;
 
 import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.remote.LocalFileDetector;
 import org.openqa.selenium.remote.RemoteWebDriver;
@@ -36,20 +37,20 @@ import org.openqa.selenium.support.PageFactory;
 
 @Getter
 public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
-    @FindBy(id = "btnCreateDirectory")
+    @FindBy(className = "btn-create-directory")
     private WebElement buttonCreateDirectory;
 
-    @FindBy(id = "btnUploadUdf")
+    @FindBy(className = "btn-upload-udf")
     private WebElement buttonUploadUdf;
 
-    @FindBy(className = "udf-items")
+    @FindBy(className = "items")
     private List<WebElement> udfList;
 
     @FindBys({
-        @FindBy(className = "el-popconfirm"),
-        @FindBy(className = "el-button--primary"),
+        @FindBy(className = "n-popconfirm__action"),
+        @FindBy(className = "n-button--primary-type"),
     })
-    private List<WebElement> buttonConfirm;
+    private WebElement buttonConfirm;
 
     private final UploadFileBox uploadFileBox;
 
@@ -92,7 +93,7 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
         udfList()
             .stream()
             .filter(it -> it.getText().contains(fileName))
-            .flatMap(it -> it.findElements(By.id("btnDownload")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-download")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No download button in udf manage list"))
@@ -105,7 +106,7 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
         udfList()
             .stream()
             .filter(it -> it.getText().contains(currentName))
-            .flatMap(it -> it.findElements(By.id("btnRename")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-edit")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No rename button in udf manage list"))
@@ -122,18 +123,13 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
         udfList()
             .stream()
             .filter(it -> it.getText().contains(name))
-            .flatMap(it -> it.findElements(By.id("btnDelete")).stream())
+            .flatMap(it -> it.findElements(By.className("btn-delete")).stream())
             .filter(WebElement::isDisplayed)
             .findFirst()
             .orElseThrow(() -> new RuntimeException("No delete button in udf manage list"))
             .click();
 
-        buttonConfirm()
-            .stream()
-            .filter(WebElement::isDisplayed)
-            .findFirst()
-            .orElseThrow(() -> new RuntimeException("No confirm button when deleting"))
-            .click();
+        ((JavascriptExecutor) driver).executeScript("arguments[0].click();", buttonConfirm());
 
         return this;
     }
@@ -144,16 +140,22 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputName")
+        @FindBys({
+                @FindBy(className = "input-directory-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputName;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
@@ -163,13 +165,16 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "btnUpload")
+        @FindBys({
+                @FindBy(className = "btn-upload"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement buttonUpload;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 
@@ -179,16 +184,22 @@ public class UdfManagePage extends NavBarPage implements ResourcePage.Tab {
             PageFactory.initElements(driver, this);
         }
 
-        @FindBy(id = "inputDirectoryName")
+        @FindBys({
+                @FindBy(className = "input-directory-name"),
+                @FindBy(tagName = "input"),
+        })
         private WebElement inputDirectoryName;
 
-        @FindBy(id = "inputDescription")
+        @FindBys({
+                @FindBy(className = "input-description"),
+                @FindBy(tagName = "textarea"),
+        })
         private WebElement inputDescription;
 
-        @FindBy(id = "btnSubmit")
+        @FindBy(className = "btn-submit")
         private WebElement buttonSubmit;
 
-        @FindBy(id = "btnCancel")
+        @FindBy(className = "btn-cancel")
         private WebElement buttonCancel;
     }
 }
diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java
index 5453af9..b5784b6 100644
--- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java
+++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java
@@ -83,7 +83,8 @@ public final class UserPage extends NavBarPage implements SecurityPage.Tab {
         return this;
     }
 
-    public UserPage update(String user, String editUser, String editPassword, String editEmail, String editPhone) {
+    public UserPage update(String user, String editUser, String editPassword, String editEmail, String editPhone,
+                           String tenant) {
         userList().stream()
             .filter(it -> it.findElement(By.className("name")).getAttribute("innerHTML").contains(user))
             .flatMap(it -> it.findElements(By.className("edit")).stream())
@@ -98,6 +99,18 @@ public final class UserPage extends NavBarPage implements SecurityPage.Tab {
 
         editUserForm().inputUserPassword().sendKeys(editPassword);
 
+        createUserForm().btnSelectTenantDropdown().click();
+
+        new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOfElementLocated(new By.ByClassName(
+                "n-base-select-option__content")));
+
+        createUserForm().selectTenant()
+                .stream()
+                .filter(it -> it.getText().contains(tenant))
+                .findFirst()
+                .orElseThrow(() -> new RuntimeException(String.format("No %s in tenant dropdown list", tenant)))
+                .click();
+
         editUserForm().inputEmail().sendKeys(Keys.CONTROL+"a");
         editUserForm().inputEmail().sendKeys(Keys.BACK_SPACE);
         editUserForm().inputEmail().sendKeys(editEmail);
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/create/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/create/index.tsx
index d6d0f7b..287da3f 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/create/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/create/index.tsx
@@ -77,6 +77,7 @@ export default defineComponent({
               v-model={[this.fileForm.fileName, 'value']}
               placeholder={t('resource.file.enter_name_tips')}
               style={{ width: '300px' }}
+              class='input-file-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.file_format')} path='suffix'>
@@ -85,6 +86,7 @@ export default defineComponent({
               v-model={[this.fileForm.suffix, 'value']}
               options={this.fileSuffixOptions}
               style={{ width: '100px' }}
+              class='select-file-format'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.description')} path='description'>
@@ -93,6 +95,7 @@ export default defineComponent({
               v-model={[this.fileForm.description, 'value']}
               placeholder={t('resource.file.enter_description_tips')}
               style={{ width: '430px' }}
+              class='input-description'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.file_content')} path='content'>
@@ -106,7 +109,7 @@ export default defineComponent({
           </NFormItem>
           <div class={styles['file-edit-content']}>
             <div class={styles.submit}>
-              <NButton type='info' size='small' round onClick={this.handleFile}>
+              <NButton type='info' size='small' round onClick={this.handleFile} class='btn-submit'>
                 {t('resource.file.save')}
               </NButton>
               <NButton
@@ -115,6 +118,7 @@ export default defineComponent({
                 text
                 style={{ marginLeft: '15px' }}
                 onClick={this.handleReturn}
+                class='btn-cancel'
               >
                 {t('resource.file.return')}
               </NButton>
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/edit/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/edit/index.tsx
index bffa252..dea3875 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/edit/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/edit/index.tsx
@@ -89,6 +89,7 @@ export default defineComponent({
                   text
                   style={{ marginRight: '15px' }}
                   onClick={this.handleReturn}
+                  class='btn-cancel'
                 >
                   {t('resource.file.return')}
                 </NButton>
@@ -97,6 +98,7 @@ export default defineComponent({
                   size='small'
                   round
                   onClick={() => this.handleFileContent()}
+                  class='btn-submit'
                 >
                   {t('resource.file.save')}
                 </NButton>
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx
index ff1834f..bf00ea5 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx
@@ -59,6 +59,8 @@ export default defineComponent({
         title={t('resource.file.create_folder')}
         onCancel={this.hideModal}
         onConfirm={this.handleFolder}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -70,6 +72,7 @@ export default defineComponent({
             <NInput
               v-model={[this.folderForm.name, 'value']}
               placeholder={t('resource.file.enter_name_tips')}
+              class='input-directory-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.description')} path='description'>
@@ -77,6 +80,7 @@ export default defineComponent({
               type='textarea'
               v-model={[this.folderForm.description, 'value']}
               placeholder={t('resource.file.enter_description_tips')}
+              class='input-description'
             />
           </NFormItem>
         </NForm>
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/index.tsx
index 62fc894..0445f2d 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/index.tsx
@@ -188,13 +188,13 @@ export default defineComponent({
           <div class={styles['conditions-model']}>
             <NSpace>
               <NButtonGroup>
-                <NButton onClick={handleCreateFolder}>
+                <NButton onClick={handleCreateFolder} class="btn-create-directory">
                   {t('resource.file.create_folder')}
                 </NButton>
-                <NButton onClick={handleCreateFile}>
+                <NButton onClick={handleCreateFile} class="btn-create-file">
                   {t('resource.file.create_file')}
                 </NButton>
-                <NButton onClick={handleUploadFile}>
+                <NButton onClick={handleUploadFile} class="btn-upload-file">
                   {t('resource.file.upload_files')}
                 </NButton>
               </NButtonGroup>
@@ -226,6 +226,7 @@ export default defineComponent({
             striped
             size={'small'}
             class={styles['table-box']}
+            row-class-name='items'
           />
           <div class={styles.pagination}>
             <NPagination
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx
index 44c38a5..8e34650 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx
@@ -75,6 +75,8 @@ export default defineComponent({
         title={t('resource.file.rename')}
         onCancel={this.hideModal}
         onConfirm={this.handleFile}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -86,6 +88,7 @@ export default defineComponent({
             <NInput
               v-model={[this.renameForm.name, 'value']}
               placeholder={t('resource.file.enter_name_tips')}
+              class='input-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.description')} path='description'>
@@ -93,6 +96,7 @@ export default defineComponent({
               type='textarea'
               v-model={[this.renameForm.description, 'value']}
               placeholder={t('resource.file.enter_description_tips')}
+              class='input-description'
             />
           </NFormItem>
         </NForm>
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx b/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx
index ea5ef89..7544856 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx
@@ -102,6 +102,7 @@ export default defineComponent({
                 }}
                 style={{ marginRight: '-5px' }}
                 circle
+                class='btn-edit'
               >
                 <NIcon>
                   <FormOutlined />
@@ -126,6 +127,7 @@ export default defineComponent({
                 }
                 style={{ marginRight: '-5px' }}
                 circle
+                class="btn-rename"
               >
                 <NIcon>
                   <EditOutlined />
@@ -146,6 +148,7 @@ export default defineComponent({
                 circle
                 style={{ marginRight: '-5px' }}
                 onClick={() => downloadResource(this.row.id)}
+                class='btn-download'
               >
                 <NIcon>
                   <DownloadOutlined />
@@ -158,7 +161,7 @@ export default defineComponent({
           {{
             default: () => t('resource.file.delete'),
             trigger: () => (
-              <NButton size='tiny' type='error' circle>
+              <NButton size='tiny' type='error' circle class='btn-delete'>
                 <NPopconfirm
                   positive-text={t('resource.file.confirm')}
                   negative-text={t('resource.file.cancel')}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx
index f64c1f2..3e3443d 100644
--- a/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx
@@ -65,6 +65,8 @@ export default defineComponent({
         title={t('resource.file.upload_files')}
         onCancel={this.hideModal}
         onConfirm={this.handleFile}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -76,6 +78,7 @@ export default defineComponent({
             <NInput
               v-model={[this.uploadForm.name, 'value']}
               placeholder={t('resource.file.enter_name_tips')}
+              class='input-file-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.description')} path='description'>
@@ -83,12 +86,14 @@ export default defineComponent({
               type='textarea'
               v-model={[this.uploadForm.description, 'value']}
               placeholder={t('resource.file.enter_description_tips')}
+              class='input-description'
             />
           </NFormItem>
           <NFormItem label={t('resource.file.upload_files')} path='file'>
             <NUpload
               v-model={[this.uploadForm.file, 'value']}
               customRequest={this.customRequest}
+              class='btn-upload'
             >
               <NButton>{t('resource.file.upload_files')}</NButton>
             </NUpload>
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/function/components/function-modal.tsx b/dolphinscheduler-ui-next/src/views/resource/udf/function/components/function-modal.tsx
index 3d86780..d8f9fca 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/function/components/function-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/function/components/function-modal.tsx
@@ -124,6 +124,8 @@ export default defineComponent({
         }
         onCancel={this.hideModal}
         onConfirm={this.row.id ? this.handleRename : this.handleCreate}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -135,6 +137,7 @@ export default defineComponent({
             <NRadioGroup
               v-model={[this.functionForm.type, 'value']}
               name='type'
+              class='radio-function-type'
             >
               <NRadio value='HIVE'>HIVE UDF</NRadio>
             </NRadioGroup>
@@ -146,6 +149,7 @@ export default defineComponent({
             <NInput
               v-model={[this.functionForm.funcName, 'value']}
               placeholder={t('resource.function.enter_udf_unction_name_tips')}
+              class='input-function-name'
             />
           </NFormItem>
           <NFormItem
@@ -155,6 +159,7 @@ export default defineComponent({
             <NInput
               v-model={[this.functionForm.className, 'value']}
               placeholder={t('resource.function.enter_package_name_tips')}
+              class='input-class-name'
             />
           </NFormItem>
           <NFormItem
@@ -173,6 +178,7 @@ export default defineComponent({
                 defaultValue={this.functionForm.resourceId}
                 disabled={this.uploadShow}
                 showPath={false}
+                class='btn-udf-resource-dropdown'
               ></NTreeSelect>
               <NButton
                 type='primary'
@@ -244,6 +250,7 @@ export default defineComponent({
                   type='textarea'
                   v-model={[this.uploadForm.description, 'value']}
                   placeholder={t('resource.function.enter_description_tips')}
+                  class='input-description'
                 />
               </NFormItem>
               <NFormItem label=' '>
@@ -260,6 +267,7 @@ export default defineComponent({
               type='textarea'
               v-model={[this.functionForm.description, 'value']}
               placeholder={t('resource.function.enter_instructions_tips')}
+              class='input-description'
             />
           </NFormItem>
         </NForm>
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/function/index.tsx b/dolphinscheduler-ui-next/src/views/resource/udf/function/index.tsx
index 9ebbbc0..e53f1b7 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/function/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/function/index.tsx
@@ -94,7 +94,7 @@ export default defineComponent({
         <Card class={styles.card}>
           <div class={styles.header}>
             <NSpace>
-              <NButton type='primary' onClick={this.handleCreateFolder}>
+              <NButton type='primary' onClick={this.handleCreateFolder} class='btn-create-udf-function'>
                 {t('resource.function.create_udf_function')}
               </NButton>
             </NSpace>
@@ -124,6 +124,7 @@ export default defineComponent({
             striped
             size={'small'}
             class={styles.table}
+            row-class-name='items'
           />
           <div class={styles.pagination}>
             <NPagination
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/function/use-table.ts b/dolphinscheduler-ui-next/src/views/resource/udf/function/use-table.ts
index 6698922..14174c5 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/function/use-table.ts
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/function/use-table.ts
@@ -94,6 +94,7 @@ export function useTable() {
                         circle: true,
                         type: 'info',
                         size: 'tiny',
+                        class: 'btn-edit',
                         onClick: () => {
                           handleEdit(row)
                         }
@@ -124,7 +125,8 @@ export function useTable() {
                             {
                               circle: true,
                               type: 'error',
-                              size: 'tiny'
+                              size: 'tiny',
+                              class: 'btn-delete'
                             },
                             {
                               icon: () => h(DeleteOutlined)
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/folder-modal.tsx b/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/folder-modal.tsx
index ef509cb..909f645 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/folder-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/folder-modal.tsx
@@ -79,6 +79,8 @@ export default defineComponent({
         title={t('resource.udf.create_folder')}
         onCancel={this.hideModal}
         onConfirm={this.row.id ? this.handleRename : this.handleCreate}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -90,6 +92,7 @@ export default defineComponent({
             <NInput
               v-model={[this.folderForm.name, 'value']}
               placeholder={t('resource.udf.enter_name_tips')}
+              class='input-directory-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.udf.description')} path='description'>
@@ -97,6 +100,7 @@ export default defineComponent({
               type='textarea'
               v-model={[this.folderForm.description, 'value']}
               placeholder={t('resource.udf.enter_description_tips')}
+              class='input-description'
             />
           </NFormItem>
         </NForm>
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/upload-modal.tsx b/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/upload-modal.tsx
index 1379970..6514c4d 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/upload-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/resource/components/upload-modal.tsx
@@ -66,6 +66,8 @@ export default defineComponent({
         title={t('resource.udf.file_upload')}
         onCancel={this.hideModal}
         onConfirm={this.handleFolder}
+        confirmClassName='btn-submit'
+        cancelClassName='btn-cancel'
       >
         <NForm
           rules={this.rules}
@@ -77,6 +79,7 @@ export default defineComponent({
             <NInput
               v-model={[this.uploadForm.name, 'value']}
               placeholder={t('resource.udf.enter_name_tips')}
+              class='input-file-name'
             />
           </NFormItem>
           <NFormItem label={t('resource.udf.description')} path='description'>
@@ -84,12 +87,14 @@ export default defineComponent({
               type='textarea'
               v-model={[this.uploadForm.description, 'value']}
               placeholder={t('resource.udf.enter_description_tips')}
+              class='input-description'
             />
           </NFormItem>
           <NFormItem label={t('resource.udf.upload_files')} path='file'>
             <NUpload
               v-model={[this.uploadForm.file, 'value']}
               customRequest={this.customRequest}
+              class='btn-upload'
             >
               <NButton>
                 {t('resource.udf.upload')}
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/resource/index.tsx b/dolphinscheduler-ui-next/src/views/resource/udf/resource/index.tsx
index 2f12aea..eb23e93 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/resource/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/resource/index.tsx
@@ -115,10 +115,10 @@ export default defineComponent({
         <Card class={styles.card}>
           <div class={styles.header}>
             <NSpace>
-              <NButton type='primary' onClick={this.handleCreateFolder}>
+              <NButton type='primary' onClick={this.handleCreateFolder} class='btn-create-directory'>
                 {t('resource.udf.create_folder')}
               </NButton>
-              <NButton strong secondary onClick={this.handleUploadFile}>
+              <NButton strong secondary onClick={this.handleUploadFile} class='btn-upload-udf'>
                 {t('resource.udf.upload_udf_resources')}
               </NButton>
             </NSpace>
@@ -151,6 +151,7 @@ export default defineComponent({
                   striped
                   size={'small'}
                   class={styles.table}
+                  row-class-name='items'
                 />
                 <div class={styles.pagination}>
                   <NPagination
diff --git a/dolphinscheduler-ui-next/src/views/resource/udf/resource/use-table.ts b/dolphinscheduler-ui-next/src/views/resource/udf/resource/use-table.ts
index 05014d7..f6b8074 100644
--- a/dolphinscheduler-ui-next/src/views/resource/udf/resource/use-table.ts
+++ b/dolphinscheduler-ui-next/src/views/resource/udf/resource/use-table.ts
@@ -131,6 +131,7 @@ export function useTable() {
                         circle: true,
                         type: 'info',
                         size: 'tiny',
+                        class: 'btn-edit',
                         onClick: () => {
                           handleEdit(row)
                         }
@@ -154,6 +155,7 @@ export function useTable() {
                         circle: true,
                         type: 'info',
                         size: 'tiny',
+                        class: 'btn-download',
                         disabled: row?.directory ? true : false,
                         onClick: () => downloadResource(row.id)
                       },
@@ -184,7 +186,8 @@ export function useTable() {
                               tag: 'div',
                               circle: true,
                               type: 'error',
-                              size: 'tiny'
+                              size: 'tiny',
+                              class: 'btn-delete',
                             },
                             {
                               icon: () => h(DeleteOutlined)
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts
index 1d508d6..67b254d 100644
--- a/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts
@@ -43,7 +43,8 @@ export function useColumns(onCallback: Function) {
       },
       {
         title: t('security.user.username'),
-        key: 'userName'
+        key: 'userName',
+        className: 'name'
       },
       {
         title: t('security.user.user_type'),
@@ -157,6 +158,7 @@ export function useColumns(onCallback: Function) {
                         circle: true,
                         type: 'info',
                         size: 'small',
+                        class: 'edit',
                         onClick: () => void onCallback({ rowData }, 'edit')
                       },
                       () => h(NIcon, null, () => h(EditOutlined))