Browse Source

[E2E] Add new test of token management (#8161)

3.0.0/version-upgrade
QuakeWang 3 years ago committed by GitHub
parent
commit
63f7efa993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/e2e.yml
  2. 99
      dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TokenE2ETest.java
  3. 18
      dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java
  4. 138
      dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TokenPage.java
  5. 4
      dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/_source/createToken.vue
  6. 10
      dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/_source/list.vue
  7. 3
      dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/index.vue
  8. 3
      dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js

2
.github/workflows/e2e.yml

@ -81,6 +81,8 @@ jobs:
class: org.apache.dolphinscheduler.e2e.cases.QueueE2ETest class: org.apache.dolphinscheduler.e2e.cases.QueueE2ETest
- name: Environment - name: Environment
class: org.apache.dolphinscheduler.e2e.cases.EnvironmentE2ETest class: org.apache.dolphinscheduler.e2e.cases.EnvironmentE2ETest
- name: Token
class: org.apache.dolphinscheduler.e2e.cases.TokenE2ETest
- name: Workflow - name: Workflow
class: org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest class: org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest
- name: FileManage - name: FileManage

99
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/TokenE2ETest.java

@ -0,0 +1,99 @@
/*
* 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.TokenPage;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
public class TokenE2ETest {
private static final String userName = "admin";
private static RemoteWebDriver browser;
@BeforeAll
public static void setup() {
new LoginPage(browser)
.login("admin", "dolphinscheduler123")
.goToNav(SecurityPage.class)
.goToTab(TokenPage.class)
;
}
@Test
@Order(10)
void testCreateToken() {
final TokenPage page = new TokenPage(browser);
page.create();
await().untilAsserted(() -> {
browser.navigate().refresh();
assertThat(page.tokenList())
.as("Token list should contain newly-created token")
.extracting(WebElement::getText)
.anyMatch(it -> it.contains(userName));
});
}
@Test
@Order(30)
void testEditToken() {
final TokenPage page = new TokenPage(browser);
String oldToken = page.getToken(userName);
page.update(userName);
await().untilAsserted(() -> {
browser.navigate().refresh();
assertThat(page.tokenList())
.as("Token list should contain newly-modified token")
.extracting(WebElement::getText)
.isNotEqualTo(oldToken);
});
}
@Test
@Order(40)
void testDeleteToken() {
final TokenPage page = new TokenPage(browser);
page.delete(userName);
await().untilAsserted(() -> {
browser.navigate().refresh();
assertThat(page.tokenList())
.noneMatch(it -> it.getText().contains(userName));
});
}
}

18
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/SecurityPage.java

@ -17,6 +17,7 @@
* under the License. * under the License.
* *
*/ */
package org.apache.dolphinscheduler.e2e.pages.security; package org.apache.dolphinscheduler.e2e.pages.security;
import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage; import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage;
@ -26,11 +27,11 @@ import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement; import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindBy;
import lombok.Getter;
import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.ui.WebDriverWait;
import lombok.Getter;
@Getter @Getter
public class SecurityPage extends NavBarPage implements NavBarItem { public class SecurityPage extends NavBarPage implements NavBarItem {
@FindBy(className = "tab-tenant-manage") @FindBy(className = "tab-tenant-manage")
@ -48,6 +49,9 @@ public class SecurityPage extends NavBarPage implements NavBarItem {
@FindBy(className = "tab-environment-manage") @FindBy(className = "tab-environment-manage")
private WebElement menuEnvironmentManage; private WebElement menuEnvironmentManage;
@FindBy(className = "tab-token-manage")
private WebElement menuTokenManage;
public SecurityPage(RemoteWebDriver driver) { public SecurityPage(RemoteWebDriver driver) {
super(driver); super(driver);
} }
@ -56,20 +60,20 @@ public class SecurityPage extends NavBarPage implements NavBarItem {
if (tab == TenantPage.class) { if (tab == TenantPage.class) {
WebElement menuTenantManageElement = new WebDriverWait(driver, 60) WebElement menuTenantManageElement = new WebDriverWait(driver, 60)
.until(ExpectedConditions.elementToBeClickable(menuTenantManage)); .until(ExpectedConditions.elementToBeClickable(menuTenantManage));
((JavascriptExecutor)driver).executeScript("arguments[0].click();", menuTenantManageElement); ((JavascriptExecutor) driver).executeScript("arguments[0].click();", menuTenantManageElement);
return tab.cast(new TenantPage(driver)); return tab.cast(new TenantPage(driver));
} }
if (tab == UserPage.class) { if (tab == UserPage.class) {
WebElement menUserManageElement = new WebDriverWait(driver, 60) WebElement menUserManageElement = new WebDriverWait(driver, 60)
.until(ExpectedConditions.elementToBeClickable(menUserManage)); .until(ExpectedConditions.elementToBeClickable(menUserManage));
((JavascriptExecutor)driver).executeScript("arguments[0].click();", menUserManageElement); ((JavascriptExecutor) driver).executeScript("arguments[0].click();", menUserManageElement);
new WebDriverWait(driver, 25).until(ExpectedConditions.urlContains("/#/security/users")); new WebDriverWait(driver, 25).until(ExpectedConditions.urlContains("/#/security/users"));
return tab.cast(new UserPage(driver)); return tab.cast(new UserPage(driver));
} }
if (tab == WorkerGroupPage.class) { if (tab == WorkerGroupPage.class) {
WebElement menWorkerGroupManageElement = new WebDriverWait(driver, 60) WebElement menWorkerGroupManageElement = new WebDriverWait(driver, 60)
.until(ExpectedConditions.elementToBeClickable(menWorkerGroupManage)); .until(ExpectedConditions.elementToBeClickable(menWorkerGroupManage));
((JavascriptExecutor)driver).executeScript("arguments[0].click();", menWorkerGroupManageElement); ((JavascriptExecutor) driver).executeScript("arguments[0].click();", menWorkerGroupManageElement);
return tab.cast(new WorkerGroupPage(driver)); return tab.cast(new WorkerGroupPage(driver));
} }
if (tab == QueuePage.class) { if (tab == QueuePage.class) {
@ -80,6 +84,10 @@ public class SecurityPage extends NavBarPage implements NavBarItem {
menuEnvironmentManage().click(); menuEnvironmentManage().click();
return tab.cast(new EnvironmentPage(driver)); return tab.cast(new EnvironmentPage(driver));
} }
if (tab == TokenPage.class) {
menuTokenManage().click();
return tab.cast(new TokenPage(driver));
}
throw new UnsupportedOperationException("Unknown tab: " + tab.getName()); throw new UnsupportedOperationException("Unknown tab: " + tab.getName());
} }

138
dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/pages/security/TokenPage.java

@ -0,0 +1,138 @@
/*
* 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 org.apache.dolphinscheduler.e2e.pages.security.SecurityPage.Tab;
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;
import com.google.common.base.Strings;
@Getter
public final class TokenPage extends NavBarPage implements Tab {
@FindBy(id = "btnCreateToken")
private WebElement buttonCreateToken;
@FindBy(className = "items")
private List<WebElement> tokenList;
@FindBys({
@FindBy(className = "el-popconfirm"),
@FindBy(className = "el-button--primary"),
})
private List<WebElement> buttonConfirm;
@FindBy(className = "userName")
private List<WebElement> userName;
@FindBy(className = "token")
private List<WebElement> token;
private final TokenForm createTokenForm = new TokenForm();
private final TokenForm editTokenForm = new TokenForm();
public TokenPage(RemoteWebDriver driver) {
super(driver);
}
public TokenPage create() {
buttonCreateToken().click();
createTokenForm().buttonGenerateToken().click();
new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(createTokenForm.buttonGenerateToken));
createTokenForm().buttonSubmit().click();
return this;
}
public TokenPage update(String userName) {
tokenList().stream()
.filter(it -> it.findElement(By.className("userName")).getAttribute("innerHTML").contains(userName))
.flatMap(it -> it.findElements(By.className("edit")).stream())
.filter(WebElement::isDisplayed)
.findFirst()
.orElseThrow(() -> new RuntimeException("No edit button in token list"))
.click();
TokenForm editTokenForm = new TokenForm();
editTokenForm.buttonGenerateToken().click();
new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(createTokenForm.buttonGenerateToken));
editTokenForm.buttonSubmit().click();
return this;
}
public String getToken(String userName) {
return tokenList().stream()
.filter(it -> it.findElement(By.className("userName")).getAttribute("innerHTML").contains(userName))
.flatMap(it -> it.findElements(By.className("token")).stream())
.filter(it -> !Strings.isNullOrEmpty(it.getAttribute("innerHTML")))
.map(it -> it.getAttribute("innerHTML"))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No token for such user: " + userName));
}
public TokenPage delete(String userName) {
tokenList()
.stream()
.filter(it -> it.getText().contains(userName))
.flatMap(it -> it.findElements(By.className("delete")).stream())
.filter(WebElement::isDisplayed)
.findFirst()
.orElseThrow(() -> new RuntimeException("No delete button in token list"))
.click();
buttonConfirm()
.stream()
.filter(WebElement::isDisplayed)
.findFirst()
.orElseThrow(() -> new RuntimeException("No confirm button when deleting"))
.click();
return this;
}
@Getter
public class TokenForm {
TokenForm() {
PageFactory.initElements(driver, this);
}
@FindBy(id = "btnGenerateToken")
private WebElement buttonGenerateToken;
@FindBy(id = "btnSubmit")
private WebElement buttonSubmit;
@FindBy(id = "btnCancel")
private WebElement buttonCancel;
}
}

4
dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/_source/createToken.vue

@ -16,6 +16,8 @@
*/ */
<template> <template>
<m-popover <m-popover
okId="btnSubmit"
cancelId="btnCancel"
ref="popover" ref="popover"
:ok-text="item ? $t('Edit') : $t('Submit')" :ok-text="item ? $t('Edit') : $t('Submit')"
@ok="_ok" @ok="_ok"
@ -59,7 +61,7 @@
v-model="token" v-model="token"
:placeholder="$t('Please enter token')"> :placeholder="$t('Please enter token')">
</el-input> </el-input>
<el-button size="small" @click="_generateToken" :loading="tokenLoading">{{$t('Generate token')}}</el-button> <el-button id="btnGenerateToken" size="small" @click="_generateToken" :loading="tokenLoading">{{$t('Generate token')}}</el-button>
</template> </template>
</m-list-box-f> </m-list-box-f>
</div> </div>

10
dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/_source/list.vue

@ -17,10 +17,10 @@
<template> <template>
<div class="list-model"> <div class="list-model">
<div class="table-box"> <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 type="index" :label="$t('#')" width="50"></el-table-column>
<el-table-column prop="userName" :label="$t('User')"></el-table-column> <el-table-column prop="userName" :label="$t('User')" class-name="userName"></el-table-column>
<el-table-column prop="token" label="Token" width="300"></el-table-column> <el-table-column prop="token" label="Token" width="300" class-name="token"></el-table-column>
<el-table-column :label="$t('Expiration time')" min-width="120"> <el-table-column :label="$t('Expiration time')" min-width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{scope.row.expireTime | formatDate}}</span> <span>{{scope.row.expireTime | formatDate}}</span>
@ -39,7 +39,7 @@
<el-table-column :label="$t('Operation')" width="130"> <el-table-column :label="$t('Operation')" width="130">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip :content="$t('Edit')" placement="top" :enterable="false"> <el-tooltip :content="$t('Edit')" placement="top" :enterable="false">
<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>
<el-tooltip :content="$t('Delete')" placement="top" :enterable="false"> <el-tooltip :content="$t('Delete')" placement="top" :enterable="false">
<el-popconfirm <el-popconfirm
@ -50,7 +50,7 @@
:title="$t('Delete?')" :title="$t('Delete?')"
@onConfirm="_delete(scope.row,scope.row.id)" @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-popconfirm>
</el-tooltip> </el-tooltip>
</template> </template>

3
dolphinscheduler-ui/src/js/conf/home/pages/user/pages/token/index.vue

@ -19,8 +19,9 @@
<template slot="conditions"> <template slot="conditions">
<m-conditions @on-conditions="_onConditions"> <m-conditions @on-conditions="_onConditions">
<template slot="button-group"> <template slot="button-group">
<el-button size="mini" @click="_create('')">{{$t('Create token')}}</el-button> <el-button id="btnCreateToken" size="mini" @click="_create('')">{{$t('Create token')}}</el-button>
<el-dialog <el-dialog
id="dialogGenerateToken"
:title="item ? $t('Edit token') : $t('Create token')" :title="item ? $t('Edit token') : $t('Create token')"
v-if="createTokenDialog" v-if="createTokenDialog"
:visible.sync="createTokenDialog" :visible.sync="createTokenDialog"

3
dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js

@ -182,9 +182,10 @@ const menu = {
id: 2, id: 2,
path: 'token-manage', path: 'token-manage',
isOpen: true, isOpen: true,
enabled: true,
icon: 'el-icon-document', icon: 'el-icon-document',
children: [], children: [],
enabled: true classNames: 'tab-token-manage'
} }
], ],
resource: [ resource: [

Loading…
Cancel
Save