diff --git a/.gitignore b/.gitignore index 958d1dd..d8cb491 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -# Eclipse -.settings/ - # Java .class diff --git a/.ivy/raise-build-plugin-version.sh b/.ivy/raise-build-plugin-version.sh new file mode 100755 index 0000000..ad960fa --- /dev/null +++ b/.ivy/raise-build-plugin-version.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +mvn --batch-mode versions:set-property versions:commit -Dproperty=project.build.plugin.version -DnewVersion=${2} -DallowSnapshots=true diff --git a/.ivy/raise-version.sh b/.ivy/raise-version.sh new file mode 100755 index 0000000..ba4d1f4 --- /dev/null +++ b/.ivy/raise-version.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +mvn -B versions:set -DnewVersion=${1} -DprocessAllModules +mvn -B versions:commit -DprocessAllModules diff --git a/Jenkinsfile b/Jenkinsfile index 2658f39..81a21f7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,6 +9,10 @@ pipeline { cron '@midnight' } + parameters { + string(name: 'engineSource', defaultValue: 'https://product.ivyteam.io', description: 'Engine page url') + } + stages { stage('build') { steps { @@ -22,11 +26,12 @@ pipeline { docker.build('maven').inside("--network ${networkName}") { withCredentials([string(credentialsId: 'gpg.password.axonivy', variable: 'GPG_PWD'), file(credentialsId: 'gpg.keystore.axonivy', variable: 'GPG_FILE')]) { sh "gpg --batch --import ${env.GPG_FILE}" - def phase = isReleaseOrMasterBranch() ? 'deploy' : 'verify' - maven cmd: "clean ${phase} " + + def phase = isReleaseOrMasterBranch() ? 'deploy' : 'verify' + maven cmd: "clean ${phase} " + "-Dmaven.test.failure.ignore=true " + + "-Dengine.page.url=${params.engineSource} " + "-Dskip.gpg=false " + - "-Dgpg.passphrase='${env.GPG_PWD}' " + + "-Dgpg.passphrase='${env.GPG_PWD}' " + "-Dselenide.remote=http://${seleniumName}:4444/wd/hub " } } @@ -36,7 +41,7 @@ pipeline { } } archiveArtifacts '**/target/*.jar' - junit '**/target/surefire-reports/**/*.xml' + junit testDataPublishers: [[$class: 'StabilityTestDataPublisher']], testResults: '**/target/*-reports/**/*.xml' recordIssues tools: [mavenConsole(), eclipse(), javaDoc()], unstableTotalAll: 1, filters: [ excludeMessage('.*JAR will be empty.*'), // for unit tester ] diff --git a/build/release/Jenkinsfile b/build/release/Jenkinsfile index c170b03..7b32bd6 100644 --- a/build/release/Jenkinsfile +++ b/build/release/Jenkinsfile @@ -5,6 +5,10 @@ pipeline { buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '20')) } + parameters { + string(name: 'engineSource', defaultValue: 'https://product.ivyteam.io', description: 'Engine page url') + } + stages { stage('build') { when { @@ -21,7 +25,7 @@ pipeline { withCredentials([string(credentialsId: 'gpg.password.axonivy', variable: 'GPG_PWD'), file(credentialsId: 'gpg.keystore.axonivy', variable: 'GPG_FILE')]) { sh "gpg --batch --import ${env.GPG_FILE}" def dryRun = isReleaseBranch() ? '' : '-DdryRun=true' - def args = "-Dmaven.test.skip=true -Dskip.gpg=false -Dgpg.passphrase='${env.GPG_PWD}'"; + def args = "-Dmaven.test.skip=true -Dengine.page.url=${params.engineSource} -Dskip.gpg=false -Dgpg.passphrase='${env.GPG_PWD}'"; maven cmd: '--batch-mode ' + dryRun + ' -Darguments="' + args + '" -DpushChanges=false -DlocalCheckout=true release:prepare release:perform' } diff --git a/pom.xml b/pom.xml index 9a72d81..c85befb 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ primeui-tester web-tester web-tester-product + web-tester-fixture unit-tester diff --git a/unit-tester/.classpath b/unit-tester/.classpath new file mode 100644 index 0000000..5e577b9 --- /dev/null +++ b/unit-tester/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit-tester/.project b/unit-tester/.project new file mode 100644 index 0000000..d297c0a --- /dev/null +++ b/unit-tester/.project @@ -0,0 +1,23 @@ + + + unit-tester + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/web-tester-fixture/.classpath b/web-tester-fixture/.classpath new file mode 100644 index 0000000..106dc80 --- /dev/null +++ b/web-tester-fixture/.classpath @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web-tester-fixture/.gitignore b/web-tester-fixture/.gitignore new file mode 100644 index 0000000..1b2547b --- /dev/null +++ b/web-tester-fixture/.gitignore @@ -0,0 +1,19 @@ +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ diff --git a/web-tester-fixture/.project b/web-tester-fixture/.project new file mode 100644 index 0000000..767b31c --- /dev/null +++ b/web-tester-fixture/.project @@ -0,0 +1,49 @@ + + + web-tester-fixture + + + + + + ch.ivyteam.ivy.designer.dataClasses.ui.ivyDataClassBuilder + + + + + ch.ivyteam.ivy.designer.process.ui.ivyWebServiceProcessClassBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + ch.ivyteam.ivy.designer.ide.ivyModelValidationBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + ch.ivyteam.ivy.project.IvyProjectNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.jem.beaninfo.BeanInfoNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/web-tester-fixture/.settings/.jsdtscope b/web-tester-fixture/.settings/.jsdtscope new file mode 100644 index 0000000..869c01d --- /dev/null +++ b/web-tester-fixture/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/web-tester-fixture/.settings/ch.ivyteam.ivy.designer.prefs b/web-tester-fixture/.settings/ch.ivyteam.ivy.designer.prefs new file mode 100644 index 0000000..64f7537 --- /dev/null +++ b/web-tester-fixture/.settings/ch.ivyteam.ivy.designer.prefs @@ -0,0 +1,5 @@ +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_DATA_CLASS=com.axonivy.demo.test.Data +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_NAMESPACE=com.axonivy.demo.test +ch.ivyteam.ivy.project.preferences\:PRIMEFACES_VERSION=11 +ch.ivyteam.ivy.project.preferences\:PROJECT_VERSION=112000 +eclipse.preferences.version=1 diff --git a/web-tester-fixture/.settings/org.eclipse.jdt.core.prefs b/web-tester-fixture/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..d4540a5 --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/web-tester-fixture/.settings/org.eclipse.wst.common.component b/web-tester-fixture/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..8dd25f1 --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 0000000..9b4b9fc --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.xml b/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..156ecdb --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/web-tester-fixture/.settings/org.eclipse.wst.css.core.prefs b/web-tester-fixture/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 0000000..5ddc6bd --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,2 @@ +css-profile/=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.container b/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 0000000..3bd5d0a --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.name b/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 0000000..05bd71b --- /dev/null +++ b/web-tester-fixture/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/web-tester-fixture/config/custom-fields.yaml b/web-tester-fixture/config/custom-fields.yaml new file mode 100644 index 0000000..c757a9a --- /dev/null +++ b/web-tester-fixture/config/custom-fields.yaml @@ -0,0 +1 @@ +CustomFields: diff --git a/web-tester-fixture/config/databases.yaml b/web-tester-fixture/config/databases.yaml new file mode 100644 index 0000000..10319e2 --- /dev/null +++ b/web-tester-fixture/config/databases.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/databases.json +Databases: diff --git a/web-tester-fixture/config/overrides.any b/web-tester-fixture/config/overrides.any new file mode 100644 index 0000000..f59ec20 --- /dev/null +++ b/web-tester-fixture/config/overrides.any @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/web-tester-fixture/config/persistence.xml b/web-tester-fixture/config/persistence.xml new file mode 100644 index 0000000..d6b96d7 --- /dev/null +++ b/web-tester-fixture/config/persistence.xml @@ -0,0 +1,2 @@ + + diff --git a/web-tester-fixture/config/rest-clients.yaml b/web-tester-fixture/config/rest-clients.yaml new file mode 100644 index 0000000..4bffaca --- /dev/null +++ b/web-tester-fixture/config/rest-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/rest-clients.json +RestClients: diff --git a/web-tester-fixture/config/roles.xml b/web-tester-fixture/config/roles.xml new file mode 100644 index 0000000..59892fe --- /dev/null +++ b/web-tester-fixture/config/roles.xml @@ -0,0 +1,4 @@ + + + Everybody + diff --git a/web-tester-fixture/config/users.xml b/web-tester-fixture/config/users.xml new file mode 100644 index 0000000..d9acb4b --- /dev/null +++ b/web-tester-fixture/config/users.xml @@ -0,0 +1,7 @@ + + + + test + test + + diff --git a/web-tester-fixture/config/variables.yaml b/web-tester-fixture/config/variables.yaml new file mode 100644 index 0000000..ca2dc28 --- /dev/null +++ b/web-tester-fixture/config/variables.yaml @@ -0,0 +1,2 @@ +Variables: + myVar: init diff --git a/web-tester-fixture/config/webservice-clients.yaml b/web-tester-fixture/config/webservice-clients.yaml new file mode 100644 index 0000000..688047a --- /dev/null +++ b/web-tester-fixture/config/webservice-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/webservice-clients.json +WebServiceClients: diff --git a/web-tester-fixture/dataclasses/com/axonivy/demo/test/Data.ivyClass b/web-tester-fixture/dataclasses/com/axonivy/demo/test/Data.ivyClass new file mode 100644 index 0000000..fff505b --- /dev/null +++ b/web-tester-fixture/dataclasses/com/axonivy/demo/test/Data.ivyClass @@ -0,0 +1,2 @@ +Data #class +com.axonivy.demo.test #namespace diff --git a/web-tester-fixture/pom.xml b/web-tester-fixture/pom.xml new file mode 100644 index 0000000..1aa8940 --- /dev/null +++ b/web-tester-fixture/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + com.axonivy.ivy.webtest + web-tester-fixture + 11.2.0-SNAPSHOT + iar-integration-test + + 11.2.0-SNAPSHOT + https://product.ivyteam.io + + + + com.axonivy.ivy.webtest + web-tester + 11.2.0-SNAPSHOT + + + + src_test + target/test-classes + + + com.axonivy.ivy.ci + project-build-plugin + 11.2.0-SNAPSHOT + true + + [11.1.0,12.0.0) + ${engine.page.url} + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0 + + true + + + + + + + + central.snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + always + + + + diff --git a/web-tester-fixture/processes/test.p.json b/web-tester-fixture/processes/test.p.json new file mode 100644 index 0000000..60cc73f --- /dev/null +++ b/web-tester-fixture/processes/test.p.json @@ -0,0 +1,40 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.1/process.json", + "id" : "18AF06DD4E1A49B8", + "config" : { + "data" : "com.axonivy.demo.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f3" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 352, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "DialogCall", + "name" : "VariableView", + "config" : { + "dialog" : "com.axonivy.demo.test.VariableView:start()" + }, + "visual" : { + "at" : { "x" : 224, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1" } + ] + } ] +} \ No newline at end of file diff --git a/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.rddescriptor b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.rddescriptor new file mode 100644 index 0000000..ae605f0 --- /dev/null +++ b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.rddescriptor @@ -0,0 +1,7 @@ + + + + viewTechnology + JSF + + diff --git a/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.xhtml b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.xhtml new file mode 100644 index 0000000..41f7536 --- /dev/null +++ b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableView.xhtml @@ -0,0 +1,35 @@ + + + + VariableView + + +

+ This is an + Html Dialog + implemented with JSF and Primefaces as widget library +

+ + + + + + + + +
+
+ + +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewData.ivyClass b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewData.ivyClass new file mode 100644 index 0000000..256af58 --- /dev/null +++ b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewData.ivyClass @@ -0,0 +1,2 @@ +VariableViewData #class +com.axonivy.demo.test.VariableView #namespace diff --git a/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewProcess.p.json b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewProcess.p.json new file mode 100644 index 0000000..1d74d79 --- /dev/null +++ b/web-tester-fixture/src_hd/com/axonivy/demo/test/VariableView/VariableViewProcess.p.json @@ -0,0 +1,48 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.1/process.json", + "id" : "18AF0790FE6D8F86", + "kind" : "HTML_DIALOG", + "config" : { + "data" : "com.axonivy.demo.test.VariableView.VariableViewData" + }, + "elements" : [ { + "id" : "f0", + "type" : "HtmlDialogStart", + "name" : "start()", + "config" : { + "signature" : "start", + "guid" : "18AF0790FE7395CA" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f1" } + ] + }, { + "id" : "f1", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 224, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "HtmlDialogEventStart", + "name" : "close", + "config" : { + "guid" : "18AF0790FE81F9E6" + }, + "visual" : { + "at" : { "x" : 96, "y" : 160 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "HtmlDialogExit", + "visual" : { + "at" : { "x" : 224, "y" : 160 } + } + } ] +} \ No newline at end of file diff --git a/web-tester-fixture/src_test/com/axonivy/demo/test/WebTestAppFixtureIT.java b/web-tester-fixture/src_test/com/axonivy/demo/test/WebTestAppFixtureIT.java new file mode 100644 index 0000000..6a3f8d8 --- /dev/null +++ b/web-tester-fixture/src_test/com/axonivy/demo/test/WebTestAppFixtureIT.java @@ -0,0 +1,65 @@ +package com.axonivy.demo.test; + +import static com.codeborne.selenide.Condition.exactText; +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.open; + +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +import com.axonivy.ivy.webtest.IvyWebTest; +import com.axonivy.ivy.webtest.engine.EngineUrl; +import com.axonivy.ivy.webtest.engine.WebAppFixture; +import com.codeborne.selenide.Selenide; +import com.codeborne.selenide.WebDriverConditions; + +@IvyWebTest +class WebTestAppFixtureIT { + + @Test + void login(WebAppFixture fixture) { + open(EngineUrl.createProcessUrl("web-tester-fixture/18AF06DD4E1A49B8/start.ivp")); + fixture.login("test", "test"); + Selenide.webdriver().shouldHave(WebDriverConditions.urlContaining("web-tester-fixture")); + assertCurrentUser("test"); + + fixture.logout(); + assertCurrentUser("Unknown User"); + + fixture.login("test", "test"); + assertCurrentUser("test"); + } + + @Test + void variable(WebAppFixture fixture) { + fixture.var("myVar", "hello"); + open(EngineUrl.createProcessUrl("web-tester-fixture/18AF06DD4E1A49B8/start.ivp")); + assertVariable("hello"); + + fixture.resetVar("myVar"); + Selenide.refresh(); + assertVariable("init"); + } + + @Test + void config(WebAppFixture fixture) { + fixture.config("Variables.myVar", "hello"); + open(EngineUrl.createProcessUrl("web-tester-fixture/18AF06DD4E1A49B8/start.ivp")); + assertVariable("hello"); + + fixture.resetConfig("Variables.myVar"); + Selenide.refresh(); + assertVariable("init"); + } + + private void assertCurrentUser(String user) { + open(EngineUrl.create().toUrl()); + $(By.id("sessionUserName")).shouldBe(text(user)); + } + + private void assertVariable(String variable) { + $(By.id("form:variable")).shouldBe(exactText(variable)); + } + +} \ No newline at end of file diff --git a/web-tester-fixture/webContent/layouts/frame-10.xhtml b/web-tester-fixture/webContent/layouts/frame-10.xhtml new file mode 100644 index 0000000..dd16147 --- /dev/null +++ b/web-tester-fixture/webContent/layouts/frame-10.xhtml @@ -0,0 +1,28 @@ + + + + + + + + <ui:insert name="title">Ivy Html Dialog</ui:insert> + + + + + + + +
+ + default content + +
+
+ \ No newline at end of file diff --git a/web-tester/CHANGELOG.md b/web-tester/CHANGELOG.md index 0da0f40..ff4432b 100644 --- a/web-tester/CHANGELOG.md +++ b/web-tester/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log (web-tester) +## [11.2.0] - + +### Added + +- Add WebAppFixture util for login and config fixtures + An Axon Ivy Engine in version 10.0.14 is at least needed for all fixtures + +### Changed + +- Upgrade selenide to 6.19.0 + ## [11.1.0] - 2023-04-19 ### Changed diff --git a/web-tester/src/main/java/com/axonivy/ivy/webtest/IvyWebTestExtension.java b/web-tester/src/main/java/com/axonivy/ivy/webtest/IvyWebTestExtension.java index 34eb7f9..961b63e 100644 --- a/web-tester/src/main/java/com/axonivy/ivy/webtest/IvyWebTestExtension.java +++ b/web-tester/src/main/java/com/axonivy/ivy/webtest/IvyWebTestExtension.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2021 Axon Ivy AG - * + * * Licensed 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 @@ -22,11 +22,14 @@ import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import com.axonivy.ivy.webtest.engine.WebAppFixture; import com.codeborne.selenide.Configuration; import com.codeborne.selenide.Selenide; -class IvyWebTestExtension implements BeforeEachCallback, BeforeAllCallback { +class IvyWebTestExtension implements BeforeEachCallback, BeforeAllCallback, ParameterResolver { static final String BROWSER_DEFAULT = "firefox"; static final boolean HEADLESS_DEFAULT = true; @@ -70,4 +73,14 @@ private Optional findAnnotation(ExtensionContext context) { return context.getTestClass().map(c -> c.getAnnotation(IvyWebTest.class)); } + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType().isAssignableFrom(WebAppFixture.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return new WebAppFixture(); + } + } diff --git a/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/BaseEngineUrl.java b/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/BaseEngineUrl.java index 5cacad8..7b91d99 100644 --- a/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/BaseEngineUrl.java +++ b/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/BaseEngineUrl.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2021 Axon Ivy AG + * + * Licensed 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 com.axonivy.ivy.webtest.engine; import java.net.InetAddress; diff --git a/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/WebAppFixture.java b/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/WebAppFixture.java new file mode 100644 index 0000000..82a3d61 --- /dev/null +++ b/web-tester/src/main/java/com/axonivy/ivy/webtest/engine/WebAppFixture.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2021 Axon Ivy AG + * + * Licensed 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 com.axonivy.ivy.webtest.engine; + +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.open; + +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +import com.axonivy.ivy.webtest.IvyWebTest; +import com.codeborne.selenide.Selenide; + +/** + *

+ * Use the WebAppFixture to temporary change the active environment for your web tests. + *

Changes are not rollbacked automatically to the previous value after the test.

+ *

To use the AppFixture annotate your test class with the {@link IvyWebTest} annotation. + * Then add the AppFixture as parameter to any {@link Test}, {@link BeforeEach}, {@link AfterEach}, + * methods or the constructor of the test class.

+ *

Example: + *


+ * {@literal @IvyWebTest}
+ * class Test {
+ *   {@literal @Test}
+ *   void test(WebAppFixture fixture) {
+ *     open(EngineUrl.create().toUrl());
+ *     $(By.id("sessionUserName")).shouldBe(Condition.exactText("Developer"));
+ *     fixture.login("test", "test");
+ *     $(By.id("sessionUserName")).shouldBe(Condition.exactText("test"));
+ *   }
+ * }
+ * 
+ * @since 10.0.14 + */ +public class WebAppFixture { + + private static final String VAR_PATH = "variables"; + private static final String CONFIG_PATH = "configs"; + + /** + *

Login to a user. + * This user will be used as long as the browser context isn't closed or the {@link #logout()} method is called. + * It uses the login form of the Dev-Workflow-UI.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   open(EngineUrl.create().toUrl());
+   *   $(By.id("sessionUserName")).shouldBe(Condition.exactText("Developer"));
+   *   fixture.login("test", "test");
+   *   $(By.id("sessionUserName")).shouldBe(Condition.exactText("test"));
+   * }
+   * 
+ * @param username the name of the user + * @param password the password of the user + */ + public void login(String username, String password) { + var currentUrl = Selenide.webdriver().driver().url(); + open(EngineUrl.create().app("").path("dev-workflow-ui/faces/login.xhtml").toUrl()); + $(By.id("loginForm:userName")).shouldBe(visible).sendKeys(username); + $(By.id("loginForm:password")).shouldBe(visible).sendKeys(password); + $(By.id("loginForm:login")).shouldBe(visible).click(); + $(By.id("loginForm:login")).shouldNotBe(visible); + if (!"about:blank".equals(currentUrl)) { + open(currentUrl); + } + } + + /** + *

Logout the current user. + * This method logout the current user by using the Dev-Workflow-UI.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   open(EngineUrl.create().toUrl());
+   *   $(By.id("sessionUserName")).shouldBe(Condition.exactText("Developer"));
+   *   fixture.logout();
+   *   $(By.id("sessionUserName")).shouldBe(Condition.exactText("test"));
+   * }
+   * 
+ */ + public void logout() { + var currentUrl = Selenide.webdriver().driver().url(); + open(EngineUrl.create().app("").path("dev-workflow-ui/faces/home.xhtml").toUrl()); + $(By.id("sessionUserName")).shouldBe(visible).click(); + $(By.id("sessionLogoutBtn")).shouldBe(visible).click(); + open(currentUrl); + } + + /** + *

Set a variable to a given value. + * This variable will be set as long it is not been {@link #resetVar(String)}.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   fixture.var("myVar", "myNewValue");
+   * }
+   * 
+ */ + public void var(String name, String value) { + try { + var url = configRestUrl(VAR_PATH + "/" + name) + "?value=" + value; + sendRequest(HttpRequest.newBuilder(new URI(url)).POST(BodyPublishers.noBody())); + } catch (Exception ex) { + throw new RuntimeException("Couldn't set variable", ex); + } + } + + /** + *

Reset a variable to the initial value.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   fixture.var("myVar", "myNewValue");
+   *   //do some tests
+   *   fixture.resetVar("myVar");
+   * }
+   * 
+ */ + public void resetVar(String name) { + try { + var url = configRestUrl(VAR_PATH + "/" + name); + sendRequest(HttpRequest.newBuilder(new URI(url)).DELETE()); + } catch (Exception ex) { + throw new RuntimeException("Couldn't remove variable", ex); + } + } + + /** + *

Set an app configuration (like rest or web service,...) to a given value. + * This configuration will be set as long it is not been {@link #resetConfig(String)}.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   fixture.config("RestClients.MyRestClient.Url", "${ivy.app.baseurl}/api/myCoolMockService");
+   * }
+   * 
+ */ + public void config(String name, String value) { + try { + var url = configRestUrl(CONFIG_PATH + "/" + name) + "?value=" + value; + sendRequest(HttpRequest.newBuilder(new URI(url)).POST(BodyPublishers.noBody())); + } catch (Exception ex) { + throw new RuntimeException("Couldn't set config", ex); + } + } + + /** + *

Reset an app configuration (like rest or web service,...) to an initial value.

+ *

Example: + *


+   * {@literal @Test}
+   * void test(WebAppFixture fixture) {
+   *   fixture.config("RestClients.MyRestClient.Url", "${ivy.app.baseurl}/api/myCoolMockService");
+   *   //do some tests
+   *   fixture.resetConfig("RestClients.MyRestClient.Url");
+   * }
+   * 
+ */ + public void resetConfig(String name) { + try { + var url = configRestUrl(CONFIG_PATH + "/" + name); + sendRequest(HttpRequest.newBuilder(new URI(url)).DELETE()); + } catch (Exception ex) { + throw new RuntimeException("Couldn't remove config", ex); + } + } + + private static String configRestUrl(String path) { + return EngineUrl.create().app("system").rest("apps/" + EngineUrl.applicationName() + "/" + path).toUrl(); + } + + private void sendRequest(HttpRequest.Builder requestBuilder) throws Exception { + var client = HttpClient.newBuilder() + .authenticator(new RestConfigAuthentication()) + .build(); + var request = requestBuilder.header("X-Requested-By", "webtest").build(); + var response = client.send(request, BodyHandlers.ofString()); + if (response.statusCode() > 399) { + throw new RuntimeException("Couldn't send web app fixture request (status code: " + response.statusCode() + "): " + response.body()); + } + } + + private static class RestConfigAuthentication extends Authenticator { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("admin", "admin".toCharArray()); + } + } +}