This is an automated email from the ASF dual-hosted git repository. kezhenxu94 pushed a commit to branch user_e2e in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git
commit bc563fdaaacc719b5e1d305f22418e4dff948842 Author: chenxingchun <[email protected]> AuthorDate: Tue Dec 21 16:41:59 2021 +0800 Add E2E case for user manage --- .github/workflows/e2e.yml | 2 + .../dolphinscheduler/e2e/cases/TenantE2ETest.java | 9 +- .../dolphinscheduler/e2e/cases/UserE2ETest.java | 139 +++++++++++++++++++ .../e2e/pages/security/SecurityPage.java | 8 ++ .../e2e/pages/security/TenantPage.java | 8 -- .../e2e/pages/security/UserPage.java | 148 +++++++++++++++++++++ .../e2e/core/DolphinSchedulerExtension.java | 8 +- .../security/pages/users/_source/createUser.vue | 6 + .../pages/security/pages/users/_source/list.vue | 8 +- .../conf/home/pages/security/pages/users/index.vue | 2 +- .../components/secondaryMenu/_source/menu.js | 3 +- 11 files changed, 322 insertions(+), 19 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 87828b7..63df024 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -42,6 +42,8 @@ jobs: case: - name: Tenant class: org.apache.dolphinscheduler.e2e.cases.TenantE2ETest + - name: User + class: org.apache.dolphinscheduler.e2e.cases.UserE2ETest - name: Project class: org.apache.dolphinscheduler.e2e.cases.ProjectE2ETest - name: Workflow diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java index 4938a8b..e5c4ef5 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TenantE2ETest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; @DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml") @@ -52,7 +53,13 @@ class TenantE2ETest { @Test @Order(10) void testCreateTenant() { - new TenantPage(browser).create(tenant); + final TenantPage page = new TenantPage(browser); + page.create(tenant); + + await().untilAsserted(() -> assertThat(page.tenantList()) + .as("Tenant list should contain newly-created tenant") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(tenant))); } @Test diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java new file mode 100644 index 0000000..8b43e1b --- /dev/null +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/UserE2ETest.java @@ -0,0 +1,139 @@ +/* + * 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.apache.dolphinscheduler.e2e.pages.security.UserPage; + +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.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebDriver; + +@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml") +class UserE2ETest { + private static final String tenant = System.getProperty("user.name"); + private static final String user = "test_user"; + private static final String password = "test_user123"; + private static final String email = "[email protected]"; + private static final String phone = "15800000000"; + + private static final String editUser = "edit_test_user"; + private static final String editPassword = "edit_test_user123"; + private static final String editEmail = "[email protected]"; + private static final String editPhone = "15800000001"; + + private static RemoteWebDriver browser; + + @BeforeAll + public static void setup() { + new LoginPage(browser) + .login("admin", "dolphinscheduler123") + .goToNav(SecurityPage.class) + .goToTab(TenantPage.class) + .create(tenant) + .goToNav(SecurityPage.class) + .goToTab(UserPage.class); + } + + @AfterAll + public static void cleanup() { + new SecurityPage(browser) + .goToTab(TenantPage.class) + .delete(tenant) + ; + } + + @Test + @Order(1) + void testCreateUser() { + final UserPage page = new UserPage(browser); + + page.create(user, password, email, phone); + + await().untilAsserted(() -> { + browser.navigate().refresh(); + + assertThat(page.userList()) + .as("User list should contain newly-created user") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(user)); + }); + } + + @Test + @Order(20) + void testCreateDuplicateUser() { + final UserPage page = new UserPage(browser); + + page.create(user, password, email, phone); + + await().untilAsserted(() -> + assertThat(browser.findElement(By.tagName("body")).getText()) + .contains("already exists") + ); + + page.createUserForm().buttonCancel().click(); + } + + @Test + @Order(30) + void testEditUser() { + final UserPage page = new UserPage(browser); + page.update(user, editUser, editPassword, editEmail, editPhone); + + await().untilAsserted(() -> { + browser.navigate().refresh(); + assertThat(page.userList()) + .as("User list should contain newly-modified User") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(editUser)); + }); + } + + + @Test + @Order(40) + void testDeleteUser() { + final UserPage page = new UserPage(browser); + + page.delete(editUser); + + await().untilAsserted(() -> { + browser.navigate().refresh(); + + assertThat( + page.userList() + ).noneMatch( + it -> it.getText().contains(user) || it.getText().contains(editUser) + ); + }); + } +} diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java index 36f960c..920d171 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java @@ -33,6 +33,10 @@ public class SecurityPage extends NavBarPage implements NavBarItem { @FindBy(className = "tab-tenant-manage") private WebElement menuTenantManage; + @FindBy(className = "tab-user-manage") + private WebElement menUserManage; + + public SecurityPage(RemoteWebDriver driver) { super(driver); } @@ -42,6 +46,10 @@ public class SecurityPage extends NavBarPage implements NavBarItem { menuTenantManage().click(); return tab.cast(new TenantPage(driver)); } + if (tab == UserPage.class) { + menUserManage().click(); + return tab.cast(new UserPage(driver)); + } throw new UnsupportedOperationException("Unknown tab: " + tab.getName()); } diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TenantPage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TenantPage.java index 6fc70c6..eba9cce 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TenantPage.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TenantPage.java @@ -19,9 +19,6 @@ 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; @@ -67,11 +64,6 @@ public final class TenantPage extends NavBarPage implements SecurityPage.Tab { 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; } diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java new file mode 100644 index 0000000..b602404 --- /dev/null +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/UserPage.java @@ -0,0 +1,148 @@ +/* + * 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.pages.security; + +import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage; + +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 lombok.Getter; + +@Getter +public final class UserPage extends NavBarPage implements SecurityPage.Tab { + @FindBy(id = "btnCreateUser") + private WebElement buttonCreateUser; + + @FindBy(className = "items") + private List<WebElement> userList; + + @FindBys({ + @FindBy(className = "el-popconfirm"), + @FindBy(className = "el-button--primary"), + }) + private List<WebElement> buttonConfirm; + + private final UserForm createUserForm = new UserForm(); + private final UserForm editUserForm = new UserForm(); + + + public UserPage(RemoteWebDriver driver) { + super(driver); + } + + public UserPage create(String user, String password, String email, String phone) { + buttonCreateUser().click(); + + createUserForm().inputUserName().sendKeys(user); + createUserForm().inputUserPassword().sendKeys(password); + createUserForm().inputEmail().sendKeys(email); + createUserForm().inputPhone().sendKeys(phone); + createUserForm().buttonSubmit().click(); + + return this; + } + + public UserPage update(String user, String editUser, String editPassword, String editEmail, String editPhone) { + userList() + .stream() + .filter(it -> it.findElement(By.className("name")).getAttribute("innerHTML").contains(user)) + .flatMap(it -> it.findElements(By.className("edit")).stream()) + .filter(WebElement::isDisplayed) + .findFirst() + .orElseThrow(() -> new RuntimeException("No edit button in user list")) + .click(); + + editUserForm().inputUserName().clear(); + editUserForm().inputUserName().sendKeys(editUser); + editUserForm().inputUserPassword().clear(); + editUserForm().inputUserPassword().sendKeys(editPassword); + editUserForm().inputEmail().clear(); + editUserForm().inputEmail().sendKeys(editEmail); + editUserForm().inputPhone().clear(); + editUserForm().inputPhone().sendKeys(editPhone); + editUserForm().buttonSubmit().click(); + + return this; + } + + public UserPage delete(String user) { + userList() + .stream() + .filter(it -> it.findElement(By.className("name")).getAttribute("innerHTML").contains(user)) + .flatMap(it -> it.findElements(By.className("delete")).stream()) + .filter(WebElement::isDisplayed) + .findFirst() + .orElseThrow(() -> new RuntimeException("No delete button in user list")) + .click(); + + buttonConfirm() + .stream() + .filter(WebElement::isDisplayed) + .findFirst() + .orElseThrow(() -> new RuntimeException("No confirm button when deleting")) + .click(); + + return this; + } + + @Getter + public class UserForm { + UserForm() { + PageFactory.initElements(driver, this); + } + + @FindBy(id = "inputUserName") + private WebElement inputUserName; + + @FindBy(id = "inputUserPassword") + private WebElement inputUserPassword; + + @FindBy(id = "selectTenant") + private WebElement selectTenant; + + @FindBy(id = "selectQueue") + private WebElement selectQueue; + + @FindBy(id = "inputEmail") + private WebElement inputEmail; + + @FindBy(id = "inputPhone") + private WebElement inputPhone; + + @FindBy(id = "radioStateEnable") + private WebElement radioStateEnable; + + @FindBy(id = "radioStateDisable") + private WebElement radioStateDisable; + + @FindBy(id = "btnSubmit") + private WebElement buttonSubmit; + + @FindBy(id = "btnCancel") + private WebElement buttonCancel; + } +} diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java index d6b1886..8a28c60 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java @@ -73,8 +73,8 @@ final class DolphinSchedulerExtension @Override @SuppressWarnings("UnstableApiUsage") public void beforeAll(ExtensionContext context) throws IOException { - Awaitility.setDefaultTimeout(Duration.ofSeconds(10)); - Awaitility.setDefaultPollInterval(Duration.ofSeconds(1)); + Awaitility.setDefaultTimeout(Duration.ofSeconds(30)); + Awaitility.setDefaultPollInterval(Duration.ofSeconds(30)); Network network = null; HostAndPort address = null; @@ -127,8 +127,8 @@ final class DolphinSchedulerExtension driver = browser.getWebDriver(); driver.manage().timeouts() - .implicitlyWait(5, TimeUnit.SECONDS) - .pageLoadTimeout(5, TimeUnit.SECONDS); + .implicitlyWait(30, TimeUnit.SECONDS) + .pageLoadTimeout(30, TimeUnit.SECONDS); driver.manage().window() .maximize(); if (address == null) { diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/createUser.vue b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/createUser.vue index 88f69fe..8f3adc0 100644 --- a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/createUser.vue +++ b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/createUser.vue @@ -16,6 +16,8 @@ */ <template> <m-popover + okId="btnSubmit" + cancelId="btnCancel" ref="popover" :ok-text="item ? $t('Edit') : $t('Submit')" @ok="_ok" @@ -26,6 +28,7 @@ <template slot="name"><strong>*</strong>{{$t('User Name')}}</template> <template slot="content"> <el-input + id="inputUserName" type="input" v-model="userName" maxlength="60" @@ -38,6 +41,7 @@ <template slot="name"><strong>*</strong>{{$t('Password')}}</template> <template slot="content"> <el-input + id="inputUserPassword" type="password" v-model="userPassword" size="small" @@ -79,6 +83,7 @@ <template slot="name"><strong>*</strong>{{$t('Email')}}</template> <template slot="content"> <el-input + id="inputEmail" type="input" v-model="email" size="small" @@ -90,6 +95,7 @@ <template slot="name">{{$t('Phone')}}</template> <template slot="content"> <el-input + id="inputPhone" type="input" v-model="phone" size="small" diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue index 615cf97..212060b 100644 --- a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue +++ b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue @@ -17,9 +17,9 @@ <template> <div class="list-model user-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="items"> <el-table-column type="index" :label="$t('#')" width="50"></el-table-column> - <el-table-column prop="userName" :label="$t('User Name')"></el-table-column> + <el-table-column prop="userName" :label="$t('User Name')" class-name="name"></el-table-column> <el-table-column :label="$t('User Type')" width="80"> <template slot-scope="scope"> {{scope.row.userType === 'GENERAL_USER' ? `${$t('Ordinary users')}` : `${$t('Administrator')}`}} @@ -62,7 +62,7 @@ </el-dropdown> </el-tooltip> <el-tooltip :content="$t('Edit')" placement="top"> - <el-button type="primary" size="mini" icon="el-icon-edit-outline" @click="_edit(scope.row)" circle></el-button> + <el-button type="primary" size="mini" icon="el-icon-edit-outline" @click="_edit(scope.row)" circle class="edit"></el-button> </el-tooltip> <el-tooltip :content="$t('Delete')" placement="top"> <el-popconfirm @@ -73,7 +73,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> diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/index.vue b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/index.vue index 58bcb86..44f8adc 100644 --- a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/index.vue +++ b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/index.vue @@ -19,7 +19,7 @@ <template slot="conditions"> <m-conditions @on-conditions="_onConditions"> <template slot="button-group" v-if="userList.length"> - <el-button size="mini" @click="_create('')">{{$t('Create User')}}</el-button> + <el-button id="btnCreateUser" size="mini" @click="_create('')">{{$t('Create User')}}</el-button> <el-dialog :title="item ? $t('Edit User') : $t('Create User')" v-if="createUserDialog" diff --git a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js index 3eb7c9f..09395ca 100644 --- a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js +++ b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js @@ -107,7 +107,8 @@ const menu = { isOpen: true, enabled: true, icon: 'el-icon-user-solid', - children: [] + children: [], + classNames: 'tab-user-manage' }, { name: `${i18n.$t('Warning group manage')}`,
