Skip to content

Commit

Permalink
[Feature] Introduce alarm e2e test (#3899)
Browse files Browse the repository at this point in the history
* [Future] Introduce alarm e2e test

* [Feature] implement alarm#edit and improve code style

* [Feature] improve by dynamic form

* [Feature] Multi alert type test

* Improve AlarmTest#create

---------

Co-authored-by: benjobs <[email protected]>
  • Loading branch information
zzzk1 and wolfboys authored Jul 23, 2024
1 parent b39d7e2 commit 8d9d41e
Show file tree
Hide file tree
Showing 12 changed files with 749 additions and 3 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ jobs:
strategy:
matrix:
case:
- name: ExternalLinkPageTest
class: org.apache.streampark.e2e.cases.ExternalLinkPageTest
- name: AlarmTest
class: org.apache.streampark.e2e.cases.AlarmTest
- name: ExternalLinkTest
class: org.apache.streampark.e2e.cases.ExternalLinkTest
- name: YarnQueueTest
class: org.apache.streampark.e2e.cases.YarnQueueTest
- name: TokenManagementTest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* 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.streampark.e2e.cases;

import org.apache.streampark.e2e.core.StreamPark;
import org.apache.streampark.e2e.pages.LoginPage;
import org.apache.streampark.e2e.pages.setting.SettingPage;
import org.apache.streampark.e2e.pages.setting.alarm.AlarmPage;
import org.apache.streampark.e2e.pages.setting.alarm.AlertTypeDetailForm;
import org.apache.streampark.e2e.pages.setting.alarm.DingTalkAlertForm;
import org.apache.streampark.e2e.pages.setting.alarm.EmailAlertForm;
import org.apache.streampark.e2e.pages.setting.alarm.LarkAlertForm;
import org.apache.streampark.e2e.pages.setting.alarm.WeChatAlertForm;

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;
import org.testcontainers.shaded.org.awaitility.Awaitility;

import static org.assertj.core.api.Assertions.assertThat;

@StreamPark(composeFiles = "docker/basic/docker-compose.yaml")
public class AlarmTest {

private static RemoteWebDriver browser;

private static final String userName = "admin";

private static final String password = "streampark";

private static final String teamName = "default";

private static final String newEmail = "[email protected]";

private static final String newAlarmName = "new_alarm";

private static final String editAlarmName = "edit_alarm";

@BeforeAll
public static void setup() {
new LoginPage(browser)
.login(userName, password, teamName)
.goToNav(SettingPage.class)
.goToTab(AlarmPage.class);
}

@Test
@Order(10)
public void testCreateAlarm() {
final AlarmPage alarmPage = new AlarmPage(browser);

final String dingTalkURL = "";
final String dingTalkToken = "dingTalkToken";
final String dingTalkSecretToken = "dingTalkSecretToken";
final String dingTalkReceiveUser = "dingTalkUser";

final String wechatToken = "wechatToken";

final String larkToken = "larkToken";
final String larkSecretToken = "larkSecretToken";

AlertTypeDetailForm alertTypeDetailForm = alarmPage.createAlarm();
alertTypeDetailForm
.<EmailAlertForm>addAlertType(AlertTypeDetailForm.AlertTypeEnum.EMAIL)
.email(newEmail)
.alertName(newAlarmName);
alertTypeDetailForm
.<DingTalkAlertForm>addAlertType(AlertTypeDetailForm.AlertTypeEnum.DINGTALK)
.url(dingTalkURL)
.token(dingTalkToken)
.secretEnable()
.secretToken(dingTalkSecretToken)
.effectToAllUsers()
.receiveUser(dingTalkReceiveUser);
alertTypeDetailForm
.<WeChatAlertForm>addAlertType(AlertTypeDetailForm.AlertTypeEnum.WECHAT)
.token(wechatToken);
alertTypeDetailForm
.<LarkAlertForm>addAlertType(AlertTypeDetailForm.AlertTypeEnum.LARK)
.token(larkToken)
.secretEnable()
.secretToken(larkSecretToken)
.effectToAllUsers()
.submit();

Awaitility.await()
.untilAsserted(
() -> assertThat(alarmPage.alarmList())
.as("Alarm list should contain newly-created alarm")
.extracting(WebElement::getText)
.anyMatch(it -> it.contains(newAlarmName))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.EMAIL.desc()))
.anyMatch(it -> it.contains(newEmail))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.DINGTALK.desc()))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.WECHAT.desc()))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.LARK.desc()))
.anyMatch(it -> it.contains(dingTalkReceiveUser)));
}

@Test
@Order(20)
public void testEditAlarm() {
final AlarmPage alarmPage = new AlarmPage(browser);

alarmPage.editAlarm(newAlarmName)
// this step will cancel E-mail type click status.
.<EmailAlertForm>addAlertType(AlertTypeDetailForm.AlertTypeEnum.EMAIL)
.alertName(editAlarmName)
.submit();

Awaitility.await()
.untilAsserted(
() -> assertThat(alarmPage.alarmList())
.as("Alarm list should contain edited alarm")
.extracting(WebElement::getText)
.anyMatch(it -> it.contains(editAlarmName))
.noneMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.EMAIL.desc()))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.DINGTALK.desc()))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.WECHAT.desc()))
.anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.LARK.desc()))
.noneMatch(it -> it.contains(newEmail)));
}

@Test
@Order(30)
public void testDeleteAlarm() {
final AlarmPage alarmPage = new AlarmPage(browser);

alarmPage.deleteAlarm(editAlarmName);

Awaitility.await()
.untilAsserted(
() -> assertThat(alarmPage.alarmList())
.as(String.format("Alarm list shouldn't contain alarm witch named %s", editAlarmName))
.extracting(WebElement::getText)
.noneMatch(it -> it.contains(editAlarmName)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import static org.assertj.core.api.Assertions.assertThat;

@StreamPark(composeFiles = "docker/basic/docker-compose.yaml")
public class ExternalLinkPageTest {
public class ExternalLinkTest {

private static RemoteWebDriver browser;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.apache.streampark.e2e.pages.common.Constants;
import org.apache.streampark.e2e.pages.common.NavBarPage;
import org.apache.streampark.e2e.pages.setting.alarm.AlarmPage;

import lombok.Getter;
import org.openqa.selenium.WebElement;
Expand All @@ -36,6 +37,9 @@ public class SettingPage extends NavBarPage implements NavBarPage.NavBarItem {
@FindBy(xpath = "//span[contains(@class, 'streampark-simple-menu-sub-title') and contains(text(), 'External Link')]//..")
private WebElement menuExternalLinkManagement;

@FindBy(xpath = "//span[contains(@class, 'streampark-simple-menu-sub-title') and contains(text(), 'Alarms')]//..")
private WebElement menuAlarmManagement;

public SettingPage(RemoteWebDriver driver) {
super(driver);
}
Expand All @@ -55,6 +59,13 @@ public <T extends SettingPage.Tab> T goToTab(Class<T> tab) {
return tab.cast(new ExternalLinkPage(driver));
}

if (tab == AlarmPage.class) {
new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.elementToBeClickable(menuAlarmManagement));
menuAlarmManagement.click();
return tab.cast(new AlarmPage(driver));
}

throw new UnsupportedOperationException("Unknown tab: " + tab.getName());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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.streampark.e2e.pages.setting.alarm;

import org.apache.streampark.e2e.pages.common.Constants;
import org.apache.streampark.e2e.pages.common.NavBarPage;
import org.apache.streampark.e2e.pages.setting.SettingPage;

import lombok.Getter;
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.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.util.List;

@Getter
public class AlarmPage extends NavBarPage implements SettingPage.Tab {

@FindBy(xpath = "//span[contains(., 'Alarms Setting')]/..//button[contains(@class, 'ant-btn-dashed')]/span[contains(text(), 'Add New')]")
private WebElement buttonCreateAlarm;

@FindBy(xpath = "//div[@class='ant-row']")
private List<WebElement> alarmList;

@FindBy(className = "ant-form-item-explain-error")
private List<WebElement> errorMessageList;

@FindBy(xpath = "//button[contains(text(), 'Submit')]")
private WebElement errorMessageConfirmButton;

@FindBy(xpath = "//button[contains(@class, 'ant-btn')]/span[contains(., 'Yes')]")
private WebElement deleteConfirmButton;

public AlarmPage(RemoteWebDriver driver) {
super(driver);
}

public AlertTypeDetailForm createAlarm() {
waitForPageLoading();

new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.elementToBeClickable(buttonCreateAlarm));
buttonCreateAlarm.click();

return new AlertTypeDetailForm(driver);
}

public AlertTypeDetailForm editAlarm(String alarmName) {
waitForPageLoading();

alarmList().stream()
// Filter out cards containing a specific alertName.
.filter(card -> {
WebElement titleElement = new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.visibilityOf(card
.findElement(By.xpath(".//div[@class='ant-card-head']//div[@class='ant-card-head-title']"))));
return titleElement.getText().contains(alarmName);
})
// Find the eligible cards and click the edit button.
.flatMap(card -> {
List<WebElement> editButtons = card.findElements(By.xpath(
".//button[.//span[contains(@class, 'anticon') and contains(@class, 'anticon-edit')]]"));

// Make sure the button is loaded.
new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.visibilityOfAllElements(editButtons));

return editButtons.stream();
})
.filter(WebElement::isDisplayed)
.findFirst()
.orElseThrow(() -> new RuntimeException("No edit button found for alarm: " + alarmName))
.click();

return new AlertTypeDetailForm(driver);
}

public AlarmPage deleteAlarm(String alarmName) {
waitForPageLoading();

alarmList().stream()
// Filter out cards containing a specific alertName.
.filter(card -> {
WebElement titleElement = new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.visibilityOf(card
.findElement(By.xpath(".//div[@class='ant-card-head']//div[@class='ant-card-head-title']"))));
return titleElement.getText().contains(alarmName);
})
// Find the eligible cards and click the delete button.
.flatMap(card -> {
List<WebElement> deleteButtons = card.findElements(By.xpath(
".//button[.//span[contains(@class, 'anticon') and contains(@class, 'anticon-delete')]]"));

// Make sure the button is loaded.
new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.visibilityOfAllElements(deleteButtons));

return deleteButtons.stream();
})
.filter(WebElement::isDisplayed)
.findFirst()
.orElseThrow(() -> new RuntimeException("No delete button found for alarm: " + alarmName))
.click();

new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.elementToBeClickable(deleteConfirmButton));

deleteConfirmButton.click();

return this;
}
private void waitForPageLoading() {
new WebDriverWait(driver, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION)
.until(ExpectedConditions.urlContains("/setting/alarm"));
}

}
Loading

0 comments on commit 8d9d41e

Please sign in to comment.