Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
apache
DolphinScheduler
提交
37ba1eb5
DolphinScheduler
项目概览
apache
/
DolphinScheduler
上一次同步 1 年多
通知
706
Star
9572
Fork
3514
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
DolphinScheduler
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
37ba1eb5
编写于
11月 30, 2021
作者:
K
kezhenxu94
提交者:
GitHub
11月 30, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add E2E tests for some core features (#7025)
上级
7b20f4c2
变更
43
隐藏空白更改
内联
并排
Showing
43 changed file
with
1861 addition
and
388 deletion
+1861
-388
.github/workflows/e2e.yml
.github/workflows/e2e.yml
+9
-7
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/ProjectE2ETest.java
...org/apache/dolphinscheduler/e2e/cases/ProjectE2ETest.java
+68
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java
.../org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java
+88
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowE2ETest.java
...rg/apache/dolphinscheduler/e2e/cases/WorkflowE2ETest.java
+200
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/LoginPage.java
...java/org/apache/dolphinscheduler/e2e/pages/LoginPage.java
+19
-6
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
.../apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
+45
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/NavBarPage.java
.../apache/dolphinscheduler/e2e/pages/common/NavBarPage.java
+62
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/ProjectDetailPage.java
...dolphinscheduler/e2e/pages/project/ProjectDetailPage.java
+58
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/ProjectPage.java
...pache/dolphinscheduler/e2e/pages/project/ProjectPage.java
+114
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowDefinitionTab.java
...ler/e2e/pages/project/workflow/WorkflowDefinitionTab.java
+123
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
...hinscheduler/e2e/pages/project/workflow/WorkflowForm.java
+85
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowInstanceTab.java
...duler/e2e/pages/project/workflow/WorkflowInstanceTab.java
+107
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
...heduler/e2e/pages/project/workflow/WorkflowRunDialog.java
+46
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowSaveDialog.java
...eduler/e2e/pages/project/workflow/WorkflowSaveDialog.java
+115
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
...eduler/e2e/pages/project/workflow/task/ShellTaskForm.java
+42
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/SubWorkflowTaskForm.java
.../e2e/pages/project/workflow/task/SubWorkflowTaskForm.java
+31
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
...heduler/e2e/pages/project/workflow/task/TaskNodeForm.java
+93
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java
...che/dolphinscheduler/e2e/pages/security/SecurityPage.java
+51
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TenantPage.java
...pache/dolphinscheduler/e2e/pages/security/TenantPage.java
+44
-11
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/basic/docker-compose.yaml
...-case/src/test/resources/docker/basic/docker-compose.yaml
+1
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/dragAndDrop.js
...lphinscheduler-e2e-case/src/test/resources/dragAndDrop.js
+55
-0
dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
.../dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
+29
-16
dolphinscheduler-e2e/pom.xml
dolphinscheduler-e2e/pom.xml
+13
-5
dolphinscheduler-ui/pom.xml
dolphinscheduler-ui/pom.xml
+1
-1
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue
...-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue
+2
-1
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue
...-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue
+1
-0
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/config.js
...scheduler-ui/src/js/conf/home/pages/dag/_source/config.js
+12
-6
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
...inscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
+1
-0
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
...rc/js/conf/home/pages/dag/_source/formModel/formModel.vue
+2
-0
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/_source/localParams.vue
...pages/dag/_source/formModel/tasks/_source/localParams.vue
+2
-0
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
.../conf/home/pages/dag/_source/udp/_source/selectTenant.vue
+2
-0
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
...heduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
+2
-1
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
...ges/projects/pages/definition/pages/list/_source/list.vue
+7
-7
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
...es/projects/pages/definition/pages/list/_source/start.vue
+1
-1
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue
...home/pages/projects/pages/definition/pages/list/index.vue
+1
-1
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
...pages/projects/pages/instance/pages/list/_source/list.vue
+6
-6
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/_source/createProject.vue
.../home/pages/projects/pages/list/_source/createProject.vue
+2
-1
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/_source/list.vue
...c/js/conf/home/pages/projects/pages/list/_source/list.vue
+3
-3
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/index.vue
...r-ui/src/js/conf/home/pages/projects/pages/list/index.vue
+1
-1
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/index.vue
...s/conf/home/pages/projects/pages/taskDefinition/index.vue
+307
-307
dolphinscheduler-ui/src/js/module/components/nav/nav.vue
dolphinscheduler-ui/src/js/module/components/nav/nav.vue
+2
-2
dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
...ui/src/js/module/components/secondaryMenu/_source/menu.js
+6
-3
dolphinscheduler-ui/src/js/module/components/secondaryMenu/secondaryMenu.vue
.../src/js/module/components/secondaryMenu/secondaryMenu.vue
+2
-2
未找到文件。
.github/workflows/e2e.yml
浏览文件 @
37ba1eb5
...
...
@@ -27,10 +27,6 @@ on:
branches
:
-
dev
env
:
TAG
:
ci
RECORDING_PATH
:
/tmp/recording
name
:
E2E
concurrency
:
...
...
@@ -45,7 +41,13 @@ jobs:
matrix
:
case
:
-
name
:
Tenant
class
:
org.apache.dolphinscheduler.e2e.cases.security.TenantE2ETest
class
:
org.apache.dolphinscheduler.e2e.cases.TenantE2ETest
-
name
:
Project
class
:
org.apache.dolphinscheduler.e2e.cases.ProjectE2ETest
-
name
:
Workflow
class
:
org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest
env
:
RECORDING_PATH
:
/tmp/recording-${{ matrix.case.name }}
steps
:
-
uses
:
actions/checkout@v2
with
:
...
...
@@ -62,13 +64,13 @@ jobs:
run
:
TAG=ci sh ./docker/build/hooks/build
-
name
:
Run Test
run
:
|
./mvnw -f dolphinscheduler-e2e/pom.xml -am \
./mvnw -
B -
f dolphinscheduler-e2e/pom.xml -am \
-DfailIfNoTests=false \
-Dtest=${{ matrix.case.class }} test
-
uses
:
actions/upload-artifact@v2
if
:
always()
name
:
Upload Recording
with
:
name
:
recording
name
:
recording
-${{ matrix.case.name }}
path
:
${{ env.RECORDING_PATH }}
retention-days
:
1
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/ProjectE2ETest.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* Licensed to 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. Apache Software Foundation (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.cases
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
awaitility
.
Awaitility
.
await
;
import
org.apache.dolphinscheduler.e2e.core.DolphinScheduler
;
import
org.apache.dolphinscheduler.e2e.pages.LoginPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.ProjectPage
;
import
org.junit.jupiter.api.BeforeAll
;
import
org.junit.jupiter.api.Order
;
import
org.junit.jupiter.api.Test
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
@DolphinScheduler
(
composeFiles
=
"docker/basic/docker-compose.yaml"
)
class
ProjectE2ETest
{
private
static
final
String
project
=
"test-project-1"
;
private
static
RemoteWebDriver
browser
;
@BeforeAll
public
static
void
setup
()
{
new
LoginPage
(
browser
)
.
login
(
"admin"
,
"dolphinscheduler123"
)
.
goToNav
(
ProjectPage
.
class
);
}
@Test
@Order
(
1
)
void
testCreateProject
()
{
new
ProjectPage
(
browser
).
create
(
project
);
}
@Test
@Order
(
30
)
void
testDeleteProject
()
{
final
var
page
=
new
ProjectPage
(
browser
);
page
.
delete
(
project
);
await
().
untilAsserted
(()
->
{
browser
.
navigate
().
refresh
();
assertThat
(
page
.
projectList
()
).
noneMatch
(
it
->
it
.
getText
().
contains
(
project
)
);
});
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/
security/
TenantE2ETest.java
→
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java
浏览文件 @
37ba1eb5
...
...
@@ -17,7 +17,7 @@
* under the License.
*/
package
org.apache.dolphinscheduler.e2e.cases
.security
;
package
org.apache.dolphinscheduler.e2e.cases
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
...
...
@@ -25,58 +25,47 @@ import static org.awaitility.Awaitility.await;
import
org.apache.dolphinscheduler.e2e.core.DolphinScheduler
;
import
org.apache.dolphinscheduler.e2e.pages.LoginPage
;
import
org.apache.dolphinscheduler.e2e.pages.TenantPage
;
import
org.apache.dolphinscheduler.e2e.pages.security.SecurityPage
;
import
org.apache.dolphinscheduler.e2e.pages.security.TenantPage
;
import
org.junit.jupiter.api.BeforeAll
;
import
org.junit.jupiter.api.Order
;
import
org.junit.jupiter.api.Test
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
@DolphinScheduler
(
composeFiles
=
"docker/
tenant
/docker-compose.yaml"
)
@DolphinScheduler
(
composeFiles
=
"docker/
basic
/docker-compose.yaml"
)
class
TenantE2ETest
{
private
RemoteWebDriver
browser
;
private
static
final
String
tenant
=
System
.
getProperty
(
"user.name"
)
;
@Test
@Order
(
1
)
void
testLogin
()
{
final
LoginPage
page
=
new
LoginPage
(
browser
);
page
.
inputUsername
().
sendKeys
(
"admin"
);
page
.
inputPassword
().
sendKeys
(
"dolphinscheduler123"
);
page
.
buttonLogin
().
click
();
private
static
RemoteWebDriver
browser
;
@BeforeAll
public
static
void
setup
()
{
new
LoginPage
(
browser
)
.
login
(
"admin"
,
"dolphinscheduler123"
)
.
goToNav
(
SecurityPage
.
class
)
.
goToTab
(
TenantPage
.
class
)
;
}
@Test
@Order
(
10
)
void
testCreateTenant
()
{
final
TenantPage
page
=
new
TenantPage
(
browser
);
final
String
tenant
=
System
.
getProperty
(
"user.name"
);
page
.
buttonCreateTenant
().
click
();
page
.
createTenantForm
().
inputTenantCode
().
sendKeys
(
tenant
);
page
.
createTenantForm
().
inputDescription
().
sendKeys
(
"Test"
);
page
.
createTenantForm
().
buttonSubmit
().
click
();
await
().
untilAsserted
(()
->
assertThat
(
page
.
tenantList
())
.
as
(
"Tenant list should contain newly-created tenant"
)
.
extracting
(
WebElement:
:
getText
)
.
anyMatch
(
it
->
it
.
contains
(
tenant
)));
new
TenantPage
(
browser
).
create
(
tenant
);
}
@Test
@Order
(
20
)
void
testCreateDuplicateTenant
()
{
final
String
tenant
=
System
.
getProperty
(
"user.name"
);
final
TenantPage
page
=
new
TenantPage
(
browser
);
page
.
buttonCreateTenant
().
click
();
page
.
createTenantForm
().
inputTenantCode
().
sendKeys
(
tenant
);
page
.
createTenantForm
().
inputDescription
().
sendKeys
(
"Test"
);
page
.
createTenantForm
().
buttonSubmit
().
click
();
final
var
page
=
new
TenantPage
(
browser
);
await
().
untilAsserted
(()
->
assertThat
(
browser
.
findElementByTagName
(
"body"
)
.
getText
().
contains
(
"already exists"
))
.
as
(
"Should fail when creating a duplicate tenant"
)
.
isTrue
());
page
.
create
(
tenant
);
await
().
untilAsserted
(()
->
assertThat
(
browser
.
findElement
(
By
.
tagName
(
"body"
)).
getText
())
.
contains
(
"already exists"
)
);
page
.
createTenantForm
().
buttonCancel
().
click
();
}
...
...
@@ -84,15 +73,16 @@ class TenantE2ETest {
@Test
@Order
(
30
)
void
testDeleteTenant
()
{
final
String
tenant
=
System
.
getProperty
(
"user.name"
);
final
TenantPage
page
=
new
TenantPage
(
browser
);
page
.
tenantList
()
.
stream
()
.
filter
(
it
->
it
.
getText
().
contains
(
tenant
))
.
findFirst
()
.
ifPresent
(
it
->
it
.
findElement
(
By
.
className
(
"delete"
)).
click
());
page
.
buttonConfirm
().
click
();
final
var
page
=
new
TenantPage
(
browser
);
page
.
delete
(
tenant
);
await
().
untilAsserted
(()
->
{
browser
.
navigate
().
refresh
();
assertThat
(
page
.
tenantList
()
).
noneMatch
(
it
->
it
.
getText
().
contains
(
tenant
)
);
});
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowE2ETest.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* Licensed to 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. Apache Software Foundation (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.cases
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
awaitility
.
Awaitility
.
await
;
import
org.apache.dolphinscheduler.e2e.core.DolphinScheduler
;
import
org.apache.dolphinscheduler.e2e.pages.LoginPage
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.ProjectPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowDefinitionTab
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm.TaskType
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowInstanceTab
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowInstanceTab.Row
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.task.ShellTaskForm
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.task.SubWorkflowTaskForm
;
import
org.apache.dolphinscheduler.e2e.pages.security.SecurityPage
;
import
org.apache.dolphinscheduler.e2e.pages.security.TenantPage
;
import
org.junit.jupiter.api.AfterAll
;
import
org.junit.jupiter.api.BeforeAll
;
import
org.junit.jupiter.api.Order
;
import
org.junit.jupiter.api.Test
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
@DolphinScheduler
(
composeFiles
=
"docker/basic/docker-compose.yaml"
)
class
WorkflowE2ETest
{
private
static
final
String
project
=
"test-workflow-1"
;
private
static
final
String
tenant
=
System
.
getProperty
(
"user.name"
);
private
static
RemoteWebDriver
browser
;
@BeforeAll
public
static
void
setup
()
{
new
LoginPage
(
browser
)
.
login
(
"admin"
,
"dolphinscheduler123"
)
.
goToNav
(
SecurityPage
.
class
)
.
goToTab
(
TenantPage
.
class
)
.
create
(
tenant
)
.
goToNav
(
ProjectPage
.
class
)
.
create
(
project
)
;
}
@AfterAll
public
static
void
cleanup
()
{
new
NavBarPage
(
browser
)
.
goToNav
(
ProjectPage
.
class
)
.
goTo
(
project
)
.
goToTab
(
WorkflowDefinitionTab
.
class
)
.
cancelPublishAll
()
.
deleteAll
()
;
new
NavBarPage
(
browser
)
.
goToNav
(
ProjectPage
.
class
)
.
delete
(
project
)
.
goToNav
(
SecurityPage
.
class
)
.
goToTab
(
TenantPage
.
class
)
.
delete
(
tenant
)
;
}
@Test
@Order
(
1
)
void
testCreateWorkflow
()
{
final
var
workflow
=
"test-workflow-1"
;
final
var
workflowDefinitionPage
=
new
ProjectPage
(
browser
)
.
goTo
(
project
)
.
goToTab
(
WorkflowDefinitionTab
.
class
);
workflowDefinitionPage
.
createWorkflow
()
.<
ShellTaskForm
>
addTask
(
TaskType
.
SHELL
)
.
script
(
"echo ${today}\necho ${global_param}\n"
)
.
name
(
"test-1"
)
.
addParam
(
"today"
,
"${system.datetime}"
)
.
submit
()
.
submit
()
.
name
(
workflow
)
.
tenant
(
tenant
)
.
addGlobalParam
(
"global_param"
,
"hello world"
)
.
submit
()
;
await
().
untilAsserted
(()
->
assertThat
(
workflowDefinitionPage
.
workflowList
()
).
anyMatch
(
it
->
it
.
getText
().
contains
(
workflow
)));
workflowDefinitionPage
.
publish
(
workflow
);
}
@Test
@Order
(
10
)
void
testCreateSubWorkflow
()
{
final
var
workflow
=
"test-sub-workflow-1"
;
final
var
workflowDefinitionPage
=
new
ProjectPage
(
browser
)
.
goToNav
(
ProjectPage
.
class
)
.
goTo
(
project
)
.
goToTab
(
WorkflowDefinitionTab
.
class
);
workflowDefinitionPage
.
createWorkflow
()
.<
SubWorkflowTaskForm
>
addTask
(
TaskType
.
SUB_PROCESS
)
.
submit
()
.
submit
()
.
name
(
workflow
)
.
tenant
(
tenant
)
.
addGlobalParam
(
"global_param"
,
"hello world"
)
.
submit
()
;
await
().
untilAsserted
(()
->
assertThat
(
workflowDefinitionPage
.
workflowList
()
).
anyMatch
(
it
->
it
.
getText
().
contains
(
workflow
)));
workflowDefinitionPage
.
publish
(
workflow
);
}
@Test
@Order
(
30
)
void
testRunWorkflow
()
{
final
var
workflow
=
"test-workflow-1"
;
final
var
projectPage
=
new
ProjectPage
(
browser
)
.
goToNav
(
ProjectPage
.
class
)
.
goTo
(
project
);
projectPage
.
goToTab
(
WorkflowInstanceTab
.
class
)
.
deleteAll
();
projectPage
.
goToTab
(
WorkflowDefinitionTab
.
class
)
.
run
(
workflow
)
.
submit
();
await
().
untilAsserted
(()
->
{
browser
.
navigate
().
refresh
();
final
Row
row
=
projectPage
.
goToTab
(
WorkflowInstanceTab
.
class
)
.
instances
()
.
iterator
()
.
next
();
assertThat
(
row
.
isSuccess
()).
isTrue
();
assertThat
(
row
.
executionTime
()).
isEqualTo
(
1
);
});
// Test rerun
projectPage
.
goToTab
(
WorkflowInstanceTab
.
class
)
.
instances
()
.
stream
()
.
filter
(
it
->
it
.
rerunButton
().
isDisplayed
())
.
iterator
()
.
next
()
.
rerun
();
await
().
untilAsserted
(()
->
{
browser
.
navigate
().
refresh
();
final
Row
row
=
projectPage
.
goToTab
(
WorkflowInstanceTab
.
class
)
.
instances
()
.
iterator
()
.
next
();
assertThat
(
row
.
isSuccess
()).
isTrue
();
assertThat
(
row
.
executionTime
()).
isEqualTo
(
2
);
});
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/LoginPage.java
浏览文件 @
37ba1eb5
...
...
@@ -19,17 +19,20 @@
package
org.apache.dolphinscheduler.e2e.pages
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.security.TenantPage
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.PageFactory
;
import
org.openqa.selenium.support.ui.ExpectedConditions
;
import
org.openqa.selenium.support.ui.WebDriverWait
;
import
lombok.Getter
;
import
lombok.SneakyThrows
;
@Getter
public
final
class
LoginPage
{
private
final
RemoteWebDriver
driver
;
public
final
class
LoginPage
extends
NavBarPage
{
@FindBy
(
id
=
"input-username"
)
private
WebElement
inputUsername
;
...
...
@@ -40,8 +43,18 @@ public final class LoginPage {
private
WebElement
buttonLogin
;
public
LoginPage
(
RemoteWebDriver
driver
)
{
this
.
driver
=
driver
;
super
(
driver
);
}
@SneakyThrows
public
TenantPage
login
(
String
username
,
String
password
)
{
inputUsername
().
sendKeys
(
username
);
inputPassword
().
sendKeys
(
password
);
buttonLogin
().
click
();
new
WebDriverWait
(
driver
(),
10
)
.
until
(
ExpectedConditions
.
urlContains
(
"/#/security"
));
PageFactory
.
initElements
(
driver
,
this
);
return
new
TenantPage
(
driver
);
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/CodeEditor.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.common
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebDriver
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.PageFactory
;
import
lombok.Getter
;
@Getter
public
final
class
CodeEditor
{
@FindBy
(
className
=
"CodeMirror"
)
private
WebElement
editor
;
public
CodeEditor
(
WebDriver
driver
)
{
PageFactory
.
initElements
(
driver
,
this
);
}
public
CodeEditor
content
(
String
content
)
{
editor
.
findElement
(
By
.
className
(
"CodeMirror-line"
)).
click
();
editor
.
findElement
(
By
.
tagName
(
"textarea"
)).
sendKeys
(
content
);
return
this
;
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/common/NavBarPage.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.common
;
import
org.apache.dolphinscheduler.e2e.pages.project.ProjectPage
;
import
org.apache.dolphinscheduler.e2e.pages.security.SecurityPage
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.PageFactory
;
import
lombok.Getter
;
@Getter
public
class
NavBarPage
{
protected
final
RemoteWebDriver
driver
;
@FindBy
(
id
=
"project-tab"
)
private
WebElement
projectTab
;
@FindBy
(
id
=
"security-tab"
)
private
WebElement
securityTab
;
public
NavBarPage
(
RemoteWebDriver
driver
)
{
this
.
driver
=
driver
;
PageFactory
.
initElements
(
driver
,
this
);
}
public
<
T
extends
NavBarItem
>
T
goToNav
(
Class
<
T
>
nav
)
{
if
(
nav
==
ProjectPage
.
class
)
{
projectTab
().
click
();
return
nav
.
cast
(
new
ProjectPage
(
driver
));
}
if
(
nav
==
SecurityPage
.
class
)
{
securityTab
().
click
();
return
nav
.
cast
(
new
SecurityPage
(
driver
));
}
throw
new
UnsupportedOperationException
(
"Unknown nav bar"
);
}
public
interface
NavBarItem
{
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/ProjectDetailPage.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowDefinitionTab
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowInstanceTab
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
lombok.Getter
;
@Getter
public
final
class
ProjectDetailPage
extends
NavBarPage
{
@FindBy
(
className
=
"process-definition"
)
private
WebElement
menuProcessDefinition
;
@FindBy
(
className
=
"process-instance"
)
private
WebElement
menuProcessInstances
;
public
ProjectDetailPage
(
RemoteWebDriver
driver
)
{
super
(
driver
);
}
public
<
T
extends
Tab
>
T
goToTab
(
Class
<
T
>
tab
)
{
if
(
tab
==
WorkflowDefinitionTab
.
class
)
{
menuProcessDefinition
().
click
();
return
tab
.
cast
(
new
WorkflowDefinitionTab
(
driver
));
}
if
(
tab
==
WorkflowInstanceTab
.
class
)
{
menuProcessInstances
().
click
();
return
tab
.
cast
(
new
WorkflowInstanceTab
(
driver
));
}
throw
new
UnsupportedOperationException
(
"Unknown tab: "
+
tab
.
getName
());
}
public
interface
Tab
{
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/ProjectPage.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage.NavBarItem
;
import
java.util.List
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.FindBys
;
import
org.openqa.selenium.support.PageFactory
;
import
org.openqa.selenium.support.ui.ExpectedConditions
;
import
org.openqa.selenium.support.ui.WebDriverWait
;
import
lombok.Getter
;
@Getter
public
final
class
ProjectPage
extends
NavBarPage
implements
NavBarItem
{
@FindBy
(
id
=
"button-create-project"
)
private
WebElement
buttonCreateProject
;
@FindBy
(
className
=
"rows-project"
)
private
List
<
WebElement
>
projectList
;
@FindBys
({
@FindBy
(
className
=
"el-popconfirm"
),
@FindBy
(
className
=
"el-button--primary"
),
})
private
List
<
WebElement
>
buttonConfirm
;
private
final
CreateProjectForm
createProjectForm
;
public
ProjectPage
(
RemoteWebDriver
driver
)
{
super
(
driver
);
this
.
createProjectForm
=
new
CreateProjectForm
();
PageFactory
.
initElements
(
driver
,
this
);
}
public
ProjectPage
create
(
String
project
)
{
buttonCreateProject
().
click
();
createProjectForm
().
inputProjectName
().
sendKeys
(
project
);
createProjectForm
().
buttonSubmit
().
click
();
new
WebDriverWait
(
driver
(),
10
)
.
until
(
ExpectedConditions
.
textToBePresentInElementLocated
(
By
.
className
(
"project-name"
),
project
));
return
this
;
}
public
ProjectPage
delete
(
String
project
)
{
projectList
()
.
stream
()
.
filter
(
it
->
it
.
getText
().
contains
(
project
))
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find project: "
+
project
))
.
findElement
(
By
.
className
(
"delete"
)).
click
();
buttonConfirm
()
.
stream
()
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"No confirm button is displayed"
))
.
click
();
return
this
;
}
public
ProjectDetailPage
goTo
(
String
project
)
{
projectList
().
stream
()
.
filter
(
it
->
it
.
getText
().
contains
(
project
))
.
map
(
it
->
it
.
findElement
(
By
.
className
(
"project-name"
)))
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot click the project item"
))
.
click
();
return
new
ProjectDetailPage
(
driver
);
}
@Getter
public
class
CreateProjectForm
{
CreateProjectForm
()
{
PageFactory
.
initElements
(
driver
,
this
);
}
@FindBy
(
id
=
"input-project-name"
)
private
WebElement
inputProjectName
;
@FindBy
(
id
=
"button-submit"
)
private
WebElement
buttonSubmit
;
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowDefinitionTab.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.ProjectDetailPage
;
import
java.util.List
;
import
java.util.function.Supplier
;
import
java.util.stream.Collectors
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.FindBys
;
import
lombok.Getter
;
@Getter
public
final
class
WorkflowDefinitionTab
extends
NavBarPage
implements
ProjectDetailPage
.
Tab
{
@FindBy
(
id
=
"button-create-process"
)
private
WebElement
buttonCreateProcess
;
@FindBy
(
className
=
"select-all"
)
private
WebElement
checkBoxSelectAll
;
@FindBy
(
className
=
"button-delete-all"
)
private
WebElement
buttonDeleteAll
;
@FindBys
({
@FindBy
(
className
=
"el-popconfirm"
),
@FindBy
(
className
=
"el-button--primary"
),
})
private
List
<
WebElement
>
buttonConfirm
;
@FindBy
(
className
=
"rows-workflow-definitions"
)
private
List
<
WebElement
>
workflowList
;
public
WorkflowDefinitionTab
(
RemoteWebDriver
driver
)
{
super
(
driver
);
}
public
WorkflowForm
createWorkflow
()
{
buttonCreateProcess
().
click
();
return
new
WorkflowForm
(
driver
);
}
public
WorkflowDefinitionTab
publish
(
String
workflow
)
{
workflowList
()
.
stream
()
.
filter
(
it
->
it
.
findElement
(
By
.
className
(
"name"
)).
getAttribute
(
"innerHTML"
).
equals
(
workflow
))
.
flatMap
(
it
->
it
.
findElements
(
By
.
className
(
"button-publish"
)).
stream
())
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find publish button in workflow definition"
))
.
click
();
return
this
;
}
public
WorkflowRunDialog
run
(
String
workflow
)
{
workflowList
()
.
stream
()
.
filter
(
it
->
it
.
findElement
(
By
.
className
(
"name"
)).
getAttribute
(
"innerHTML"
).
equals
(
workflow
))
.
flatMap
(
it
->
it
.
findElements
(
By
.
className
(
"button-run"
)).
stream
())
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find run button in workflow definition"
))
.
click
();
return
new
WorkflowRunDialog
(
this
);
}
public
WorkflowDefinitionTab
cancelPublishAll
()
{
final
Supplier
<
List
<
WebElement
>>
cancelButtons
=
()
->
workflowList
()
.
stream
()
.
flatMap
(
it
->
it
.
findElements
(
By
.
className
(
"button-cancel-publish"
)).
stream
())
.
filter
(
WebElement:
:
isDisplayed
)
.
collect
(
Collectors
.
toList
());
for
(
var
buttons
=
cancelButtons
.
get
();
!
buttons
.
isEmpty
();
buttons
=
cancelButtons
.
get
())
{
buttons
.
forEach
(
WebElement:
:
click
);
driver
().
navigate
().
refresh
();
}
return
this
;
}
public
WorkflowDefinitionTab
deleteAll
()
{
if
(
workflowList
().
isEmpty
())
{
return
this
;
}
checkBoxSelectAll
().
click
();
buttonDeleteAll
().
click
();
buttonConfirm
()
.
stream
()
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"No confirm button is displayed"
))
.
click
();
return
this
;
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowForm.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.task.ShellTaskForm
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.task.SubWorkflowTaskForm
;
import
java.nio.charset.StandardCharsets
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.JavascriptExecutor
;
import
org.openqa.selenium.WebDriver
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.PageFactory
;
import
com.google.common.io.Resources
;
import
lombok.Getter
;
import
lombok.SneakyThrows
;
@SuppressWarnings
(
"UnstableApiUsage"
)
@Getter
public
final
class
WorkflowForm
{
private
final
WebDriver
driver
;
private
final
WorkflowSaveDialog
saveForm
;
@FindBy
(
id
=
"button-save"
)
private
WebElement
buttonSave
;
public
WorkflowForm
(
WebDriver
driver
)
{
this
.
driver
=
driver
;
this
.
saveForm
=
new
WorkflowSaveDialog
(
this
);
PageFactory
.
initElements
(
driver
,
this
);
}
@SneakyThrows
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
T
addTask
(
TaskType
type
)
{
final
var
task
=
driver
.
findElement
(
By
.
className
(
"task-item-"
+
type
.
name
()));
final
var
canvas
=
driver
.
findElement
(
By
.
className
(
"dag-container"
));
final
var
js
=
(
JavascriptExecutor
)
driver
;
final
var
dragAndDrop
=
String
.
join
(
"\n"
,
Resources
.
readLines
(
Resources
.
getResource
(
"dragAndDrop.js"
),
StandardCharsets
.
UTF_8
));
js
.
executeScript
(
dragAndDrop
,
task
,
canvas
);
switch
(
type
)
{
case
SHELL:
return
(
T
)
new
ShellTaskForm
(
this
);
case
SUB_PROCESS:
return
(
T
)
new
SubWorkflowTaskForm
(
this
);
}
throw
new
UnsupportedOperationException
(
"Unknown task type"
);
}
public
WorkflowSaveDialog
submit
()
{
buttonSave
().
click
();
return
new
WorkflowSaveDialog
(
this
);
}
public
enum
TaskType
{
SHELL
,
SUB_PROCESS
,
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowInstanceTab.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.project.ProjectDetailPage
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.FindBys
;
import
lombok.Getter
;
import
lombok.RequiredArgsConstructor
;
@Getter
public
final
class
WorkflowInstanceTab
extends
NavBarPage
implements
ProjectDetailPage
.
Tab
{
@FindBy
(
className
=
"rows-workflow-instances"
)
private
List
<
WebElement
>
instanceList
;
@FindBy
(
className
=
"select-all"
)
private
WebElement
checkBoxSelectAll
;
@FindBy
(
className
=
"button-delete-all"
)
private
WebElement
buttonDeleteAll
;
@FindBys
({
@FindBy
(
className
=
"el-popconfirm"
),
@FindBy
(
className
=
"el-button--primary"
),
})
private
List
<
WebElement
>
buttonConfirm
;
public
WorkflowInstanceTab
(
RemoteWebDriver
driver
)
{
super
(
driver
);
}
public
List
<
Row
>
instances
()
{
return
instanceList
()
.
stream
()
.
filter
(
WebElement:
:
isDisplayed
)
.
map
(
Row:
:
new
)
.
collect
(
Collectors
.
toList
());
}
public
WorkflowInstanceTab
deleteAll
()
{
if
(
instanceList
().
isEmpty
())
{
return
this
;
}
checkBoxSelectAll
().
click
();
buttonDeleteAll
().
click
();
buttonConfirm
()
.
stream
()
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"No confirm button is displayed"
))
.
click
();
return
this
;
}
@RequiredArgsConstructor
public
static
class
Row
{
private
final
WebElement
row
;
public
WebElement
rerunButton
()
{
return
row
.
findElement
(
By
.
className
(
"button-rerun"
));
}
public
boolean
isSuccess
()
{
return
!
row
.
findElements
(
By
.
className
(
"success"
)).
isEmpty
();
}
public
int
executionTime
()
{
return
Integer
.
parseInt
(
row
.
findElement
(
By
.
className
(
"execution-time"
)).
getText
());
}
public
Row
rerun
()
{
row
.
findElements
(
By
.
className
(
"button-rerun"
))
.
stream
()
.
filter
(
WebElement:
:
isDisplayed
)
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find rerun button"
))
.
click
();
return
this
;
}
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowRunDialog.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.PageFactory
;
import
lombok.Getter
;
@Getter
public
final
class
WorkflowRunDialog
{
private
final
WorkflowDefinitionTab
parent
;
@FindBy
(
id
=
"button-submit"
)
private
WebElement
buttonSubmit
;
public
WorkflowRunDialog
(
WorkflowDefinitionTab
parent
)
{
this
.
parent
=
parent
;
PageFactory
.
initElements
(
parent
().
driver
(),
this
);
}
public
WorkflowDefinitionTab
submit
()
{
buttonSubmit
().
click
();
return
parent
();
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/WorkflowSaveDialog.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow
;
import
java.util.List
;
import
java.util.stream.Stream
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebDriver
;
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.pagefactory.ByChained
;
import
org.openqa.selenium.support.ui.ExpectedConditions
;
import
org.openqa.selenium.support.ui.WebDriverWait
;
import
lombok.Getter
;
@Getter
public
final
class
WorkflowSaveDialog
{
private
final
WebDriver
driver
;
private
final
WorkflowForm
parent
;
@FindBy
(
id
=
"input-name"
)
private
WebElement
inputName
;
@FindBy
(
id
=
"button-submit"
)
private
WebElement
buttonSubmit
;
@FindBys
({
@FindBy
(
className
=
"input-param-key"
),
@FindBy
(
tagName
=
"input"
),
})
private
List
<
WebElement
>
inputParamKey
;
@FindBys
({
@FindBy
(
className
=
"input-param-val"
),
@FindBy
(
tagName
=
"input"
),
})
private
List
<
WebElement
>
inputParamVal
;
@FindBy
(
id
=
"select-tenant"
)
private
WebElement
selectTenant
;
public
WorkflowSaveDialog
(
WorkflowForm
parent
)
{
this
.
parent
=
parent
;
this
.
driver
=
parent
.
driver
();
PageFactory
.
initElements
(
driver
,
this
);
}
public
WorkflowSaveDialog
name
(
String
name
)
{
inputName
().
sendKeys
(
name
);
return
this
;
}
public
WorkflowSaveDialog
tenant
(
String
tenant
)
{
selectTenant
().
click
();
final
var
optionsLocator
=
By
.
className
(
"option-tenants"
);
new
WebDriverWait
(
driver
,
10
)
.
until
(
ExpectedConditions
.
visibilityOfElementLocated
(
optionsLocator
));
driver
().
findElements
(
optionsLocator
)
.
stream
()
.
filter
(
it
->
it
.
getText
().
contains
(
tenant
))
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"No such tenant: "
+
tenant
))
.
click
()
;
return
this
;
}
public
WorkflowSaveDialog
addGlobalParam
(
String
key
,
String
val
)
{
assert
inputParamKey
().
size
()
==
inputParamVal
().
size
();
final
var
len
=
inputParamKey
().
size
();
final
var
driver
=
parent
().
driver
();
Stream
.
concat
(
driver
.
findElements
(
new
ByChained
(
By
.
className
(
"user-def-params-model"
),
By
.
className
(
"add"
))).
stream
(),
driver
.
findElements
(
new
ByChained
(
By
.
className
(
"user-def-params-model"
),
By
.
className
(
"add-dp"
))).
stream
())
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find button to add param"
))
.
click
();
inputParamKey
().
get
(
len
).
sendKeys
(
key
);
inputParamVal
().
get
(
len
).
sendKeys
(
val
);
return
this
;
}
public
WorkflowForm
submit
()
{
buttonSubmit
().
click
();
return
parent
;
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/ShellTaskForm.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow.task
;
import
org.apache.dolphinscheduler.e2e.pages.common.CodeEditor
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm
;
import
lombok.Getter
;
@Getter
public
final
class
ShellTaskForm
extends
TaskNodeForm
{
private
final
CodeEditor
codeEditor
;
public
ShellTaskForm
(
WorkflowForm
parent
)
{
super
(
parent
);
this
.
codeEditor
=
new
CodeEditor
(
parent
.
driver
());
}
public
ShellTaskForm
script
(
String
script
)
{
codeEditor
().
content
(
script
);
return
this
;
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/SubWorkflowTaskForm.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow.task
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm
;
import
lombok.Getter
;
@Getter
public
final
class
SubWorkflowTaskForm
extends
TaskNodeForm
{
public
SubWorkflowTaskForm
(
WorkflowForm
parent
)
{
super
(
parent
);
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/project/workflow/task/TaskNodeForm.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.project.workflow.task
;
import
org.apache.dolphinscheduler.e2e.pages.project.workflow.WorkflowForm
;
import
java.util.List
;
import
java.util.stream.Stream
;
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.pagefactory.ByChained
;
import
lombok.Getter
;
@Getter
public
abstract
class
TaskNodeForm
{
@FindBy
(
id
=
"input-node-name"
)
private
WebElement
inputNodeName
;
@FindBy
(
id
=
"button-submit"
)
private
WebElement
buttonSubmit
;
@FindBys
({
@FindBy
(
className
=
"input-param-key"
),
@FindBy
(
tagName
=
"input"
),
})
private
List
<
WebElement
>
inputParamKey
;
@FindBys
({
@FindBy
(
className
=
"input-param-val"
),
@FindBy
(
tagName
=
"input"
),
})
private
List
<
WebElement
>
inputParamVal
;
private
final
WorkflowForm
parent
;
TaskNodeForm
(
WorkflowForm
parent
)
{
this
.
parent
=
parent
;
final
var
driver
=
parent
.
driver
();
PageFactory
.
initElements
(
driver
,
this
);
}
public
TaskNodeForm
name
(
String
name
)
{
inputNodeName
().
sendKeys
(
name
);
return
this
;
}
public
TaskNodeForm
addParam
(
String
key
,
String
val
)
{
assert
inputParamKey
().
size
()
==
inputParamVal
().
size
();
final
var
len
=
inputParamKey
().
size
();
final
var
driver
=
parent
().
driver
();
Stream
.
concat
(
driver
.
findElements
(
new
ByChained
(
By
.
className
(
"user-def-params-model"
),
By
.
className
(
"add"
))).
stream
(),
driver
.
findElements
(
new
ByChained
(
By
.
className
(
"user-def-params-model"
),
By
.
className
(
"add-dp"
))).
stream
())
.
findFirst
()
.
orElseThrow
(()
->
new
RuntimeException
(
"Cannot find button to add param"
))
.
click
();
inputParamKey
().
get
(
len
).
sendKeys
(
key
);
inputParamVal
().
get
(
len
).
sendKeys
(
val
);
return
this
;
}
public
WorkflowForm
submit
()
{
buttonSubmit
.
click
();
return
parent
();
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.pages.security
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage.NavBarItem
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
lombok.Getter
;
@Getter
public
class
SecurityPage
extends
NavBarPage
implements
NavBarItem
{
@FindBy
(
className
=
"tenant-manage"
)
private
WebElement
menuTenantManage
;
public
SecurityPage
(
RemoteWebDriver
driver
)
{
super
(
driver
);
}
public
<
T
extends
SecurityPage
.
Tab
>
T
goToTab
(
Class
<
T
>
tab
)
{
if
(
tab
==
TenantPage
.
class
)
{
menuTenantManage
().
click
();
return
tab
.
cast
(
new
TenantPage
(
driver
));
}
throw
new
UnsupportedOperationException
(
"Unknown tab: "
+
tab
.
getName
());
}
public
interface
Tab
{
}
}
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/TenantPage.java
→
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/
security/
TenantPage.java
浏览文件 @
37ba1eb5
...
...
@@ -17,12 +17,18 @@
* under the License.
*/
package
org.apache.dolphinscheduler.e2e.pages
;
package
org.apache.dolphinscheduler.e2e.pages.security
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
awaitility
.
Awaitility
.
await
;
import
org.apache.dolphinscheduler.e2e.pages.common.NavBarPage
;
import
java.util.List
;
import
org.openqa.selenium.
WebDriver
;
import
org.openqa.selenium.
By
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.remote.RemoteWebDriver
;
import
org.openqa.selenium.support.FindBy
;
import
org.openqa.selenium.support.FindBys
;
import
org.openqa.selenium.support.PageFactory
;
...
...
@@ -30,9 +36,7 @@ import org.openqa.selenium.support.PageFactory;
import
lombok.Getter
;
@Getter
public
final
class
TenantPage
{
private
final
WebDriver
driver
;
public
final
class
TenantPage
extends
NavBarPage
implements
SecurityPage
.
Tab
{
@FindBy
(
id
=
"button-create-tenant"
)
private
WebElement
buttonCreateTenant
;
...
...
@@ -40,18 +44,47 @@ public final class TenantPage {
private
List
<
WebElement
>
tenantList
;
@FindBys
({
@FindBy
(
className
=
"el-popconfirm"
),
@FindBy
(
className
=
"el-button--primary"
),
@FindBy
(
className
=
"el-popconfirm"
),
@FindBy
(
className
=
"el-button--primary"
),
})
private
WebElement
buttonConfirm
;
private
final
CreateTenantForm
createTenantForm
;
public
TenantPage
(
WebDriver
driver
)
{
this
.
driver
=
driver
;
this
.
createTenantForm
=
new
CreateTenantForm
();
public
TenantPage
(
RemoteWebDriver
driver
)
{
super
(
driver
);
createTenantForm
=
new
CreateTenantForm
();
}
public
TenantPage
create
(
String
tenant
)
{
return
create
(
tenant
,
""
);
}
public
TenantPage
create
(
String
tenant
,
String
description
)
{
buttonCreateTenant
().
click
();
createTenantForm
().
inputTenantCode
().
sendKeys
(
tenant
);
createTenantForm
().
inputDescription
().
sendKeys
(
description
);
createTenantForm
().
buttonSubmit
().
click
();
await
().
untilAsserted
(()
->
assertThat
(
tenantList
())
.
as
(
"Tenant list should contain newly-created tenant"
)
.
extracting
(
WebElement:
:
getText
)
.
anyMatch
(
it
->
it
.
contains
(
tenant
)));
return
this
;
}
public
TenantPage
delete
(
String
tenant
)
{
tenantList
()
.
stream
()
.
filter
(
it
->
it
.
getText
().
contains
(
tenant
))
.
findFirst
()
.
ifPresent
(
it
->
it
.
findElement
(
By
.
className
(
"delete"
)).
click
());
buttonConfirm
().
click
();
PageFactory
.
initElements
(
driver
,
this
)
;
return
this
;
}
@Getter
...
...
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/
tenant
/docker-compose.yaml
→
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/
basic
/docker-compose.yaml
浏览文件 @
37ba1eb5
...
...
@@ -23,6 +23,7 @@ services:
command
:
[
standalone-server
]
environment
:
DATABASE_TYPE
:
h2
WORKER_TENANT_AUTO_CREATE
:
'
true'
expose
:
-
12345
networks
:
...
...
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/dragAndDrop.js
0 → 100644
浏览文件 @
37ba1eb5
/*
* 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.
*/
function
createEvent
(
typeOfEvent
)
{
const
event
=
document
.
createEvent
(
"
CustomEvent
"
);
event
.
initCustomEvent
(
typeOfEvent
,
true
,
true
,
null
);
event
.
dataTransfer
=
{
data
:
{},
setData
:
function
(
key
,
value
)
{
this
.
data
[
key
]
=
value
;
},
getData
:
function
(
key
)
{
return
this
.
data
[
key
];
}
};
return
event
;
}
function
dispatchEvent
(
element
,
event
,
transferData
)
{
if
(
transferData
!==
undefined
)
{
event
.
dataTransfer
=
transferData
;
}
if
(
element
.
dispatchEvent
)
{
element
.
dispatchEvent
(
event
);
}
else
if
(
element
.
fireEvent
)
{
element
.
fireEvent
(
"
on
"
+
event
.
type
,
event
);
}
}
function
simulateHTML5DragAndDrop
(
element
,
destination
)
{
const
dragStartEvent
=
createEvent
(
'
dragstart
'
);
dispatchEvent
(
element
,
dragStartEvent
);
const
dropEvent
=
createEvent
(
'
drop
'
);
dispatchEvent
(
destination
,
dropEvent
,
dragStartEvent
.
dataTransfer
);
const
dragEndEvent
=
createEvent
(
'
dragend
'
);
dispatchEvent
(
element
,
dragEndEvent
,
dropEvent
.
dataTransfer
);
}
const
source
=
arguments
[
0
];
const
destination
=
arguments
[
1
];
simulateHTML5DragAndDrop
(
source
,
destination
);
dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java
浏览文件 @
37ba1eb5
...
...
@@ -24,6 +24,8 @@ import static org.testcontainers.containers.VncRecordingContainer.VncRecordingFo
import
java.io.File
;
import
java.io.IOException
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.net.URL
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
...
...
@@ -60,8 +62,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
final
class
DolphinSchedulerExtension
implements
BeforeAllCallback
,
AfterAllCallback
,
BeforeEachCallback
{
implements
BeforeAllCallback
,
AfterAllCallback
,
BeforeEachCallback
{
private
final
boolean
LOCAL_MODE
=
Objects
.
equals
(
System
.
getProperty
(
"local"
),
"true"
);
private
RemoteWebDriver
driver
;
...
...
@@ -71,7 +73,7 @@ final class DolphinSchedulerExtension
@Override
@SuppressWarnings
(
"UnstableApiUsage"
)
public
void
beforeAll
(
ExtensionContext
context
)
throws
IOException
{
Awaitility
.
setDefaultTimeout
(
Duration
.
ofSeconds
(
5
));
Awaitility
.
setDefaultTimeout
(
Duration
.
ofSeconds
(
10
));
Awaitility
.
setDefaultPollInterval
(
Duration
.
ofSeconds
(
1
));
Network
network
=
null
;
...
...
@@ -115,8 +117,8 @@ final class DolphinSchedulerExtension
record
=
Files
.
createTempDirectory
(
"record-"
);
}
browser
=
new
BrowserWebDriverContainer
<>()
.
withCapabilities
(
new
ChromeOptions
())
.
withRecordingMode
(
RECORD_ALL
,
record
.
toFile
(),
MP4
);
.
withCapabilities
(
new
ChromeOptions
())
.
withRecordingMode
(
RECORD_ALL
,
record
.
toFile
(),
MP4
);
if
(
network
!=
null
)
{
browser
.
withNetwork
(
network
);
}
...
...
@@ -127,6 +129,8 @@ final class DolphinSchedulerExtension
driver
.
manage
().
timeouts
()
.
implicitlyWait
(
5
,
TimeUnit
.
SECONDS
)
.
pageLoadTimeout
(
5
,
TimeUnit
.
SECONDS
);
driver
.
manage
().
window
()
.
maximize
();
if
(
address
==
null
)
{
try
{
address
=
HostAndPort
.
fromParts
(
browser
.
getTestHostIpAddress
(),
8888
);
...
...
@@ -142,6 +146,12 @@ final class DolphinSchedulerExtension
driver
.
get
(
new
URL
(
"http"
,
address
.
getHost
(),
address
.
getPort
(),
rootPath
).
toString
());
browser
.
beforeTest
(
new
TestDescription
(
context
));
final
Class
<?>
clazz
=
context
.
getRequiredTestClass
();
Stream
.
of
(
clazz
.
getDeclaredFields
())
.
filter
(
it
->
Modifier
.
isStatic
(
it
.
getModifiers
()))
.
filter
(
f
->
WebDriver
.
class
.
isAssignableFrom
(
f
.
getType
()))
.
forEach
(
it
->
setDriver
(
clazz
,
it
));
}
@Override
...
...
@@ -158,14 +168,16 @@ final class DolphinSchedulerExtension
final
Object
instance
=
context
.
getRequiredTestInstance
();
Stream
.
of
(
instance
.
getClass
().
getDeclaredFields
())
.
filter
(
f
->
WebDriver
.
class
.
isAssignableFrom
(
f
.
getType
()))
.
forEach
(
it
->
{
try
{
it
.
setAccessible
(
true
);
it
.
set
(
instance
,
driver
);
}
catch
(
IllegalAccessException
e
)
{
LOGGER
.
error
(
"Failed to inject web driver to field: {}"
,
it
.
getName
(),
e
);
}
});
.
forEach
(
it
->
setDriver
(
instance
,
it
));
}
private
void
setDriver
(
Object
object
,
Field
field
)
{
try
{
field
.
setAccessible
(
true
);
field
.
set
(
object
,
driver
);
}
catch
(
IllegalAccessException
e
)
{
LOGGER
.
error
(
"Failed to inject web driver to field: {}"
,
field
.
getName
(),
e
);
}
}
private
DockerComposeContainer
<?>
createDockerCompose
(
ExtensionContext
context
)
{
...
...
@@ -178,9 +190,10 @@ final class DolphinSchedulerExtension
.
map
(
File:
:
new
)
.
collect
(
Collectors
.
toList
());
compose
=
new
DockerComposeContainer
<>(
files
)
.
withPull
(
true
)
.
withTailChildContainers
(
true
)
.
waitingFor
(
"dolphinscheduler_1"
,
Wait
.
forHealthcheck
());
.
withPull
(
true
)
.
withTailChildContainers
(
true
)
.
withLogConsumer
(
"dolphinscheduler_1"
,
outputFrame
->
LOGGER
.
info
(
outputFrame
.
getUtf8String
()))
.
waitingFor
(
"dolphinscheduler_1"
,
Wait
.
forHealthcheck
());
return
compose
;
}
...
...
dolphinscheduler-e2e/pom.xml
浏览文件 @
37ba1eb5
...
...
@@ -31,8 +31,8 @@
</modules>
<properties>
<maven.compiler.source>
8
</maven.compiler.source>
<maven.compiler.target>
8
</maven.compiler.target>
<maven.compiler.source>
11
</maven.compiler.source>
<maven.compiler.target>
11
</maven.compiler.target>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<junit.version>
5.7.2
</junit.version>
...
...
@@ -41,18 +41,21 @@
<assertj-core.version>
3.20.2
</assertj-core.version>
<awaitility.version>
4.1.0
</awaitility.version>
<kotlin.version>
1.5.30
</kotlin.version>
<slf4j-api.version>
1.7.32
</slf4j-api.version>
<log4j-slf4j-impl.version>
2.14.1
</log4j-slf4j-impl.version>
<guava.version>
31.0.1-jre
</guava.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.slf4j
</groupId>
<artifactId>
slf4j-api
</artifactId>
<version>
1.7.32
</version>
<version>
${slf4j-api.version}
</version>
</dependency>
<dependency>
<groupId>
org.apache.logging.log4j
</groupId>
<artifactId>
log4j-slf4j-impl
</artifactId>
<version>
2.14.1
</version>
<version>
${log4j-slf4j-impl.version}
</version>
</dependency>
<dependency>
...
...
@@ -109,6 +112,11 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
com.google.guava
</groupId>
<artifactId>
guava
</artifactId>
<version>
${guava.version}
</version>
</dependency>
<dependency>
<groupId>
org.junit
</groupId>
<artifactId>
junit-bom
</artifactId>
...
...
@@ -119,7 +127,7 @@
<dependency>
<groupId>
org.testcontainers
</groupId>
<artifactId>
testcontainers-bom
</artifactId>
<version>
1.16.
0
</version>
<version>
1.16.
1
</version>
<scope>
import
</scope>
<type>
pom
</type>
</dependency>
...
...
dolphinscheduler-ui/pom.xml
浏览文件 @
37ba1eb5
...
...
@@ -29,7 +29,7 @@
<name>
${project.artifactId}
</name>
<properties>
<node.version>
v1
2.20.2
</node.version>
<node.version>
v1
4.15.1
</node.version>
<npm.version>
6.14.11
</npm.version>
<sonar.sources>
src
</sonar.sources>
</properties>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue
浏览文件 @
37ba1eb5
...
...
@@ -25,7 +25,8 @@
:key=
"taskType.name"
@
onDragstart=
"(e) => $emit('on-drag-start', e, taskType)"
:class=
"
{
disabled: isDetails
disabled: isDetails,
[`task-item-${taskType.name}`]: true
}"
>
<div
class=
"task-item"
>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue
浏览文件 @
37ba1eb5
...
...
@@ -143,6 +143,7 @@
type=
"primary"
size=
"mini"
@
click=
"saveProcess"
id=
"button-save"
>
{{
$t
(
"
Save
"
)
}}
</el-button
>
<el-button
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/config.js
浏览文件 @
37ba1eb5
...
...
@@ -155,28 +155,32 @@ const tasksState = {
desc
:
`
${
i18n
.
$t
(
'
Submitted successfully
'
)}
`
,
color
:
'
#A9A9A9
'
,
icoUnicode
:
'
ri-record-circle-fill
'
,
isSpin
:
false
isSpin
:
false
,
classNames
:
'
submitted
'
},
RUNNING_EXECUTION
:
{
id
:
1
,
desc
:
`
${
i18n
.
$t
(
'
Executing
'
)}
`
,
color
:
'
#0097e0
'
,
icoUnicode
:
'
el-icon-s-tools
'
,
isSpin
:
true
isSpin
:
true
,
classNames
:
'
executing
'
},
READY_PAUSE
:
{
id
:
2
,
desc
:
`
${
i18n
.
$t
(
'
Ready to pause
'
)}
`
,
color
:
'
#07b1a3
'
,
icoUnicode
:
'
ri-settings-3-line
'
,
isSpin
:
false
isSpin
:
false
,
classNames
:
'
submitted
'
},
PAUSE
:
{
id
:
3
,
desc
:
`
${
i18n
.
$t
(
'
Pause
'
)}
`
,
color
:
'
#057c72
'
,
icoUnicode
:
'
el-icon-video-pause
'
,
isSpin
:
false
isSpin
:
false
,
classNames
:
'
pause
'
},
READY_STOP
:
{
id
:
4
,
...
...
@@ -197,14 +201,16 @@ const tasksState = {
desc
:
`
${
i18n
.
$t
(
'
Failed
'
)}
`
,
color
:
'
#000000
'
,
icoUnicode
:
'
el-icon-circle-close
'
,
isSpin
:
false
isSpin
:
false
,
classNames
:
'
failed
'
},
SUCCESS
:
{
id
:
7
,
desc
:
`
${
i18n
.
$t
(
'
Success
'
)}
`
,
color
:
'
#33cc00
'
,
icoUnicode
:
'
el-icon-circle-check
'
,
isSpin
:
false
isSpin
:
false
,
classNames
:
'
success
'
},
NEED_FAULT_TOLERANCE
:
{
id
:
8
,
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
浏览文件 @
37ba1eb5
...
...
@@ -23,6 +23,7 @@
size=
""
:with-header=
"false"
:wrapperClosable=
"false"
class=
"task-drawer"
>
<!-- fix the bug that Element-ui(2.13.2) auto focus on the first input -->
<div
style=
"width: 0px; height: 0px; overflow: hidden"
>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
浏览文件 @
37ba1eb5
...
...
@@ -60,6 +60,7 @@
:disabled=
"isDetails"
:placeholder=
"$t('Please enter name (required)')"
maxlength=
"100"
id=
"input-node-name"
>
</el-input>
</div>
...
...
@@ -437,6 +438,7 @@
:loading=
"spinnerLoading"
@
click=
"ok()"
:disabled=
"isDetails"
id=
"button-submit"
>
{{ spinnerLoading ? $t("Loading...") : $t("Confirm") }}
</el-button>
</div>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/_source/localParams.vue
浏览文件 @
37ba1eb5
...
...
@@ -26,6 +26,7 @@
size=
"small"
v-model=
"localParamsList[$index].prop"
:placeholder=
"$t('prop(required)')"
class=
"input-param-key"
maxlength=
"256"
@
blur=
"_verifProp()"
:style=
"inputStyle"
>
...
...
@@ -64,6 +65,7 @@
size=
"small"
v-model=
"localParamsList[$index].value"
:placeholder=
"$t('value(optional)')"
class=
"input-param-val"
maxlength=
"256"
@
blur=
"_handleValue()"
:style=
"inputStyle"
>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
浏览文件 @
37ba1eb5
...
...
@@ -20,9 +20,11 @@
@
change=
"_onChange"
v-model=
"selectedValue"
size=
"small"
id=
"select-tenant"
style=
"width: 180px"
>
<el-option
v-for=
"item in itemList"
class=
"option-tenants"
:key=
"item.id"
:value=
"item.id"
:label=
"item.tenantCode"
>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
浏览文件 @
37ba1eb5
...
...
@@ -22,6 +22,7 @@
type=
"text"
size=
"small"
v-model=
"name"
id=
"input-name"
:disabled=
"router.history.current.name === 'projects-instance-details'"
:placeholder=
"$t('Please enter name (required)')"
>
</el-input>
...
...
@@ -101,7 +102,7 @@
</div>
</
template
>
<el-button
type=
"text"
size=
"small"
@
click=
"close()"
>
{{$t('Cancel')}}
</el-button>
<el-button
type=
"primary"
size=
"small"
round
:disabled=
"isDetails"
@
click=
"ok()"
>
{{$t('Add')}}
</el-button>
<el-button
type=
"primary"
size=
"small"
round
:disabled=
"isDetails"
@
click=
"ok()"
id=
"button-submit"
>
{{$t('Add')}}
</el-button>
</div>
</div>
</div>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
浏览文件 @
37ba1eb5
...
...
@@ -17,8 +17,8 @@
<
template
>
<div
class=
"list-model"
style=
"position: relative;"
>
<div
class=
"table-box"
>
<el-table
:data=
"list"
size=
"mini"
style=
"width: 100%"
@
selection-change=
"_arrDelChange"
>
<el-table-column
type=
"selection"
width=
"50"
:selectable=
"selectable"
></el-table-column>
<el-table
:data=
"list"
size=
"mini"
style=
"width: 100%"
@
selection-change=
"_arrDelChange"
row-class-name=
"rows-workflow-definitions"
>
<el-table-column
type=
"selection"
width=
"50"
:selectable=
"selectable"
class-name=
"select-all"
></el-table-column>
<el-table-column
prop=
"id"
:label=
"$t('#')"
width=
"50"
></el-table-column>
<el-table-column
:label=
"$t('Process Name')"
min-width=
"200"
>
<template
slot-scope=
"scope"
>
...
...
@@ -26,7 +26,7 @@
<p>
{{
scope
.
row
.
name
}}
</p>
<div
slot=
"reference"
class=
"name-wrapper"
>
<router-link
:to=
"
{ path: `/projects/${projectCode}/definition/list/${scope.row.code}` }" tag="a" class="links">
<span
class=
"ellipsis"
>
{{
scope
.
row
.
name
}}
</span>
<span
class=
"ellipsis
name
"
>
{{
scope
.
row
.
name
}}
</span>
</router-link>
</div>
</el-popover>
...
...
@@ -66,16 +66,16 @@
<span><el-button
type=
"primary"
size=
"mini"
icon=
"el-icon-edit-outline"
:disabled=
"scope.row.releaseState === 'ONLINE'"
@
click=
"_edit(scope.row)"
circle
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('Start')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"success"
size=
"mini"
:disabled=
"scope.row.releaseState !== 'ONLINE'"
icon=
"el-icon-video-play"
@
click=
"_start(scope.row)"
circle
></el-button></span>
<span><el-button
type=
"success"
size=
"mini"
:disabled=
"scope.row.releaseState !== 'ONLINE'"
icon=
"el-icon-video-play"
@
click=
"_start(scope.row)"
circle
class=
"button-run"
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('Timing')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"primary"
size=
"mini"
icon=
"el-icon-time"
:disabled=
"scope.row.releaseState !== 'ONLINE' || scope.row.scheduleReleaseState !== null"
@
click=
"_timing(scope.row)"
circle
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('online')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"warning"
size=
"mini"
v-if=
"scope.row.releaseState === 'OFFLINE'"
icon=
"el-icon-upload2"
@
click=
"_poponline(scope.row)"
circle
></el-button></span>
<span><el-button
type=
"warning"
size=
"mini"
v-if=
"scope.row.releaseState === 'OFFLINE'"
icon=
"el-icon-upload2"
@
click=
"_poponline(scope.row)"
circle
class=
"button-publish"
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('offline')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"danger"
size=
"mini"
icon=
"el-icon-download"
v-if=
"scope.row.releaseState === 'ONLINE'"
@
click=
"_downline(scope.row)"
circle
></el-button></span>
<span><el-button
type=
"danger"
size=
"mini"
icon=
"el-icon-download"
v-if=
"scope.row.releaseState === 'ONLINE'"
@
click=
"_downline(scope.row)"
circle
class=
"button-cancel-publish"
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('Copy Workflow')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"primary"
size=
"mini"
:disabled=
"scope.row.releaseState === 'ONLINE'"
icon=
"el-icon-document-copy"
@
click=
"_copyProcess(scope.row)"
circle
></el-button></span>
...
...
@@ -115,7 +115,7 @@
:title=
"$t('Delete?')"
@
onConfirm=
"_delete({},-1)"
>
<el-button
style=
"position: absolute; bottom: -48px; left: 19px;"
type=
"primary"
size=
"mini"
:disabled=
"!strSelectCodes"
slot=
"reference"
>
{{$t('Delete')}}
</el-button>
<el-button
style=
"position: absolute; bottom: -48px; left: 19px;"
type=
"primary"
size=
"mini"
:disabled=
"!strSelectCodes"
slot=
"reference"
class=
"button-delete-all"
>
{{$t('Delete')}}
</el-button>
</el-popconfirm>
</el-tooltip>
<el-button
type=
"primary"
size=
"mini"
:disabled=
"!strSelectCodes"
style=
"position: absolute; bottom: -48px; left: 80px;"
@
click=
"_batchExport(item)"
>
{{$t('Export')}}
</el-button>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
浏览文件 @
37ba1eb5
...
...
@@ -191,7 +191,7 @@
</div>
<div
class=
"submit"
>
<el-button
type=
"text"
size=
"small"
@
click=
"close()"
>
{{$t('Cancel')}}
</el-button>
<el-button
type=
"primary"
size=
"small"
round
:loading=
"spinnerLoading"
@
click=
"ok()"
>
{{spinnerLoading ? $t('Loading...') : $t('Start')}}
</el-button>
<el-button
type=
"primary"
size=
"small"
round
:loading=
"spinnerLoading"
@
click=
"ok()"
id=
"button-submit"
>
{{spinnerLoading ? $t('Loading...') : $t('Start')}}
</el-button>
</div>
</div>
</template>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue
浏览文件 @
37ba1eb5
...
...
@@ -20,7 +20,7 @@
<template
slot=
"conditions"
>
<m-conditions
@
on-conditions=
"_onConditions"
>
<template
slot=
"button-group"
>
<el-button
size=
"mini"
@
click=
"() => this.$router.push(
{name: 'definition-create'})">
{{
$t
(
'
Create process
'
)
}}
</el-button>
<el-button
size=
"mini"
@
click=
"() => this.$router.push(
{name: 'definition-create'})"
id="button-create-process"
>
{{
$t
(
'
Create process
'
)
}}
</el-button>
<el-button
size=
"mini"
@
click=
"_uploading"
>
{{
$t
(
'
Import process
'
)
}}
</el-button>
</
template
>
</m-conditions>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
浏览文件 @
37ba1eb5
...
...
@@ -17,8 +17,8 @@
<
template
>
<div
class=
"list-model"
style=
"position: relative;"
>
<div
class=
"table-box"
>
<el-table
class=
"fixed"
:data=
"list"
size=
"mini"
style=
"width: 100%"
@
selection-change=
"_arrDelChange"
>
<el-table-column
type=
"selection"
width=
"50"
></el-table-column>
<el-table
class=
"fixed"
:data=
"list"
size=
"mini"
style=
"width: 100%"
@
selection-change=
"_arrDelChange"
row-class-name=
"rows-workflow-instances"
>
<el-table-column
type=
"selection"
width=
"50"
class-name=
"select-all"
></el-table-column>
<el-table-column
prop=
"id"
:label=
"$t('#')"
width=
"50"
></el-table-column>
<el-table-column
:label=
"$t('Process Name')"
min-width=
"200"
>
<template
slot-scope=
"scope"
>
...
...
@@ -61,7 +61,7 @@
<span>
{{
scope
.
row
.
duration
|
filterNull
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"runTimes"
:label=
"$t('Run Times')"
></el-table-column>
<el-table-column
prop=
"runTimes"
:label=
"$t('Run Times')"
class-name=
"execution-time"
></el-table-column>
<el-table-column
prop=
"recovery"
:label=
"$t('fault-tolerant sign')"
></el-table-column>
<el-table-column
:label=
"$t('Dry-run flag')"
width=
"100"
>
<
template
slot-scope=
"scope"
>
...
...
@@ -80,7 +80,7 @@
</span>
</el-tooltip>
<el-tooltip
:content=
"$t('Rerun')"
placement=
"top"
:enterable=
"false"
>
<span><el-button
type=
"primary"
size=
"mini"
:disabled=
"scope.row.state !== 'SUCCESS' && scope.row.state !== 'PAUSE' && scope.row.state !== 'FAILURE' && scope.row.state !== 'STOP'"
icon=
"el-icon-refresh"
@
click=
"_reRun(scope.row,scope.$index)"
circle
></el-button></span>
<span><el-button
type=
"primary"
size=
"mini"
:disabled=
"scope.row.state !== 'SUCCESS' && scope.row.state !== 'PAUSE' && scope.row.state !== 'FAILURE' && scope.row.state !== 'STOP'"
icon=
"el-icon-refresh"
@
click=
"_reRun(scope.row,scope.$index)"
circle
class=
"button-rerun"
></el-button></span>
</el-tooltip>
<el-tooltip
:content=
"$t('Recovery Failed')"
placement=
"top"
:enterable=
"false"
>
<span>
...
...
@@ -233,7 +233,7 @@
:title=
"$t('Delete?')"
@
onConfirm=
"_delete({},-1)"
>
<el-button
style=
"position: absolute; bottom: -48px; left: 19px;"
type=
"primary"
size=
"mini"
:disabled=
"!strDelete"
slot=
"reference"
>
{{$t('Delete')}}
</el-button>
<el-button
style=
"position: absolute; bottom: -48px; left: 19px;"
type=
"primary"
size=
"mini"
:disabled=
"!strDelete"
slot=
"reference"
class=
"button-delete-all"
>
{{$t('Delete')}}
</el-button>
</el-popconfirm>
</el-tooltip>
</div>
...
...
@@ -273,7 +273,7 @@
*/
_rtState
(
code
)
{
let
o
=
tasksState
[
code
]
return
`<em class="fa ansfont
${
o
.
icoUnicode
}
${
o
.
isSpin
?
'
as as-spin
'
:
''
}
" style="color:
${
o
.
color
}
" data-toggle="tooltip" data-container="body" title="
${
o
.
desc
}
"></em>`
return
`<em class="fa ansfont
${
o
.
classNames
}
${
o
.
icoUnicode
}
${
o
.
isSpin
?
'
as as-spin
'
:
''
}
" style="color:
${
o
.
color
}
" data-toggle="tooltip" data-container="body" title="
${
o
.
desc
}
"></em>`
},
/**
* delete
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/_source/createProject.vue
浏览文件 @
37ba1eb5
...
...
@@ -16,13 +16,14 @@
*/
<
template
>
<m-popover
ref=
"popover"
:nameText=
"item ? $t('Edit') : $t('Create Project')"
:ok-text=
"item ? $t('Edit') : $t('Submit')"
@
close=
"_close"
@
ok=
"_ok"
>
@
close=
"_close"
@
ok=
"_ok"
ok-id=
"button-submit"
>
<template
slot=
"content"
>
<div
class=
"projects-create-model"
>
<m-list-box-f>
<template
slot=
"name"
><strong>
*
</strong>
{{
$t
(
'
Project Name
'
)
}}
</
template
>
<
template
slot=
"content"
>
<el-input
id=
"input-project-name"
v-model=
"projectName"
:placeholder=
"$t('Please enter name')"
maxlength=
"60"
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/_source/list.vue
浏览文件 @
37ba1eb5
...
...
@@ -17,14 +17,14 @@
<
template
>
<div
class=
"list-model"
>
<div
class=
"table-box"
>
<el-table
:data=
"list"
size=
"mini"
style=
"width: 100%"
>
<el-table
:data=
"list"
size=
"mini"
style=
"width: 100%"
row-class-name=
"rows-project"
>
<el-table-column
type=
"index"
:label=
"$t('#')"
width=
"50"
></el-table-column>
<el-table-column
:label=
"$t('Project Name')"
>
<template
slot-scope=
"scope"
>
<el-popover
trigger=
"hover"
placement=
"top"
>
<p>
{{
scope
.
row
.
name
}}
</p>
<div
slot=
"reference"
class=
"name-wrapper"
>
<a
href=
"javascript:"
class=
"links"
@
click=
"_switchProjects(scope.row)"
>
{{
scope
.
row
.
name
}}
</a>
<a
href=
"javascript:"
class=
"links
project-name
"
@
click=
"_switchProjects(scope.row)"
>
{{
scope
.
row
.
name
}}
</a>
</div>
</el-popover>
</
template
>
...
...
@@ -61,7 +61,7 @@
:title=
"$t('Delete?')"
@
onConfirm=
"_delete(scope.row,scope.row.id)"
>
<el-button
type=
"danger"
size=
"mini"
icon=
"el-icon-delete"
circle
slot=
"reference"
></el-button>
<el-button
type=
"danger"
size=
"mini"
icon=
"el-icon-delete"
circle
slot=
"reference"
class=
"delete"
></el-button>
</el-popconfirm>
</el-tooltip>
</
template
>
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/list/index.vue
浏览文件 @
37ba1eb5
...
...
@@ -19,7 +19,7 @@
<template
slot=
"conditions"
>
<m-conditions
@
on-conditions=
"_onConditions"
>
<template
slot=
"button-group"
>
<el-button
size=
"mini"
@
click=
"_create('')"
>
{{
$t
(
'
Create Project
'
)
}}
</el-button>
<el-button
size=
"mini"
@
click=
"_create('')"
id=
"button-create-project"
>
{{
$t
(
'
Create Project
'
)
}}
</el-button>
<el-dialog
:title=
"item ? $t('Edit') : $t('Create Project')"
v-if=
"createProjectDialog"
...
...
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/index.vue
浏览文件 @
37ba1eb5
/*
* 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.
*/
<
template
>
<div
class=
"task-definition"
v-if=
"!isLoading"
>
<m-list-construction
:title=
"$t('Task Definition')"
>
<template
slot=
"conditions"
>
<m-conditions
@
on-conditions=
"_onConditions"
:taskTypeShow=
"true"
>
<template
v-slot:button-group
>
<el-button
size=
"mini"
@
click=
"createTask"
>
{{
$t
(
"
Create task
"
)
}}
</el-button>
</
template
>
</m-conditions>
</template>
<
template
v-slot:content
>
<template
v-if=
"tasksList.length || total > 0"
>
<m-list
:tasksList=
"tasksList"
@
on-update=
"_onUpdate"
@
editTask=
"editTask"
@
viewTaskDetail=
"viewTaskDetail"
></m-list>
<div
class=
"page-box"
>
<el-pagination
background
@
current-change=
"_page"
@
size-change=
"_pageSize"
:page-size=
"searchParams.pageSize"
:current-page.sync=
"searchParams.pageNo"
:page-sizes=
"[10, 30, 50]"
:total=
"total"
layout=
"sizes, prev, pager, next, jumper"
>
</el-pagination>
</div>
</
template
>
<
template
v-if=
"!tasksList.length"
>
<m-no-data></m-no-data>
</
template
>
<m-spin
:is-spin=
"isLoading"
></m-spin>
</template>
</m-list-construction>
<el-drawer
:visible.sync=
"taskDrawer"
size=
""
:with-header=
"false"
@
close=
"closeTaskDrawer"
>
<!-- fix the bug that Element-ui(2.13.2) auto focus on the first input -->
<div
style=
"width: 0px; height: 0px; overflow: hidden"
>
<el-input
type=
"text"
/>
</div>
<m-form-model
v-if=
"taskDrawer"
:nodeData=
"nodeData"
type=
"task-definition"
@
changeTaskType=
"changeTaskType"
@
close=
"closeTaskDrawer"
@
addTaskInfo=
"saveTask"
:taskDefinition=
"editingTask"
>
</m-form-model>
</el-drawer>
</div>
</template>
<
script
>
import
mListConstruction
from
'
@/module/components/listConstruction/listConstruction
'
import
mConditions
from
'
@/module/components/conditions/conditions
'
import
mList
from
'
./_source/list
'
import
mNoData
from
'
@/module/components/noData/noData
'
import
mSpin
from
'
@/module/components/spin/spin
'
import
{
mapActions
,
mapMutations
}
from
'
vuex
'
import
listUrlParamHandle
from
'
@/module/mixin/listUrlParamHandle
'
import
mFormModel
from
'
@/conf/home/pages/dag/_source/formModel/formModel.vue
'
/**
* tasksType
*/
import
{
tasksType
}
from
'
@/conf/home/pages/dag/_source/config.js
'
const
DEFAULT_NODE_DATA
=
{
id
:
-
1
,
taskType
:
'
SHELL
'
,
instanceId
:
-
1
}
export
default
{
name
:
'
task-definition-index
'
,
data
()
{
// tasksType
const
tasksTypeList
=
Object
.
keys
(
tasksType
)
return
{
total
:
null
,
tasksList
:
[],
isLoading
:
true
,
searchParams
:
{
pageSize
:
10
,
pageNo
:
1
,
searchVal
:
''
,
taskType
:
''
,
userId
:
''
},
// whether the task config drawer is visible
taskDrawer
:
false
,
// nodeData
nodeData
:
{
...
DEFAULT_NODE_DATA
},
// tasksType
tasksTypeList
,
// editing task definition
editingTask
:
null
}
},
mixins
:
[
listUrlParamHandle
],
methods
:
{
...
mapActions
(
'
dag
'
,
[
'
getTaskDefinitionsList
'
,
'
genTaskCodeList
'
,
'
saveTaskDefinition
'
,
'
updateTaskDefinition
'
]),
...
mapActions
(
'
dag
'
,
[
'
getProcessList
'
,
'
getProjectList
'
,
'
getResourcesList
'
,
'
getResourcesListJar
'
,
'
getResourcesListJar
'
]),
...
mapMutations
(
'
dag
'
,
[
'
resetParams
'
,
'
setIsDetails
'
]),
...
mapActions
(
'
security
'
,
[
'
getTenantList
'
,
'
getWorkerGroupsAll
'
,
'
getAlarmGroupsAll
'
]),
/**
* Toggle task drawer
*/
showTaskDrawer
()
{
this
.
taskDrawer
=
true
},
closeTaskDrawer
()
{
this
.
setIsDetails
(
false
)
this
.
taskDrawer
=
false
},
saveTask
({
item
})
{
const
isEditing
=
!!
this
.
editingTask
if
(
isEditing
)
{
this
.
updateTaskDefinition
(
item
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
res
.
msg
)
this
.
_onUpdate
()
this
.
closeTaskDrawer
()
})
.
catch
((
e
)
=>
{
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
else
{
this
.
genTaskCodeList
({
genNum
:
1
})
.
then
((
res
)
=>
{
const
[
code
]
=
res
return
code
})
.
then
((
code
)
=>
{
return
this
.
saveTaskDefinition
({
taskDefinitionJson
:
[
{
...
item
,
code
}
]
})
})
.
then
((
res
)
=>
{
this
.
$message
.
success
(
res
.
msg
)
this
.
_onUpdate
()
this
.
closeTaskDrawer
()
})
.
catch
((
e
)
=>
{
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
},
createTask
()
{
this
.
editingTask
=
null
this
.
nodeData
.
taskType
=
DEFAULT_NODE_DATA
.
taskType
this
.
showTaskDrawer
()
},
editTask
(
task
)
{
this
.
editingTask
=
task
this
.
nodeData
.
id
=
task
.
code
this
.
nodeData
.
taskType
=
task
.
taskType
this
.
showTaskDrawer
()
},
viewTaskDetail
(
task
)
{
this
.
setIsDetails
(
true
)
this
.
editTask
(
task
)
},
/**
* pageNo
*/
_page
(
val
)
{
this
.
searchParams
.
pageNo
=
val
},
_pageSize
(
val
)
{
this
.
searchParams
.
pageSize
=
val
},
/**
* conditions
*/
_onConditions
(
o
)
{
this
.
searchParams
.
searchVal
=
o
.
searchVal
this
.
searchParams
.
taskType
=
o
.
taskType
this
.
searchParams
.
pageNo
=
1
},
/**
* get task definition list
*/
_getList
(
flag
)
{
this
.
isLoading
=
!
flag
this
.
getTaskDefinitionsList
(
this
.
searchParams
)
.
then
((
res
)
=>
{
if
(
this
.
searchParams
.
pageNo
>
1
&&
res
.
totalList
.
length
===
0
)
{
this
.
searchParams
.
pageNo
=
this
.
searchParams
.
pageNo
-
1
}
else
{
this
.
tasksList
=
[]
this
.
tasksList
=
res
.
totalList
this
.
total
=
res
.
total
this
.
isLoading
=
false
}
})
.
catch
((
e
)
=>
{
this
.
isLoading
=
false
})
},
/**
* update task dataList
*/
_onUpdate
()
{
this
.
_debounceGET
(
'
false
'
)
},
/**
* change form modal task type
*/
changeTaskType
(
value
)
{
this
.
nodeData
.
taskType
=
value
}
},
created
()
{
this
.
isLoading
=
true
// Initialization parameters
this
.
resetParams
()
// Promise Get node needs data
Promise
.
all
([
// get process definition
this
.
getProcessList
(),
// get project
this
.
getProjectList
(),
// get jar
this
.
getResourcesListJar
(),
// get resource
this
.
getResourcesList
(),
// get jar
this
.
getResourcesListJar
(),
// get worker group list
this
.
getWorkerGroupsAll
(),
// get alarm group list
this
.
getAlarmGroupsAll
(),
this
.
getTenantList
()
])
.
then
((
data
)
=>
{
this
.
isLoading
=
false
})
.
catch
(()
=>
{
this
.
isLoading
=
false
})
},
mounted
()
{},
components
:
{
mListConstruction
,
mConditions
,
mList
,
mNoData
,
mSpin
,
mFormModel
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.task-definition
{
.taskGroupBtn
{
width
:
300px
;
}
}
</
style
>
/*
* 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.
*/
<
template
>
<div
class=
"task-definition"
v-if=
"!isLoading"
>
<m-list-construction
:title=
"$t('Task Definition')"
>
<template
slot=
"conditions"
>
<m-conditions
@
on-conditions=
"_onConditions"
:taskTypeShow=
"true"
>
<template
v-slot:button-group
>
<el-button
size=
"mini"
@
click=
"createTask"
>
{{
$t
(
"
Create task
"
)
}}
</el-button>
</
template
>
</m-conditions>
</template>
<
template
v-slot:content
>
<template
v-if=
"tasksList.length || total > 0"
>
<m-list
:tasksList=
"tasksList"
@
on-update=
"_onUpdate"
@
editTask=
"editTask"
@
viewTaskDetail=
"viewTaskDetail"
></m-list>
<div
class=
"page-box"
>
<el-pagination
background
@
current-change=
"_page"
@
size-change=
"_pageSize"
:page-size=
"searchParams.pageSize"
:current-page.sync=
"searchParams.pageNo"
:page-sizes=
"[10, 30, 50]"
:total=
"total"
layout=
"sizes, prev, pager, next, jumper"
>
</el-pagination>
</div>
</
template
>
<
template
v-if=
"!tasksList.length"
>
<m-no-data></m-no-data>
</
template
>
<m-spin
:is-spin=
"isLoading"
></m-spin>
</template>
</m-list-construction>
<el-drawer
:visible.sync=
"taskDrawer"
size=
""
:with-header=
"false"
@
close=
"closeTaskDrawer"
>
<!-- fix the bug that Element-ui(2.13.2) auto focus on the first input -->
<div
style=
"width: 0px; height: 0px; overflow: hidden"
>
<el-input
type=
"text"
/>
</div>
<m-form-model
v-if=
"taskDrawer"
:nodeData=
"nodeData"
type=
"task-definition"
@
changeTaskType=
"changeTaskType"
@
close=
"closeTaskDrawer"
@
addTaskInfo=
"saveTask"
:taskDefinition=
"editingTask"
>
</m-form-model>
</el-drawer>
</div>
</template>
<
script
>
import
mListConstruction
from
'
@/module/components/listConstruction/listConstruction
'
import
mConditions
from
'
@/module/components/conditions/conditions
'
import
mList
from
'
./_source/list
'
import
mNoData
from
'
@/module/components/noData/noData
'
import
mSpin
from
'
@/module/components/spin/spin
'
import
{
mapActions
,
mapMutations
}
from
'
vuex
'
import
listUrlParamHandle
from
'
@/module/mixin/listUrlParamHandle
'
import
mFormModel
from
'
@/conf/home/pages/dag/_source/formModel/formModel.vue
'
/**
* tasksType
*/
import
{
tasksType
}
from
'
@/conf/home/pages/dag/_source/config.js
'
const
DEFAULT_NODE_DATA
=
{
id
:
-
1
,
taskType
:
'
SHELL
'
,
instanceId
:
-
1
}
export
default
{
name
:
'
task-definition-index
'
,
data
()
{
// tasksType
const
tasksTypeList
=
Object
.
keys
(
tasksType
)
return
{
total
:
null
,
tasksList
:
[],
isLoading
:
true
,
searchParams
:
{
pageSize
:
10
,
pageNo
:
1
,
searchVal
:
''
,
taskType
:
''
,
userId
:
''
},
// whether the task config drawer is visible
taskDrawer
:
false
,
// nodeData
nodeData
:
{
...
DEFAULT_NODE_DATA
},
// tasksType
tasksTypeList
,
// editing task definition
editingTask
:
null
}
},
mixins
:
[
listUrlParamHandle
],
methods
:
{
...
mapActions
(
'
dag
'
,
[
'
getTaskDefinitionsList
'
,
'
genTaskCodeList
'
,
'
saveTaskDefinition
'
,
'
updateTaskDefinition
'
]),
...
mapActions
(
'
dag
'
,
[
'
getProcessList
'
,
'
getProjectList
'
,
'
getResourcesList
'
,
'
getResourcesListJar
'
,
'
getResourcesListJar
'
]),
...
mapMutations
(
'
dag
'
,
[
'
resetParams
'
,
'
setIsDetails
'
]),
...
mapActions
(
'
security
'
,
[
'
getTenantList
'
,
'
getWorkerGroupsAll
'
,
'
getAlarmGroupsAll
'
]),
/**
* Toggle task drawer
*/
showTaskDrawer
()
{
this
.
taskDrawer
=
true
},
closeTaskDrawer
()
{
this
.
setIsDetails
(
false
)
this
.
taskDrawer
=
false
},
saveTask
({
item
})
{
const
isEditing
=
!!
this
.
editingTask
if
(
isEditing
)
{
this
.
updateTaskDefinition
(
item
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
res
.
msg
)
this
.
_onUpdate
()
this
.
closeTaskDrawer
()
})
.
catch
((
e
)
=>
{
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
else
{
this
.
genTaskCodeList
({
genNum
:
1
})
.
then
((
res
)
=>
{
const
[
code
]
=
res
return
code
})
.
then
((
code
)
=>
{
return
this
.
saveTaskDefinition
({
taskDefinitionJson
:
[
{
...
item
,
code
}
]
})
})
.
then
((
res
)
=>
{
this
.
$message
.
success
(
res
.
msg
)
this
.
_onUpdate
()
this
.
closeTaskDrawer
()
})
.
catch
((
e
)
=>
{
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
},
createTask
()
{
this
.
editingTask
=
null
this
.
nodeData
.
taskType
=
DEFAULT_NODE_DATA
.
taskType
this
.
showTaskDrawer
()
},
editTask
(
task
)
{
this
.
editingTask
=
task
this
.
nodeData
.
id
=
task
.
code
this
.
nodeData
.
taskType
=
task
.
taskType
this
.
showTaskDrawer
()
},
viewTaskDetail
(
task
)
{
this
.
setIsDetails
(
true
)
this
.
editTask
(
task
)
},
/**
* pageNo
*/
_page
(
val
)
{
this
.
searchParams
.
pageNo
=
val
},
_pageSize
(
val
)
{
this
.
searchParams
.
pageSize
=
val
},
/**
* conditions
*/
_onConditions
(
o
)
{
this
.
searchParams
.
searchVal
=
o
.
searchVal
this
.
searchParams
.
taskType
=
o
.
taskType
this
.
searchParams
.
pageNo
=
1
},
/**
* get task definition list
*/
_getList
(
flag
)
{
this
.
isLoading
=
!
flag
this
.
getTaskDefinitionsList
(
this
.
searchParams
)
.
then
((
res
)
=>
{
if
(
this
.
searchParams
.
pageNo
>
1
&&
res
.
totalList
.
length
===
0
)
{
this
.
searchParams
.
pageNo
=
this
.
searchParams
.
pageNo
-
1
}
else
{
this
.
tasksList
=
[]
this
.
tasksList
=
res
.
totalList
this
.
total
=
res
.
total
this
.
isLoading
=
false
}
})
.
catch
((
e
)
=>
{
this
.
isLoading
=
false
})
},
/**
* update task dataList
*/
_onUpdate
()
{
this
.
_debounceGET
(
'
false
'
)
},
/**
* change form modal task type
*/
changeTaskType
(
value
)
{
this
.
nodeData
.
taskType
=
value
}
},
created
()
{
this
.
isLoading
=
true
// Initialization parameters
this
.
resetParams
()
// Promise Get node needs data
Promise
.
all
([
// get process definition
this
.
getProcessList
(),
// get project
this
.
getProjectList
(),
// get jar
this
.
getResourcesListJar
(),
// get resource
this
.
getResourcesList
(),
// get jar
this
.
getResourcesListJar
(),
// get worker group list
this
.
getWorkerGroupsAll
(),
// get alarm group list
this
.
getAlarmGroupsAll
(),
this
.
getTenantList
()
])
.
then
((
data
)
=>
{
this
.
isLoading
=
false
})
.
catch
(()
=>
{
this
.
isLoading
=
false
})
},
mounted
()
{},
components
:
{
mListConstruction
,
mConditions
,
mList
,
mNoData
,
mSpin
,
mFormModel
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.task-definition
{
.taskGroupBtn
{
width
:
300px
;
}
}
</
style
>
dolphinscheduler-ui/src/js/module/components/nav/nav.vue
浏览文件 @
37ba1eb5
...
...
@@ -29,7 +29,7 @@
</div>
<div
class=
"clearfix list"
>
<div
class=
"nav-links"
>
<router-link
:to=
"
{ path: '/projects'}" tag="a" active-class="active">
<router-link
:to=
"
{ path: '/projects'}" tag="a" active-class="active"
id="project-tab"
>
<span><em
class=
"ansiconfont el-icon-tickets"
></em>
{{
$t
(
'
Project Manage
'
)
}}
</span><strong></strong>
</router-link>
</div>
...
...
@@ -57,7 +57,7 @@
</div>
<div
class=
"clearfix list"
>
<div
class=
"nav-links"
>
<router-link
:to=
"
{ path: '/security'}" tag="a" active-class="active" v-ps="['ADMIN_USER']">
<router-link
:to=
"
{ path: '/security'}" tag="a" active-class="active" v-ps="['ADMIN_USER']"
id="security-tab"
>
<span><em
class=
"ansfont ri-shield-check-line"
></em>
{{
$t
(
'
Security
'
)
}}
</span><strong></strong>
</router-link>
</div>
...
...
dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
浏览文件 @
37ba1eb5
...
...
@@ -51,13 +51,15 @@ const menu = {
name
:
`
${
i18n
.
$t
(
'
Process definition
'
)}
`
,
path
:
'
definition
'
,
id
:
0
,
enabled
:
true
enabled
:
true
,
classNames
:
'
process-definition
'
},
{
name
:
`
${
i18n
.
$t
(
'
Process Instance
'
)}
`
,
path
:
'
instance
'
,
id
:
1
,
enabled
:
true
enabled
:
true
,
classNames
:
'
process-instance
'
},
{
name
:
`
${
i18n
.
$t
(
'
Task Instance
'
)}
`
,
...
...
@@ -95,7 +97,8 @@ const menu = {
isOpen
:
true
,
enabled
:
true
,
icon
:
'
el-icon-user-solid
'
,
children
:
[]
children
:
[],
classNames
:
'
tenant-manage
'
},
{
name
:
`
${
i18n
.
$t
(
'
User Manage
'
)}
`
,
...
...
dolphinscheduler-ui/src/js/module/components/secondaryMenu/secondaryMenu.vue
浏览文件 @
37ba1eb5
...
...
@@ -23,7 +23,7 @@
<div
class=
"leven-1"
v-for=
"(item,$index) in menuList"
:key=
"$index"
>
<div
v-if=
"item.enabled"
>
<template
v-if=
"item.path"
>
<router-link
:to=
"
{ name: item.path}">
<router-link
:to=
"
{ name: item.path}"
:class="item.classNames"
>
<div
class=
"name"
@
click=
"_toggleSubMenu(item)"
>
<a
href=
"javascript:"
>
<em
class=
"fa icon"
:class=
"item.icon"
></em>
...
...
@@ -44,7 +44,7 @@
<
/template
>
<
ul
v
-
if
=
"
item.isOpen && item.children.length
"
>
<
template
v
-
for
=
"
(el,index) in item.children
"
>
<
router
-
link
:
to
=
"
{ name: el.path
}
"
tag
=
"
li
"
active
-
class
=
"
active
"
v
-
if
=
"
el.enabled
"
:
key
=
"
index
"
>
<
router
-
link
:
to
=
"
{ name: el.path
}
"
tag
=
"
li
"
active
-
class
=
"
active
"
v
-
if
=
"
el.enabled
"
:
key
=
"
index
"
:
class
=
"
el.classNames
"
>
<
span
>
{{
el
.
name
}}
<
/span
>
<
/router-link
>
<
/template
>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录