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

ruanwenjun 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 9f68254ffa [Improvement-18712][E2E] Fix flaky e2e test (#18715)
9f68254ffa is described below

commit 9f68254ffa7446a4bd52969dc5fdd6471b0d6850
Author: xiangzihao <[email protected]>
AuthorDate: Fri Oct 16 18:49:15 2026 +0800

    [Improvement-18712][E2E] Fix flaky e2e test (#18715)
---
 .../e2e/cases/tasks/PythonTaskE2ETest.java         |   2 +-
 .../e2e/cases/tasks/ShellTaskE2ETest.java          |   6 +-
 .../e2e/cases/workflow/BaseWorkflowE2ETest.java    |  20 ++-
 .../e2e/pages/common/CodeEditor.java               | 138 +++++++++------------
 .../e2e/pages/datasource/DataSourcePage.java       |  18 +--
 .../e2e/pages/project/workflow/WorkflowForm.java   |  21 +++-
 .../pages/project/workflow/WorkflowRunDialog.java  |  24 +---
 .../pages/project/workflow/task/ShellTaskForm.java |   4 -
 .../pages/project/workflow/task/TaskNodeForm.java  |  47 ++++---
 .../e2e/pages/resource/FileManagePage.java         |  23 ++--
 .../e2e/core/DolphinSchedulerExtension.java        |  69 ++++++++++-
 .../dolphinscheduler/e2e/core/UiInteraction.java   |  63 ++++++++++
 .../e2e/core/WebDriverWaitFactory.java             |   2 +-
 dolphinscheduler-ui/src/components/modal/index.tsx |  18 ++-
 .../src/components/monaco-editor/index.tsx         |  12 +-
 .../workflow/definition/components/start-modal.tsx |   1 +
 .../resource/components/resource/folder/index.tsx  |   1 +
 17 files changed, 316 insertions(+), 153 deletions(-)

diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/PythonTaskE2ETest.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/PythonTaskE2ETest.java
index cb18e58848..09a3e7b4e0 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/PythonTaskE2ETest.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/PythonTaskE2ETest.java
@@ -279,7 +279,7 @@ public class PythonTaskE2ETest extends BaseWorkflowE2ETest {
                 .<PythonTaskForm>addTask(WorkflowForm.TaskType.PYTHON)
                 .script(pythonScripts)
                 .name(taskName)
-                .selectResource(testFileName)
+                .selectResource(testFileName + ".sh")
                 .addParam("file_name", String.format("%s.sh", testFileName))
                 .submit()
 
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/ShellTaskE2ETest.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/ShellTaskE2ETest.java
index 7a547201c9..be2f60e4f8 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/ShellTaskE2ETest.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/tasks/ShellTaskE2ETest.java
@@ -35,6 +35,8 @@ import 
org.apache.dolphinscheduler.e2e.pages.security.SecurityPage;
 import org.apache.dolphinscheduler.e2e.pages.security.TenantPage;
 import org.apache.dolphinscheduler.e2e.pages.security.UserPage;
 
+import java.util.UUID;
+
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Test;
@@ -212,7 +214,7 @@ public class ShellTaskE2ETest extends BaseWorkflowE2ETest {
 
     @Test
     void testRunShellTasks_UsingResourceFile() {
-        String testFileName = "echo";
+        String testFileName = "echo_" + UUID.randomUUID();
         new ResourcePage(browser)
                 .goToNav(ResourcePage.class)
                 .goToTab(FileManagePage.class)
@@ -231,7 +233,7 @@ public class ShellTaskE2ETest extends BaseWorkflowE2ETest {
                 .<ShellTaskForm>addTask(WorkflowForm.TaskType.SHELL)
                 .script("cat " + testFileName + ".sh")
                 .name(taskName)
-                .selectResource(testFileName)
+                .selectResource(testFileName + ".sh")
                 .submit()
 
                 .submit()
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/workflow/BaseWorkflowE2ETest.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/workflow/BaseWorkflowE2ETest.java
index 550f83d719..fd3d610079 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/workflow/BaseWorkflowE2ETest.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/workflow/BaseWorkflowE2ETest.java
@@ -91,17 +91,25 @@ public abstract class BaseWorkflowE2ETest {
         final ProjectDetailPage projectPage = new ProjectPage(browser)
                 .goToNav(ProjectPage.class)
                 .goTo(projectName);
-        return await()
+        log.info("Waiting for workflow instance success: project={}, 
workflow={}", projectName, workflowName);
+        return await("Workflow instance should succeed: " + workflowName)
                 .until(() -> {
                     browser.navigate().refresh();
-                    return projectPage
+                    List<WorkflowInstanceTab.Row> instances = projectPage
                             .goToTab(WorkflowInstanceTab.class)
                             .instances()
                             .stream()
-                            .filter(it -> 
it.workflowInstanceName().startsWith(workflowName))
-                            .filter(WorkflowInstanceTab.Row::isSuccess)
-                            .findFirst()
-                            .orElse(null);
+                            .filter(it -> 
it.workflowInstanceName().startsWith(workflowName + "-"))
+                            .collect(Collectors.toList());
+                    for (WorkflowInstanceTab.Row instance : instances) {
+                        if (instance.isFailed()) {
+                            throw new IllegalStateException(
+                                    "Workflow instance failed while waiting 
for success: project="
+                                            + projectName + ", instance=" + 
instance.workflowInstanceName()
+                                            + ", url=" + 
browser.getCurrentUrl());
+                        }
+                    }
+                    return 
instances.stream().filter(WorkflowInstanceTab.Row::isSuccess).findFirst().orElse(null);
                 }, Objects::nonNull);
     }
 
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 60f1be2ee9..09bdeb268d 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
@@ -17,112 +17,94 @@
 
 package org.apache.dolphinscheduler.e2e.pages.common;
 
-import org.apache.dolphinscheduler.e2e.core.Constants;
+import org.apache.dolphinscheduler.e2e.core.UiInteraction;
 import org.apache.dolphinscheduler.e2e.core.WebDriverWaitFactory;
 
-import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
-import lombok.Getter;
-import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 
-import org.junit.platform.commons.util.StringUtils;
+import org.openqa.selenium.By;
 import org.openqa.selenium.JavascriptExecutor;
 import org.openqa.selenium.Keys;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.interactions.Actions;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-import org.openqa.selenium.support.PageFactory;
-import org.openqa.selenium.support.ui.ExpectedConditions;
 
-@Getter
 @Slf4j
 public final class CodeEditor {
 
-    @FindBys({
-            @FindBy(className = "monaco-editor"),
-            @FindBy(className = "view-line"),
-    })
-    private List<WebElement> editor;
-
-    @FindBy(className = "pre-tasks-model")
-    private WebElement scrollBar;
-
-    private WebDriver driver;
+    private final WebDriver driver;
 
     public CodeEditor(WebDriver driver) {
-        PageFactory.initElements(driver, this);
         this.driver = driver;
     }
 
-    @SneakyThrows
     public CodeEditor content(String content) {
-        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.elementToBeClickable(editor.get(0)));
-
-        Actions actions = new Actions(this.driver);
-
-        List<String> contentList = 
List.of(content.split(Constants.LINE_SEPARATOR));
-
-        try {
-            ((JavascriptExecutor) 
driver).executeScript("arguments[0].scrollIntoView();", scrollBar);
-        } catch (org.openqa.selenium.NoSuchElementException ignored) {
-            log.warn("scroll bar not found, skipping...");
+        UiInteraction.click(driver, ".monaco-editor .overflow-guard");
+        WebElement focusedInput = driver.switchTo().activeElement();
+        if (!"textarea".equals(focusedInput.getTagName())
+                || !focusedInput.getDomAttribute("class").contains("inputarea")
+                || focusedInput.getDomAttribute("readonly") != null) {
+            throw new IllegalStateException("Expected focus in an editable 
Monaco input: " + focusedInput.getTagName());
         }
-
-        for (int i = 0; i < contentList.size(); i++) {
-            String editorLineText;
-            String inputContent = contentList.get(i);
-            if (i == 0) {
-                actions.moveToElement(editor.get(i))
-                        .click()
-                        .sendKeys(inputContent)
-                        .sendKeys(Constants.LINE_SEPARATOR)
-                        .perform();
-                continue;
+        WebElement editor = 
focusedInput.findElement(By.xpath("ancestor::*[@data-editor-model-version][1]"));
+        String initialVersion = 
editor.getDomAttribute("data-editor-model-version");
+        int initialLength = 
Integer.parseInt(editor.getDomAttribute("data-editor-content-length"));
+        String normalized = content.replace("\r\n", "\n");
+        log.info("Replacing Monaco content: characters={}, initialVersion={}, 
initialLength={}",
+                normalized.length(), initialVersion, initialLength);
+        if (!normalized.isEmpty() || initialLength != 0) {
+            Actions input = new 
Actions(driver).keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL);
+            if (normalized.isEmpty()) {
+                input.sendKeys(Keys.BACK_SPACE).perform();
             } else {
-                editorLineText = editor.get(i).getText();
+                clipboard(normalized);
+                
input.keyDown(Keys.CONTROL).sendKeys("v").keyUp(Keys.CONTROL).perform();
             }
+            WebDriverWaitFactory.createWebDriverWait(driver)
+                    .withMessage("Monaco paste did not update the model to " + 
normalized.length() + " characters")
+                    .until(ignored -> 
!initialVersion.equals(editor.getDomAttribute("data-editor-model-version"))
+                            && 
Integer.parseInt(editor.getDomAttribute("data-editor-content-length")) == 
normalized
+                                    .length());
+        }
 
-            if (StringUtils.isNotBlank(inputContent)) {
-                if (editorLineText.isEmpty()) {
-                    actions.moveToElement(editor.get(i))
-                            .click()
-                            .sendKeys(inputContent)
-                            .sendKeys(Constants.LINE_SEPARATOR)
-                            .perform();
-                    Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS);
-                } else {
-                    for (int p = 0; p < editorLineText.strip().length(); p++) {
-                        clearLine(actions, editor.get(i));
-                    }
-                    if (!editorLineText.isEmpty()) {
-                        clearLine(actions, editor.get(i));
-                    }
-                    actions.moveToElement(editor.get(i))
-                            .click()
-                            .sendKeys(inputContent)
-                            .sendKeys(Constants.LINE_SEPARATOR)
-                            .perform();
-                    Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS);
-                }
-            } else {
-                actions.moveToElement(editor.get(i))
-                        .click()
-                        .sendKeys(Constants.LINE_SEPARATOR)
-                        .perform();
-                Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS);
+        if (!normalized.isEmpty()) {
+            // Clear the clipboard so an unsuccessful copy cannot validate the 
paste payload.
+            String sentinel = UUID.randomUUID().toString();
+            clipboard(sentinel);
+            new Actions(driver)
+                    .keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL)
+                    .keyDown(Keys.CONTROL).sendKeys("c").keyUp(Keys.CONTROL)
+                    .perform();
+            String actual = WebDriverWaitFactory.createWebDriverWait(driver)
+                    .withMessage("Monaco copy did not populate the clipboard")
+                    .until(ignored -> {
+                        String copied = clipboard(null);
+                        return sentinel.equals(copied) ? null : copied;
+                    }).replace("\r\n", "\n");
+            if (!normalized.equals(actual)) {
+                throw new IllegalStateException("Monaco content mismatch: 
expected characters=" + normalized.length()
+                        + ", copied characters=" + actual.length());
             }
         }
-
+        new Actions(driver).sendKeys(Keys.ARROW_RIGHT).perform();
+        log.info("Monaco content verified: characters={}", 
normalized.length());
         return this;
     }
 
-    private void clearLine(Actions actions, WebElement element) {
-        actions.moveToElement(element)
-                .click()
-                .sendKeys(Keys.BACK_SPACE)
-                .perform();
+    private String clipboard(String value) {
+        Map<?, ?> result = (Map<?, ?>) ((JavascriptExecutor) 
driver).executeAsyncScript(
+                "const value = arguments[0], done = arguments[arguments.length 
- 1];"
+                        + "if (!navigator.clipboard) { done({error: 'Clipboard 
API unavailable; check secure origin configuration'}); return; }"
+                        + "const operation = value === null ? 
navigator.clipboard.readText() : navigator.clipboard.writeText(value);"
+                        + "operation.then(text => done({text: value === null ? 
text : value}),"
+                        + "error => done({error: error.name + ': ' + 
error.message}));",
+                value);
+        if (result.containsKey("error")) {
+            throw new IllegalStateException("Monaco clipboard operation 
failed: " + result.get("error"));
+        }
+        return (String) result.get("text");
     }
 }
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/datasource/DataSourcePage.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/datasource/DataSourcePage.java
index aa91da1fd9..2b356a347f 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/datasource/DataSourcePage.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/datasource/DataSourcePage.java
@@ -17,10 +17,12 @@
 
 package org.apache.dolphinscheduler.e2e.pages.datasource;
 
+import org.apache.dolphinscheduler.e2e.core.UiInteraction;
 import org.apache.dolphinscheduler.e2e.core.WebDriverWaitFactory;
 import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
 
 import java.util.List;
+import java.util.Locale;
 
 import lombok.Getter;
 
@@ -63,16 +65,16 @@ public class DataSourcePage extends NavBarPage implements 
NavBarPage.NavBarItem
     public DataSourcePage createDataSource(String dataSourceType, String 
dataSourceName, String dataSourceDescription,
                                            String ip, String port, String 
userName, String password, String database,
                                            String jdbcParams) {
-        buttonCreateDataSource().click();
+        UiInteraction.click(driver, ".btn-create-data-source");
 
         
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.visibilityOf(dataSourceModal));
-        By dataSourceTypeBox = By.className(dataSourceType.toUpperCase() + 
"-box");
-        WebElement dataSourceTypeButton = 
WebDriverWaitFactory.createWebDriverWait(driver)
-                
.until(ExpectedConditions.elementToBeClickable(dataSourceTypeBox));
-        dataSourceTypeButton.click();
+        String typeLabel = dataSourceType.toUpperCase(Locale.ROOT);
+        // Labels such as HIVE/IMPALA are class tokens, but not valid 
unescaped CSS identifiers.
+        String typeClass = typeLabel.replace("\\", "\\\\").replace("'", "\\'") 
+ "-box";
+        UiInteraction.click(driver, ".dialog-source-modal [class~='" + 
typeClass + "']");
 
         
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.textToBePresentInElementLocated(
-                By.className("dialog-create-data-source"), 
dataSourceType.toUpperCase()));
+                By.className("dialog-create-data-source"), typeLabel));
 
         createDataSourceForm().inputDataSourceName().sendKeys(dataSourceName);
         
createDataSourceForm().inputDataSourceDescription().sendKeys(dataSourceDescription);
@@ -90,7 +92,9 @@ public class DataSourcePage extends NavBarPage implements 
NavBarPage.NavBarItem
             createDataSourceForm().inputJdbcParams().sendKeys(jdbcParams);
         }
 
-        createDataSourceForm().buttonSubmit().click();
+        UiInteraction.click(driver, ".dialog-create-data-source .btn-submit");
+        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.invisibilityOfElementLocated(
+                By.cssSelector(".dialog-create-data-source")));
 
         return this;
     }
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
index 7c47eb36a1..ada4ddc3f4 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
@@ -30,6 +30,7 @@ import java.util.List;
 
 import lombok.Getter;
 import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
 
 import org.openqa.selenium.By;
 import org.openqa.selenium.JavascriptExecutor;
@@ -44,6 +45,7 @@ import com.google.common.io.Resources;
 
 @SuppressWarnings("UnstableApiUsage")
 @Getter
+@Slf4j
 public final class WorkflowForm {
 
     private WebDriver driver;
@@ -67,15 +69,28 @@ public final class WorkflowForm {
     @SneakyThrows
     @SuppressWarnings("unchecked")
     public <T> T addTask(TaskType type) {
-        final WebElement task = driver.findElement(By.className("task-item-" + 
type.name()));
+        log.info("Waiting for DAG canvas and task menu: type={}, url={}", 
type, driver.getCurrentUrl());
+        WebDriverWaitFactory.createWebDriverWait(driver)
+                .withMessage("DAG graph has not mounted before adding task " + 
type)
+                
.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".dag-container
 .x6-graph-svg")));
+        // Task entries are populated by the asynchronous DAG menu request 
after navigation.
+        final WebElement task = 
WebDriverWaitFactory.createWebDriverWait(driver)
+                .withMessage("DAG task menu has not rendered a draggable entry 
for " + type)
+                .until(ExpectedConditions.visibilityOfElementLocated(
+                        By.cssSelector(".task-item-" + type.name() + 
"[draggable='true']")));
         final WebElement canvas = 
driver.findElement(By.className("dag-container"));
+        log.info("Dragging DAG task: type={}, sourceLocation={}, 
sourceSize={}, canvasLocation={}, canvasSize={}",
+                type, task.getLocation(), task.getSize(), 
canvas.getLocation(), canvas.getSize());
 
         final JavascriptExecutor js = (JavascriptExecutor) driver;
         final String dragAndDrop = String.join("\n",
                 Resources.readLines(Resources.getResource("dragAndDrop.js"), 
StandardCharsets.UTF_8));
         js.executeScript(dragAndDrop, task, canvas);
-        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions
-                .visibilityOfElementLocated(By.xpath("//*[contains(text(), 
'Current node settings')]")));
+        WebDriverWaitFactory.createWebDriverWait(driver)
+                .withMessage("Task settings did not open after dragging " + 
type)
+                .until(ExpectedConditions
+                        
.visibilityOfElementLocated(By.xpath("//*[contains(text(), 'Current node 
settings')]")));
+        log.info("DAG task settings opened: type={}", type);
 
         switch (type) {
             case SHELL:
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
index 8bfa738e2f..4602fea0e0 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
@@ -17,15 +17,12 @@
 
 package org.apache.dolphinscheduler.e2e.pages.project.workflow;
 
+import org.apache.dolphinscheduler.e2e.core.UiInteraction;
 import org.apache.dolphinscheduler.e2e.core.WebDriverWaitFactory;
 
 import lombok.Getter;
 
 import org.openqa.selenium.By;
-import org.openqa.selenium.WebElement;
-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;
 
 @Getter
@@ -33,29 +30,14 @@ public final class WorkflowRunDialog {
 
     private final WorkflowDefinitionTab parent;
 
-    @FindBys({
-            @FindBy(xpath = "//div[contains(text(), 'Please set the parameters 
before starting')]/../.."),
-            @FindBy(className = "btn-submit")
-    })
-    private WebElement buttonSubmit;
-
     public WorkflowRunDialog(WorkflowDefinitionTab parent) {
         this.parent = parent;
-
-        PageFactory.initElements(parent().driver(), this);
     }
 
     public WorkflowDefinitionTab submit() {
-        By runDialogTitleXpath =
-                By.xpath(String.format("//*[contains(text(), '%s')]", "Please 
set the parameters before starting"));
-        WebDriverWaitFactory.createWebDriverWait(parent.driver())
-                
.until(ExpectedConditions.visibilityOfElementLocated(runDialogTitleXpath));
-        WebDriverWaitFactory.createWebDriverWait(parent.driver())
-                
.until(ExpectedConditions.elementToBeClickable(buttonSubmit()));
-
-        buttonSubmit().click();
+        UiInteraction.click(parent.driver(), ".dialog-start-workflow 
.btn-submit");
         WebDriverWaitFactory.createWebDriverWait(parent.driver())
-                
.until(ExpectedConditions.invisibilityOfElementLocated(runDialogTitleXpath));
+                
.until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector(".dialog-start-workflow")));
         return parent();
     }
 }
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
index d84708f290..fd15e384e9 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
@@ -17,12 +17,10 @@
 
 package org.apache.dolphinscheduler.e2e.pages.project.workflow.task;
 
-import org.apache.dolphinscheduler.e2e.core.Constants;
 import org.apache.dolphinscheduler.e2e.pages.common.CodeEditor;
 import org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm;
 
 import lombok.Getter;
-import lombok.SneakyThrows;
 
 import org.openqa.selenium.WebDriver;
 
@@ -41,9 +39,7 @@ public final class ShellTaskForm extends TaskNodeForm {
         this.driver = parent.driver();
     }
 
-    @SneakyThrows
     public ShellTaskForm script(String script) {
-        Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS);
         codeEditor.content(script);
 
         return this;
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
index bec099385d..acd71ca770 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
@@ -17,12 +17,16 @@
 
 package org.apache.dolphinscheduler.e2e.pages.project.workflow.task;
 
+import org.apache.dolphinscheduler.e2e.core.UiInteraction;
 import org.apache.dolphinscheduler.e2e.core.WebDriverWaitFactory;
 import org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
 
 import org.openqa.selenium.By;
 import org.openqa.selenium.JavascriptExecutor;
@@ -35,6 +39,7 @@ import org.openqa.selenium.support.PageFactory;
 import org.openqa.selenium.support.ui.ExpectedConditions;
 
 @Getter
+@Slf4j
 public abstract class TaskNodeForm {
 
     @FindBys({
@@ -162,22 +167,34 @@ public abstract class TaskNodeForm {
         return this;
     }
 
-    public TaskNodeForm selectResource(String resourceName) {
-        ((JavascriptExecutor) 
parent().driver()).executeScript("arguments[0].click();", selectResource);
-
+    public TaskNodeForm selectResource(String resourceFileName) {
+        UiInteraction.click(parent.driver(), ".resource-select 
.n-base-selection");
+        WebElement filter = 
WebDriverWaitFactory.createWebDriverWait(parent.driver())
+                
.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".resource-select
 input")));
+        filter.clear();
+        filter.sendKeys(resourceFileName);
         final By optionsLocator = By.className("n-tree-node-content__text");
-
-        WebDriverWaitFactory.createWebDriverWait(parent().driver())
-                
.until(ExpectedConditions.visibilityOfElementLocated(optionsLocator));
-
-        parent().driver()
-                .findElements(optionsLocator)
-                .stream()
-                .filter(it -> it.getText().startsWith(resourceName))
-                .findFirst()
-                .orElseThrow(() -> new RuntimeException("No such resource: " + 
resourceName))
-                .click();
-
+        AtomicReference<List<String>> visibleNames = new AtomicReference<>();
+        WebElement resource = 
WebDriverWaitFactory.createWebDriverWait(parent.driver())
+                .withMessage(() -> "Resource not found by exact file name: " + 
resourceFileName
+                        + "; visible resources=" + visibleNames.get())
+                .until(ignored -> {
+                    List<WebElement> options = 
parent.driver().findElements(optionsLocator).stream()
+                            .filter(WebElement::isDisplayed)
+                            .collect(Collectors.toList());
+                    
visibleNames.set(options.stream().map(WebElement::getText).collect(Collectors.toList()));
+                    List<WebElement> matches = options.stream()
+                            .filter(it -> 
resourceFileName.equals(it.getText().trim()))
+                            .collect(Collectors.toList());
+                    if (matches.size() > 1) {
+                        throw new IllegalStateException("Ambiguous resource 
file name: " + resourceFileName
+                                + "; visible resources=" + visibleNames.get());
+                    }
+                    return matches.isEmpty() ? null : matches.get(0);
+                });
+        log.info("Selecting resource by exact file name: requested={}, 
selected={}", resourceFileName,
+                resource.getText());
+        resource.click();
         parent.driver().switchTo().activeElement().sendKeys(Keys.ESCAPE);
         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 10ba4e2cba..65f5a170f8 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
@@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.e2e.pages.resource;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
 
+import org.apache.dolphinscheduler.e2e.core.UiInteraction;
 import org.apache.dolphinscheduler.e2e.core.WebDriverWaitFactory;
 import org.apache.dolphinscheduler.e2e.pages.common.CodeEditor;
 import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
@@ -89,21 +90,23 @@ public class FileManagePage extends NavBarPage implements 
ResourcePage.Tab {
 
     public FileManagePage createDirectory(String name) {
         waitForPageLoading();
-        buttonCreateDirectory().click();
+        UiInteraction.click(driver, ".btn-create-directory");
 
         createDirectoryBox().inputDirectoryName().sendKeys(name);
-        WebDriverWaitFactory.createWebDriverWait(driver)
-                
.until(ExpectedConditions.elementToBeClickable(createDirectoryBox().buttonSubmit()));
-        createDirectoryBox().buttonSubmit().click();
+        UiInteraction.click(driver, ".dialog-create-directory .btn-submit");
+        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.invisibilityOfElementLocated(
+                By.cssSelector(".dialog-create-directory")));
         return this;
     }
 
     public FileManagePage cancelCreateDirectory(String name) {
         waitForPageLoading();
-        buttonCreateDirectory().click();
+        UiInteraction.click(driver, ".btn-create-directory");
 
         createDirectoryBox().inputDirectoryName().sendKeys(name);
-        createDirectoryBox().buttonCancel().click();
+        UiInteraction.click(driver, ".dialog-create-directory .btn-cancel");
+        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.invisibilityOfElementLocated(
+                By.cssSelector(".dialog-create-directory")));
 
         return this;
     }
@@ -136,12 +139,12 @@ public class FileManagePage extends NavBarPage implements 
ResourcePage.Tab {
                 .orElseThrow(() -> new RuntimeException(String.format("No %s 
in file manage list", directoryName)))
                 .click();
 
-        buttonCreateDirectory().click();
+        UiInteraction.click(driver, ".btn-create-directory");
 
         createDirectoryBox().inputDirectoryName().sendKeys(subDirectoryName);
-        WebDriverWaitFactory.createWebDriverWait(driver)
-                
.until(ExpectedConditions.elementToBeClickable(createDirectoryBox().buttonSubmit()));
-        createDirectoryBox().buttonSubmit().click();
+        UiInteraction.click(driver, ".dialog-create-directory .btn-submit");
+        
WebDriverWaitFactory.createWebDriverWait(driver).until(ExpectedConditions.invisibilityOfElementLocated(
+                By.cssSelector(".dialog-create-directory")));
 
         return this;
     }
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
index 351363154e..5dc3ba6a20 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
@@ -25,13 +25,16 @@ import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.Duration;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.logging.Level;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -41,8 +44,14 @@ import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.TestWatcher;
+import org.openqa.selenium.By;
+import org.openqa.selenium.OutputType;
 import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
 import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.logging.LogType;
+import org.openqa.selenium.logging.LoggingPreferences;
 import org.openqa.selenium.remote.RemoteWebDriver;
 import org.testcontainers.Testcontainers;
 import org.testcontainers.containers.BindMode;
@@ -56,7 +65,7 @@ import com.google.common.base.Strings;
 import com.google.common.net.HostAndPort;
 
 @Slf4j
-final class DolphinSchedulerExtension implements BeforeAllCallback, 
AfterAllCallback, BeforeEachCallback {
+final class DolphinSchedulerExtension implements BeforeAllCallback, 
AfterAllCallback, BeforeEachCallback, TestWatcher {
 
     private final boolean LOCAL_MODE = 
Objects.equals(System.getProperty("local"), "true");
 
@@ -73,6 +82,7 @@ final class DolphinSchedulerExtension implements 
BeforeAllCallback, AfterAllCall
     private String rootPath;
 
     private Path record;
+    private Throwable firstFailure;
 
     private final String serviceName = "dolphinscheduler";
 
@@ -98,11 +108,18 @@ final class DolphinSchedulerExtension implements 
BeforeAllCallback, AfterAllCall
         }
         browser.start();
         ChromeOptions chromeOptions = new ChromeOptions();
+        chromeOptions.setExperimentalOption("prefs", 
Map.of("profile.default_content_setting_values.clipboard", 1));
+        LoggingPreferences logging = new LoggingPreferences();
+        logging.enable(LogType.BROWSER, Level.ALL);
+        chromeOptions.setCapability("goog:loggingPrefs", logging);
         chromeOptions.addArguments("--allow-running-insecure-content");
         
chromeOptions.addArguments(String.format("--unsafely-treat-insecure-origin-as-secure=http://%s:%s";,
                 address.getHost(), address.getPort()));
         driver = new RemoteWebDriver(browser.getSeleniumAddress(), 
chromeOptions);
 
+        log.info("E2E environment: local={}, m1={}, address={}, browser={}, 
recording={}",
+                LOCAL_MODE, M1_CHIP_FLAG, address, driver.getCapabilities(), 
record);
+
         driver.manage().timeouts()
                 .implicitlyWait(Duration.ofSeconds(1))
                 .pageLoadTimeout(Duration.ofSeconds(5));
@@ -185,7 +202,7 @@ final class DolphinSchedulerExtension implements 
BeforeAllCallback, AfterAllCall
 
     @Override
     public void afterAll(ExtensionContext context) {
-        browser.afterTest(new TestDescription(context), Optional.empty());
+        browser.afterTest(new TestDescription(context), 
Optional.ofNullable(firstFailure));
         browser.stop();
         if (compose != null) {
             compose.stop();
@@ -194,12 +211,60 @@ final class DolphinSchedulerExtension implements 
BeforeAllCallback, AfterAllCall
 
     @Override
     public void beforeEach(ExtensionContext context) {
+        log.info("Starting E2E test: {}, url={}", context.getUniqueId(), 
driver.getCurrentUrl());
         final Object instance = context.getRequiredTestInstance();
         Stream.of(instance.getClass().getDeclaredFields())
                 .filter(f -> WebDriver.class.isAssignableFrom(f.getType()))
                 .forEach(it -> setDriver(instance, it));
     }
 
+    @Override
+    public void testFailed(ExtensionContext context, Throwable cause) {
+        if (firstFailure == null) {
+            firstFailure = cause;
+        }
+        log.error("E2E test failed: {}", context.getUniqueId(), cause);
+        String name = context.getRequiredTestClass().getSimpleName() + "-"
+                + context.getRequiredTestMethod().getName();
+        try {
+            Files.write(record.resolve(name + ".png"), 
driver.getScreenshotAs(OutputType.BYTES));
+            Files.write(record.resolve(name + ".html"), 
driver.getPageSource().getBytes(StandardCharsets.UTF_8));
+            log.error("E2E failure screenshot and DOM saved: {}", 
record.resolve(name));
+            WebElement focused = driver.switchTo().activeElement();
+            log.error("E2E browser state: url={}, window={}, focusTag={}, 
focusClass={}",
+                    driver.getCurrentUrl(), driver.manage().window().getSize(),
+                    focused.getTagName(), focused.getDomAttribute("class"));
+            for (WebElement modal : 
driver.findElements(By.cssSelector("[data-modal-ready]"))) {
+                if (modal.isDisplayed()) {
+                    log.error("E2E visible modal: class={}, ready={}, 
location={}, size={}",
+                            modal.getDomAttribute("class"), 
modal.getDomAttribute("data-modal-ready"),
+                            modal.getLocation(), modal.getSize());
+                }
+            }
+            for (WebElement feedback : 
driver.findElements(By.cssSelector(".n-form-item-feedback-wrapper"))) {
+                if (feedback.isDisplayed() && !feedback.getText().isEmpty()) {
+                    log.error("E2E form feedback: {}", feedback.getText());
+                }
+            }
+            for (WebElement editor : 
driver.findElements(By.cssSelector("[data-editor-model-version]"))) {
+                log.error("E2E editor: version={}, characters={}",
+                        editor.getDomAttribute("data-editor-model-version"),
+                        editor.getDomAttribute("data-editor-content-length"));
+            }
+        } catch (Exception diagnosticFailure) {
+            cause.addSuppressed(diagnosticFailure);
+            log.error("Failed to capture E2E browser state for {}", 
context.getUniqueId(), diagnosticFailure);
+        }
+        try {
+            String browserLogs = 
driver.manage().logs().get(LogType.BROWSER).getAll().toString();
+            log.error("E2E browser console: {}", browserLogs);
+            Files.write(record.resolve(name + "-browser.log"), 
browserLogs.getBytes(StandardCharsets.UTF_8));
+        } catch (Exception diagnosticFailure) {
+            cause.addSuppressed(diagnosticFailure);
+            log.error("Failed to capture E2E browser console for {}", 
context.getUniqueId(), diagnosticFailure);
+        }
+    }
+
     private void setDriver(Object object, Field field) {
         try {
             field.setAccessible(true);
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/UiInteraction.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/UiInteraction.java
new file mode 100644
index 0000000000..9b1175f023
--- /dev/null
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/UiInteraction.java
@@ -0,0 +1,63 @@
+/*
+ * 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.e2e.core;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+@Slf4j
+public final class UiInteraction {
+
+    private UiInteraction() {
+    }
+
+    /**
+     * Wait for the modal's enter transition before performing exactly one 
native click.
+     */
+    public static void click(WebDriver driver, String selector) {
+        AtomicReference<String> lastState = new AtomicReference<>();
+        WebElement element = WebDriverWaitFactory.createWebDriverWait(driver)
+                .withMessage(() -> "UI target not ready: " + selector + "; 
state=" + lastState.get())
+                .until(ignored -> {
+                    List<WebElement> targets = 
driver.findElements(By.cssSelector(selector)).stream()
+                            .filter(WebElement::isDisplayed)
+                            .collect(Collectors.toList());
+                    if (targets.size() != 1) {
+                        lastState.set("visible matches=" + targets.size());
+                        return null;
+                    }
+                    WebElement target = targets.get(0);
+                    List<WebElement> modals =
+                            
target.findElements(By.xpath("ancestor-or-self::*[@data-modal-ready][1]"));
+                    String modalReady =
+                            modals.isEmpty() ? "outside modal" : 
modals.get(0).getDomAttribute("data-modal-ready");
+                    lastState.set("enabled=" + target.isEnabled() + ", 
modalReady=" + modalReady);
+                    return target.isEnabled() && (modals.isEmpty() || 
"true".equals(modalReady)) ? target : null;
+                });
+        log.info("Clicking UI target: selector={}, url={}, location={}, 
size={}, state={}",
+                selector, driver.getCurrentUrl(), element.getLocation(), 
element.getSize(), lastState.get());
+        element.click();
+    }
+}
diff --git 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/WebDriverWaitFactory.java
 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/WebDriverWaitFactory.java
index 2d9f47eb23..299637b5c8 100644
--- 
a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/WebDriverWaitFactory.java
+++ 
b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/WebDriverWaitFactory.java
@@ -29,7 +29,7 @@ public class WebDriverWaitFactory {
     private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(10);
 
     /**
-     * Create a WebDriverWait instance with default timeout 60s and interval 
100ms.
+     * Create a WebDriverWait instance with default timeout 10s and interval 
500ms.
      */
     public static WebDriverWait createWebDriverWait(WebDriver driver) {
         return createWebDriverWait(driver, DEFAULT_TIMEOUT);
diff --git a/dolphinscheduler-ui/src/components/modal/index.tsx 
b/dolphinscheduler-ui/src/components/modal/index.tsx
index db39caba20..73be5fc695 100644
--- a/dolphinscheduler-ui/src/components/modal/index.tsx
+++ b/dolphinscheduler-ui/src/components/modal/index.tsx
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType, renderSlot, Ref } from 'vue'
+import { defineComponent, PropType, renderSlot, Ref, ref, watch } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { NModal, NCard, NButton, NSpace } from 'naive-ui'
 import ButtonLink from '@/components/button-link'
@@ -77,6 +77,18 @@ const Modal = defineComponent({
   emits: ['cancel', 'confirm', 'jumpLink', 'maskClick'],
   setup(props, ctx) {
     const { t } = useI18n()
+    // NModal does not animate an initially visible modal on its first mount.
+    const entered = ref(props.show)
+    watch(
+      () => props.show,
+      () => {
+        entered.value = false
+      },
+      { flush: 'sync' }
+    )
+    const onAfterEnter = () => {
+      entered.value = props.show
+    }
 
     const onCancel = () => {
       ctx.emit('cancel')
@@ -90,7 +102,7 @@ const Modal = defineComponent({
       ctx.emit('maskClick')
     }
 
-    return { t, onCancel, onConfirm, onMaskClick }
+    return { t, entered, onAfterEnter, onCancel, onConfirm, onMaskClick }
   },
   render() {
     const {
@@ -110,10 +122,12 @@ const Modal = defineComponent({
         mask-closable={false}
         auto-focus={this.autoFocus}
         onMaskClick={onMaskClick}
+        onAfterEnter={this.onAfterEnter}
       >
         <NCard
           title={this.title}
           class={styles['modal-card']}
+          data-modal-ready={this.entered}
           contentStyle={{ overflowY: 'auto' }}
         >
           {{
diff --git a/dolphinscheduler-ui/src/components/monaco-editor/index.tsx 
b/dolphinscheduler-ui/src/components/monaco-editor/index.tsx
index b565d9ce8b..eb821f5b9a 100644
--- a/dolphinscheduler-ui/src/components/monaco-editor/index.tsx
+++ b/dolphinscheduler-ui/src/components/monaco-editor/index.tsx
@@ -90,6 +90,9 @@ export default defineComponent({
     const themeStore = useThemeStore()
     const monacoEditorThemeRef = ref(themeStore.darkTheme ? 'vs-dark' : 'vs')
     const editorRef = ref()
+    // Expose model progress without depending on Monaco's virtualized line 
DOM.
+    const contentLength = ref(0)
+    const modelVersion = ref(0)
     const getValue = () => editor?.getValue()
     const formItem = useFormItem({})
 
@@ -106,7 +109,12 @@ export default defineComponent({
             alwaysConsumeMouseWheel: false
           }
         })
+        const model = editor.getModel()!
+        contentLength.value = model.getValue().length
+        modelVersion.value = model.getVersionId()
         editor.onDidChangeModelContent(() => {
+          contentLength.value = model.getValue().length
+          modelVersion.value = model.getVersionId()
           const { onUpdateValue, 'onUpdate:value': _onUpdateValue } = props
           const value = editor?.getValue() || ''
 
@@ -161,12 +169,14 @@ export default defineComponent({
 
     ctx.expose({ getValue })
 
-    return { editorRef }
+    return { editorRef, contentLength, modelVersion }
   },
   render() {
     return (
       <div
         ref='editorRef'
+        data-editor-content-length={this.contentLength}
+        data-editor-model-version={this.modelVersion}
         style={{
           height: this.height,
           minHeight: '150px',
diff --git 
a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx
 
b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx
index eea89ad0d4..b5254055b6 100644
--- 
a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx
+++ 
b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx
@@ -319,6 +319,7 @@ export default defineComponent({
     const { t } = this
     return (
       <Modal
+        class='dialog-start-workflow'
         show={this.show}
         title={t('project.workflow.set_parameters_before_starting')}
         onCancel={this.hideModal}
diff --git 
a/dolphinscheduler-ui/src/views/resource/components/resource/folder/index.tsx 
b/dolphinscheduler-ui/src/views/resource/components/resource/folder/index.tsx
index 6367971cd5..c095dae9d0 100644
--- 
a/dolphinscheduler-ui/src/views/resource/components/resource/folder/index.tsx
+++ 
b/dolphinscheduler-ui/src/views/resource/components/resource/folder/index.tsx
@@ -65,6 +65,7 @@ export default defineComponent({
     const { t } = useI18n()
     return (
       <Modal
+        class='dialog-create-directory'
         show={this.$props.show}
         title={t('resource.file.create_folder')}
         onCancel={this.hideModal}

Reply via email to