diff --git a/jenkinsfiles/test/Jenkinsfile b/jenkinsfiles/test/Jenkinsfile index 09a3939c370ca8b4dfaed317b214c092b98ccc03..c227917d2e97dbf663cc1fb9ce5f1eb75d3d769c 100644 --- a/jenkinsfiles/test/Jenkinsfile +++ b/jenkinsfiles/test/Jenkinsfile @@ -13,6 +13,7 @@ pipeline { string(name: 'BASE_URL_DIENSTE', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanDienste') string(name: 'BASE_URL_INSPIRE_PLU', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of INSPIRE PLU') string(name: 'BASE_URL_MANAGER_API', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanManagerAPI') + string(name: 'BASE_URL_Validator_WEB', defaultValue: "https://xplanbox.lat-lon.de/xplan-validator-web", description: 'Set base URL of XPlanValidatorWeb') string(name: 'BASE_URL_DOKUMENTEN_API', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanDokumentenAPI') string(name: 'BASE_URL_MAPSERVER', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of MapServer') string(name: 'BASE_URL_MAPPROXY', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of MapProxy') @@ -22,7 +23,7 @@ pipeline { string(name: 'SLACK_TOKEN_CREDENTIAL_ID', defaultValue: "slack-integration-id", description: 'Set slack token credential id') } stages { - stage('Test XPlanManagerAPI') { + stage('SoapUI-Test XPlanManagerAPI') { steps { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') @@ -31,7 +32,7 @@ pipeline { } } } - stage('Test XPlanValidatorAPI') { + stage('SoapUI-Test XPlanValidatorAPI') { steps { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') @@ -40,7 +41,7 @@ pipeline { } } } - stage('Test XPlanDokumentenAPI') { + stage('SoapUI-Test XPlanDokumentenAPI') { steps { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') @@ -49,7 +50,7 @@ pipeline { } } } - stage('Test XPlanManagerWeb') { + stage('SoapUI-Test XPlanManagerWeb') { steps { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') @@ -58,7 +59,7 @@ pipeline { } } } - stage('Test XPlanDienste') { + stage('SoapUI-Test XPlanDienste') { steps { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') @@ -67,9 +68,19 @@ pipeline { } } } + stage('Selenium-Test XPlanValidatorWeb') { + steps { + withCredentials([ + usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') + ]) { + sh 'mvn integration-test -pl :xplan-tests-selenium -Psystem-tests -DbaseUrlValidatorWeb=${BASE_URL_Validator_WEB} -Dusername=$Username -Dpassword=$Password' + } + } + } stage('Results') { steps{ junit '**/**/target/soapui/TEST-*.xml' + junit '**/**/target/failsafe-reports/TEST-*.xml' } } } diff --git a/xplan-tests/pom.xml b/xplan-tests/pom.xml index 0035579bb114c0f6badc7189412adc61b4e2a71a..97e51a3824b9e14142492f67e70466ac43e6d829 100644 --- a/xplan-tests/pom.xml +++ b/xplan-tests/pom.xml @@ -19,6 +19,7 @@ <modules> <module>xplan-tests-soapui</module> <module>xplan-tests-manual</module> + <module>xplan-tests-selenium</module> </modules> </project> \ No newline at end of file diff --git a/xplan-tests/xplan-tests-selenium/.dockerignore b/xplan-tests/xplan-tests-selenium/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..425b7731496f206d5a0fd8a5bfb6c1c4da140a18 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/.dockerignore @@ -0,0 +1,3 @@ +target/** +Dockerfile +.settings/* \ No newline at end of file diff --git a/xplan-tests/xplan-tests-selenium/Dockerfile b/xplan-tests/xplan-tests-selenium/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..69d2336b1343f767f4d5a2b4bcec57e923e00485 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/Dockerfile @@ -0,0 +1,34 @@ +FROM maven:3.9-eclipse-temurin-11-alpine +ARG BUILD_DATE=? +ARG DOCKER_IMAGE_NAME=? +ARG GIT_REVISION=? +ARG XPLANBOX_VERSION=latest + +# see https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys +LABEL "org.opencontainers.image.created"="$BUILD_DATE" \ + "org.opencontainers.image.description"="ozgxplanung xPlanBox component" \ + "org.opencontainers.image.licenses"="GNU Affero General Public License & others" \ + "org.opencontainers.image.ref.name"="$DOCKER_IMAGE_NAME" \ + "org.opencontainers.image.revision"="$GIT_REVISION" \ + "org.opencontainers.image.title"="ozgxplanung - $DOCKER_IMAGE_NAME" \ + "org.opencontainers.image.url"="https://gitlab.opencode.de/diplanung/ozgxplanung" \ + "org.opencontainers.image.vendor"="lat/lon GmbH" \ + "org.opencontainers.image.version"="$XPLANBOX_VERSION" + +RUN apk add py3-pip py3-pillow py3-cffi py3-brotli gcc musl-dev python3-dev pango pango-tools aws-cli chromium chromium-chromedriver xvfb +RUN pip install weasyprint + +ADD . /xplanbox/xplan-tests/xplan-tests-selenium +ADD maven/xplanbox-pom.xml /xplanbox/pom.xml +ADD maven/xplan-tests-pom.xml /xplanbox/xplan-tests/pom.xml + +WORKDIR /xplanbox/xplan-tests/xplan-tests-selenium + +ENV TZ=Europe/Berlin + +# pre-fill local maven repo (including plugins) +RUN mvn -B integration-test -Psystem-tests -Dtest=NotExistingOne -Dsurefire.failIfNoSpecifiedTests=false > /dev/null \ + && mvn -B surefire-report:report-only \ + && rm -rf target + +ENTRYPOINT ["/xplanbox/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh"] \ No newline at end of file diff --git a/xplan-tests/xplan-tests-selenium/README.md b/xplan-tests/xplan-tests-selenium/README.md new file mode 100644 index 0000000000000000000000000000000000000000..00054be033d3641d5ab7a23947db7bc833b7d204 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/README.md @@ -0,0 +1,43 @@ +# Selenium Tests + +Die Tests in diesem Projekt sind nicht gedacht, um direkt als Teil vom Build ausgeführt zu werden sondern eher zu einem späteren Zeitpunkt, wenn die Anwendungen schon deployt worden sind. + +## Ausführung mit Maven + +``` +mvn clean integration-test -Psystem-tests -DbaseUrlValidatorWeb=https://xplanbox.lat-lon.de/xplan-validator-web -Dpassword='PWD' -Dusername=xplanbox +``` + +## Ausführung im Docker Container + +Die Selenium Tests können in einem Docker Container ausgeführt werden + +``` +docker run --env XPLAN_WEB_VALIDATOR_BASE_URL=... xplanbox/xplan-tests-selenium +``` + +### Umgebungsvariablen + +- `XPLAN_WEB_VALIDATOR_BASE_URL` +- `XPLAN_WEB_VALIDATOR_USERNAME` +- `XPLAN_WEB_VALIDATOR_PASSWORD` + +Der Report im PDF Format kann zu einem S3 Bucket hochgeladen werden, dafür müssen folgende Umgebungsvariablen gesetzt werden: + +- `XPLAN_UPLOAD_TEST_REPORT`: muss auf `true` gesetzt werden +- `XPLAN_S3_ENDPOINT`: die S3 Url +- `XPLAN_S3_REPORT_ID` (optional): ein Id, dass im S3-Objektname verwendet werden soll +- `XPLAN_S3_REPORT_PATH`(optional): der Pfad im S3 Bucket (default: `test-reports`) +- `XPLAN_S3_ACCESS_KEY`: der S3-Zugangschlüssel +- `XPLAN_S3_SECRET_ACCESS_KEY`: der S3-Geheimzugangschlüssel +- `XPLAN_S3_REGION`: die S3 Region +- `XPLAN_S3_BUCKET_NAME`: der Name des Buckets + +Der Report kann aus S3 lokal kopiert werden, z,B. mit: + + aws s3 cp s3://my-bucket/test-reports/report-2023-05-26T08:57:15.pdf report.pdf --endpoint-url https://the.s3.url + +Eine Notification kann nach der Ausführung der Tests zu einem Slack Chanel geschickt werden. Dafür müssen folgende Umgebungsvariablen gesetzt werden: + +- `XPLAN_NOTIFY_SLACK_CHANNEL`: der Slack Kanal +- `XPLAN_NOTIFY_SLACK_TOKEN`: das Slack Authorisierungstoken diff --git a/xplan-tests/xplan-tests-selenium/pom.xml b/xplan-tests/xplan-tests-selenium/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ea05632a98730d2e73c7fae7aa4ef7907f522865 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/pom.xml @@ -0,0 +1,122 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>xplan-tests-selenium</artifactId> + + <parent> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-tests</artifactId> + <version>7.2-SNAPSHOT</version> + </parent> + + <properties> + <maven.test.skip>true</maven.test.skip> + <maven.install.skip>true</maven.install.skip> + <maven.deploy.skip>true</maven.deploy.skip> + <enforcer.skip>true</enforcer.skip> + </properties> + + <dependencies> + <dependency> + <groupId>org.seleniumhq.selenium</groupId> + <artifactId>selenium-remote-driver</artifactId> + <version>4.12.1</version> + </dependency> + <dependency> + <groupId>org.seleniumhq.selenium</groupId> + <artifactId>selenium-chrome-driver</artifactId> + <version>4.12.1</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <skipIfEmpty>true</skipIfEmpty> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>system-tests</id> + <properties> + <baseUrlValidatorWeb/> + <maven.test.skip>false</maven.test.skip> + <password/> + <username/> + </properties> + <build> + <plugins> + <plugin> + <groupId>io.github.git-commit-id</groupId> + <artifactId>git-commit-id-maven-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <configuration> + <systemPropertyVariables> + <baseUrlValidatorWeb>${baseUrlValidatorWeb}</baseUrlValidatorWeb> + <username>${username}</username> + <password>${password}</password> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>docker</id> + <properties> + <docker-image.skip>false</docker-image.skip> + </properties> + <build> + <plugins> + <plugin> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + <configuration> + <images> + <image> + <build> + <assembly> + <inline> + <files> + <file> + <source>../pom.xml</source> + <destName>xplan-tests-pom.xml</destName> + </file> + <file> + <source>../../pom.xml</source> + <destName>xplanbox-pom.xml</destName> + </file> + </files> + </inline> + </assembly> + </build> + </image> + </images> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> \ No newline at end of file diff --git a/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh b/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh new file mode 100755 index 0000000000000000000000000000000000000000..ea36420295777900725b67b6ada906b8e61306e6 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# start virtual buffer +Xvfb :0 -screen 0 1024x768x24 & +xvfbPid=$! + +echo "Executing tests..." +DISPLAY=:0 mvn integration-test -Psystem-tests -DbaseUrlValidatorWeb=$XPLAN_WEB_VALIDATOR_BASE_URL -Dusername=$XPLAN_WEB_VALIDATOR_USERNAME -Dpassword=$XPLAN_WEB_VALIDATOR_PASSWORD -Dwebdriver.chrome.driver=/usr/bin/chromedriver +kill -9 $xvfbPid + +echo "Generating html report..." +mvn surefire-report:failsafe-report-only -q + +REPORT_PATH_PDF=target/test-report.pdf +echo "Transforming to PDF $REPORT_PATH_PDF..." +sed -i 's/display:none;//' target/site/failsafe-report.html +weasyprint file://$PWD/target/site/failsafe-report.html $REPORT_PATH_PDF +REPORT_PATH_TAR=target/test-report.tar.gz +tar czf $REPORT_PATH_TAR -C target/site . + +if [ "$XPLAN_UPLOAD_TEST_REPORT" = "true" ]; +then + export AWS_ACCESS_KEY_ID="$XPLAN_S3_ACCESS_KEY" + export AWS_SECRET_ACCESS_KEY="$XPLAN_S3_SECRET_ACCESS_KEY" + export AWS_DEFAULT_REGION="$XPLAN_S3_REGION" + XPLAN_S3_REPORT_ID="${XPLAN_S3_REPORT_ID:-`date +%y-%m-%dT%H:%m:%S`}" + XPLAN_S3_REPORT_PATH="${XPLAN_S3_REPORT_PATH:-test-reports}" + + S3_PATH_REPORT_PATH_TAR="s3://$XPLAN_S3_BUCKET_NAME/$XPLAN_S3_REPORT_PATH/report_selenium-$XPLAN_S3_REPORT_ID.tar.gz" + S3_PATH_PDF="s3://$XPLAN_S3_BUCKET_NAME/$XPLAN_S3_REPORT_PATH/report_selenium-$XPLAN_S3_REPORT_ID.pdf" + echo "Uploading report to $S3_PATH..." + aws --endpoint-url $XPLAN_S3_ENDPOINT s3 cp $REPORT_PATH_TAR $S3_PATH_REPORT_PATH_TAR + aws --endpoint-url $XPLAN_S3_ENDPOINT s3 cp $REPORT_PATH_PDF $S3_PATH_PDF +else + echo "No upload to S3 configured" +fi + + +if [ -n "$XPLAN_NOTIFY_SLACK_CHANNEL" ] && [ -n "$XPLAN_NOTIFY_SLACK_TOKEN" ]; then + echo "Sending slack notification to $XPLAN_NOTIFY_SLACK_CHANNEL" + message="Finished Selenium tests for $XPLAN_WEB_VALIDATOR_BASE_URL and Co." + if [ -n "$S3_PATH" ]; then + echo "s3" + message="$message%0A%0ATest Report available at $S3_PATH" + fi + curl -d "text=$message" -d "channel=$XPLAN_NOTIFY_SLACK_CHANNEL" -H "Authorization: Bearer $XPLAN_NOTIFY_SLACK_TOKEN" -X POST https://slack.com/api/chat.postMessage +fi + +echo "Ready" diff --git a/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java new file mode 100644 index 0000000000000000000000000000000000000000..26fe9f80d7cc2188ffab27e5ae216077fd894bf3 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java @@ -0,0 +1,189 @@ +package de.latlon.xplanbox.tests.selenium.validatorweb; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.CleanupMode; +import org.junit.jupiter.api.io.TempDir; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class XPlanValidatorWebIT { + + private static final String NAMEBPLAN_TEST = "Selenium_Test"; + + private static final String NAMEBPLAN_ORIGINAL = "BPlan001_5-4"; + + private static String connectUrl; + + @BeforeAll + static void readSystemProperties() { + String url = System.getProperty("baseUrlValidatorWeb", "https://xplanbox.lat-lon.de/xplan-validator-web"); + String username = System.getProperty("username", null); + String password = System.getProperty("password", null); + connectUrl = createConnectUrl(url, username, password); + } + + @Test + void test_XPlanValidatorWeb_withValidPlan(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path tmpDir) + throws Exception { + + Map<String, Object> chromePrefs = new HashMap<String, Object>(); + // chromePrefs.put("profile.default_content_settings.popups", 0); + chromePrefs.put("download.default_directory", tmpDir.toString()); + ChromeOptions options = new ChromeOptions(); + options.setExperimentalOption("prefs", chromePrefs); + + options.addArguments("--no-sandbox", "--ignore-certificate-errors", "--headless", "--disable-dev-shm-usage", + "--disable-gpu", "--disable-software-rasterizer"); + + // options.addArguments("--headless"); + WebDriver driver = new ChromeDriver(options); + String myZIPFile = getClass().getResource("/" + NAMEBPLAN_ORIGINAL + ".zip").getFile().toString(); + + try { + driver.get(connectUrl); + assertEquals("XPlanValidatorWeb", driver.getTitle()); + + WebElement elemAdd = driver.findElement(By.name("uploadPlanItem")); + elemAdd.sendKeys(myZIPFile); + + driver.findElement(By.xpath("//button[text()='Hochladen und Validierungsoptionen einstellen']")).click(); + verifyByXPath(driver, "//*[@class='gwt-DialogBox']//*[@class='Caption']", "Plan hochgeladen"); + + // einmal abbrechen und neu validieren + driver.findElement(By.xpath("//button[text()='abbrechen']")).click(); + + driver.findElement(By.xpath("//button[text()='Hochladen und Validierungsoptionen einstellen']")).click(); + verifyByXPath(driver, "//*[@class='gwt-DialogBox']//*[@class='Caption']", "Plan hochgeladen"); + + driver.findElement(By.xpath("//button[text()='zur Validierung']")).click(); + verifyByXPath(driver, "//*[@class='valOptionTitle']", "Validierungsoptionen"); + + WebElement elemName = driver.findElement(By.xpath( + "//tr[td/div[@class='valOptionLabel']/text() = 'Bezeichnung für den Report']/following-sibling::tr[1]//input")); + elemName.clear(); + elemName.sendKeys(NAMEBPLAN_TEST); + + driver.findElement(By.xpath("//label[text() = 'semantisch']")).click(); + + // Hier kann theoretisch noch ein Schema ausgewählt werden + // try { + // driver.findElement(By.xpath("//tr[12]/td/span/input")).click(); + // sleep(); + // } + // catch (NoSuchElementException nSEE_Ex) { + // nSEE_Ex.printStackTrace(); + // } + + driver.findElement(By.xpath("//button[text()='Validierung starten']")).click(); + + verifyByXPath(driver, "//*[@class='gwt-DialogBox']//*[@class='Caption']", "Ergebnisse der Validierung", 30); + + WebElement iframe = driver.findElement( + By.xpath("//html/body/div[4]/div/table/tbody/tr[2]/td[2]/div/table/tbody/tr/td[1]/iframe")); // Hier + // iframeName + // durch + // den + // tatsächlichen + // Namen + // des + // iframes + // ersetzen + driver.switchTo().frame(iframe); + + String name = driver.findElement(By.xpath("//body/p[1]/b")).getText(); + String valide = driver.findElement(By.xpath("//body/p[4]/b/font")).getText(); + String planname = driver.findElement(By.xpath("//body/ul[1]/b/li[1]")).getText(); + + SoftAssertions softly = new SoftAssertions(); + softly.assertThat(name).isEqualTo(NAMEBPLAN_TEST); + softly.assertThat(valide).isEqualTo("valide"); + softly.assertThat(planname).isEqualTo(NAMEBPLAN_ORIGINAL); + softly.assertAll(); + + driver.switchTo().defaultContent(); + + driver.findElement(By.xpath("//label[text() = 'HTML Report']")).click(); + driver.findElement(By.xpath("//label[text() = 'PDF Report']")).click(); + driver.findElement(By.xpath("//label[text() = 'XML Report']")).click(); + driver.findElement(By.xpath("//button[text() = 'Download']")).click(); + + String zipFileName = NAMEBPLAN_TEST + "-Report.zip"; + withinSeconds(10, () -> assertThat(listFiles(tmpDir)).containsExactly(zipFileName)); + + Path zipFile = tmpDir.resolve(zipFileName); + try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, null)) { + Path rootInZip = zipfs.getRootDirectories().iterator().next(); + + assertThat(listFiles(rootInZip)).describedAs("Content of " + zipFile) + .containsExactlyInAnyOrder(NAMEBPLAN_TEST + ".html", NAMEBPLAN_TEST + ".pdf", + NAMEBPLAN_TEST + ".xml"); + } + } + finally { + // System.out.println("url: " + driver.getCurrentUrl()); + driver.close(); + } + } + + private void withinSeconds(int nbSeconds, Callable<?> callable) throws Exception { + Instant timeout = Instant.now().plusSeconds(nbSeconds); + + while (Instant.now().isBefore(timeout)) { + try { + callable.call(); + return; + } + catch (Exception | org.opentest4j.AssertionFailedError e) { + Thread.sleep(1000); + } + } + callable.call(); + } + + private List<String> listFiles(Path tmpDir) throws IOException { + return Files.list(tmpDir).map((it) -> it.getFileName().toString()).collect(Collectors.toList()); + } + + private void verifyByXPath(WebDriver driver, String xpath, String expectedText) throws Exception { + verifyByXPath(driver, xpath, expectedText, 5); + } + + private void verifyByXPath(WebDriver driver, String xpath, String expectedText, int maxWaitInSeconds) + throws Exception { + Callable<?> c = () -> { + WebElement elt = driver.findElement(By.xpath(xpath)); + assertEquals(expectedText, elt.getText()); + return null; + }; + withinSeconds(maxWaitInSeconds, c); + } + + private static String createConnectUrl(String url, String username, String password) { + if (username != null && !username.isEmpty()) { + String authentication = username + ":" + password + "@"; + return url.replace("://", "://" + authentication); + } + return url; + } + +} diff --git a/xplan-tests/xplan-tests-selenium/src/test/resources/BPlan001_5-4.zip b/xplan-tests/xplan-tests-selenium/src/test/resources/BPlan001_5-4.zip new file mode 100644 index 0000000000000000000000000000000000000000..db9c96803f87ae159467ec89fbd9db8b548d46f7 Binary files /dev/null and b/xplan-tests/xplan-tests-selenium/src/test/resources/BPlan001_5-4.zip differ