Add E2E tests for some core features (#7025)
parent
7b20f4c239
commit
37ba1eb5ad
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.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.remote.RemoteWebDriver;
|
||||
|
||||
@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
|
||||
class TenantE2ETest {
|
||||
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)
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
void testCreateTenant() {
|
||||
new TenantPage(browser).create(tenant);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(20)
|
||||
void testCreateDuplicateTenant() {
|
||||
final var page = new TenantPage(browser);
|
||||
|
||||
page.create(tenant);
|
||||
|
||||
await().untilAsserted(() ->
|
||||
assertThat(browser.findElement(By.tagName("body")).getText())
|
||||
.contains("already exists")
|
||||
);
|
||||
|
||||
page.createTenantForm().buttonCancel().click();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(30)
|
||||
void testDeleteTenant() {
|
||||
final var page = new TenantPage(browser);
|
||||
page.delete(tenant);
|
||||
|
||||
await().untilAsserted(() -> {
|
||||
browser.navigate().refresh();
|
||||
assertThat(
|
||||
page.tenantList()
|
||||
).noneMatch(
|
||||
it -> it.getText().contains(tenant)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* 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.security;
|
||||
|
||||
|
||||
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.TenantPage;
|
||||
|
||||
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")
|
||||
class TenantE2ETest {
|
||||
private RemoteWebDriver browser;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void testLogin() {
|
||||
final LoginPage page = new LoginPage(browser);
|
||||
page.inputUsername().sendKeys("admin");
|
||||
page.inputPassword().sendKeys("dolphinscheduler123");
|
||||
page.buttonLogin().click();
|
||||
}
|
||||
|
||||
@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)));
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
await().untilAsserted(() -> assertThat(browser.findElementByTagName("body")
|
||||
.getText().contains("already exists"))
|
||||
.as("Should fail when creating a duplicate tenant")
|
||||
.isTrue());
|
||||
|
||||
page.createTenantForm().buttonCancel().click();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
PageFactory.initElements(driver, this);
|
||||
@SneakyThrows
|
||||
public TenantPage login(String username, String password) {
|
||||
inputUsername().sendKeys(username);
|
||||
inputPassword().sendKeys(password);
|
||||
buttonLogin().click();
|
||||
|
||||
new WebDriverWait(driver(), 10)
|
||||
.until(ExpectedConditions.urlContains("/#/security"));
|
||||
|
||||
return new TenantPage(driver);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.dolphinscheduler.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.dolphinscheduler.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 {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.dolphinscheduler.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 {
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
PageFactory.initElements(driver, this);
|
||||
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();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
|
@ -23,6 +23,7 @@ services:
|
|||
command: [ standalone-server ]
|
||||
environment:
|
||||
DATABASE_TYPE: h2
|
||||
WORKER_TENANT_AUTO_CREATE: 'true'
|
||||
expose:
|
||||
- 12345
|
||||
networks:
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
<name>${project.artifactId}</name>
|
||||
|
||||
<properties>
|
||||
<node.version>v12.20.2</node.version>
|
||||
<node.version>v14.15.1</node.version>
|
||||
<npm.version>6.14.11</npm.version>
|
||||
<sonar.sources>src</sonar.sources>
|
||||
</properties>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@
|
|||
type="primary"
|
||||
size="mini"
|
||||
@click="saveProcess"
|
||||
id="button-save"
|
||||
>{{ $t("Save") }}</el-button
|
||||
>
|
||||
<el-button
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,307 +1,307 @@
|
|||
/*
|
||||
* 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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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')}`,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue