aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--README.md60
-rw-r--r--build.gradle115
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 59536 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties24
-rwxr-xr-xgradlew169
-rw-r--r--gradlew.bat89
-rw-r--r--settings.gradle2
-rw-r--r--src/main/java/org/onap/portalng/e2e/RunUiTests.java58
-rw-r--r--src/main/java/org/onap/portalng/e2e/helpers/BeforeAfter.java31
-rw-r--r--src/main/java/org/onap/portalng/e2e/helpers/CommonUtils.java34
-rw-r--r--src/main/java/org/onap/portalng/e2e/helpers/ConfigFileReader.java62
-rw-r--r--src/main/java/org/onap/portalng/e2e/helpers/SerenitySessionVariable.java51
-rw-r--r--src/main/java/org/onap/portalng/e2e/helpers/UiSessionVariables.java24
-rw-r--r--src/main/java/org/onap/portalng/e2e/hooks/Hooks.java41
-rw-r--r--src/main/java/org/onap/portalng/e2e/hooks/SelenideConfiguration.java55
-rw-r--r--src/main/java/org/onap/portalng/e2e/pages/LoginPage.java39
-rw-r--r--src/main/java/org/onap/portalng/e2e/pages/MainPage.java34
-rw-r--r--src/main/java/org/onap/portalng/e2e/pages/PageWithTable.java31
-rw-r--r--src/main/java/org/onap/portalng/e2e/pages/PopUpDialogPage.java32
-rw-r--r--src/main/java/org/onap/portalng/e2e/pages/UserAdministrationPage.java84
-rw-r--r--src/main/java/org/onap/portalng/e2e/step_definitions/common/CommonStepDef.java43
-rw-r--r--src/main/java/org/onap/portalng/e2e/step_definitions/login/LoginStepDef.java65
-rw-r--r--src/main/java/org/onap/portalng/e2e/step_definitions/main/MainStepDef.java46
-rw-r--r--src/main/java/org/onap/portalng/e2e/step_definitions/user_administration/UserAdministrationStepDef.java132
-rw-r--r--src/main/java/org/onap/portalng/e2e/steps/common/CommonSteps.java92
-rw-r--r--src/main/java/org/onap/portalng/e2e/steps/common/TableSteps.java95
-rw-r--r--src/main/java/org/onap/portalng/e2e/steps/login/LoginSteps.java61
-rw-r--r--src/main/java/org/onap/portalng/e2e/steps/main/MainSteps.java42
-rw-r--r--src/main/java/org/onap/portalng/e2e/steps/user_administration/UserAdministrationSteps.java282
-rw-r--r--src/main/java/org/onap/portalng/e2e/types/Button.java27
-rw-r--r--src/main/java/org/onap/portalng/e2e/types/Page.java27
-rw-r--r--src/main/java/org/onap/portalng/e2e/types/UserRole.java36
-rw-r--r--src/main/resources/test-local.properties20
-rw-r--r--src/main/resources/test-remote.properties20
-rw-r--r--src/test/resources/cucumber.properties20
-rw-r--r--src/test/resources/features/login.feature32
-rw-r--r--src/test/resources/features/user_administration.feature121
38 files changed, 2205 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ef9a2c1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+allure-report/
+allure-results/
+out/
+build/
+target/
+.gradle/
+.idea/
+src/main/java/de/.DS_Store
+results.html
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e81a777
--- /dev/null
+++ b/README.md
@@ -0,0 +1,60 @@
+# portal-ng end-to-end ui tests
+This project contains portal-ng edition end-to-end UI tests.
+
+This project is using Java, Cucumber, Selenide and Allure.
+
+## Run tests
+If you want to test a remote instance of the Portal, change the $PORTAL_ENV variable:
+`export PORTAL_ENV=test-remote.properties`
+
+or provide the base url in an environment variable PORTAL_BASE_URL=https://external-host.org
+
+`export PORTAL_BASE_URL=https://external-host.org`
+
+Then adjust the properties file as needed.
+
+
+By default `test-local.properties` is used.
+
+### In Chrome
+To run the tests with Chrome from gradle wrapper:
+```shell
+./gradlew cucumber
+```
+or with
+```shell
+./gradlew clean cucumber
+```
+### In headless mode
+There's also a task for running in a headless mode (used in pipelines):
+```shell
+./gradlew cucumberCli
+```
+## Local development
+
+If you want to run a single scenario for debugging while developing any of the tests or creating a new one, you can do so.
+
+First you have to specify a `@debug` tag in any of the `.feature` files adding the `@debug` tag above the line `Scenario:`
+and then you can run the task to only run this scenario:
+
+```shell
+./gradlew cucumberLocalDebug
+```
+
+You can also add `@debug` to more scenarios to be executed within the project and run the task above again.
+
+This will also work in IntelliJ Idea when you create a gradle configuration, so you can easily run tests that you are currently developing.
+
+Don't forget to add `PORTAL_ENV=test-local.properties` as an environment variable in your IntelliJ configuration.
+
+## Test Reports
+
+To generate allure reports on MAC
+you must install allure via **brew** and then:
+
+```shell
+allure generate --clean
+```
+
+the report will appear:
+`./allure-report/index.html`
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..abc7675
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,115 @@
+plugins {
+ id 'java'
+ id 'io.qameta.allure' version '2.9.3'
+}
+apply plugin: 'io.qameta.allure'
+group 'org.onap.portalng.e2e'
+version '1.0'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'io.cucumber:cucumber-junit:7.3.3'
+ implementation 'io.cucumber:cucumber-java:7.3.3'
+ implementation 'com.codeborne:selenide:6.14.0'
+ implementation 'io.qameta.allure:allure-cucumber7-jvm:2.20.1'
+ implementation 'net.serenity-bdd:serenity-cucumber:3.4.3'
+ implementation 'net.serenity-bdd:serenity-core:3.4.3'
+ implementation 'net.serenity-bdd:serenity-junit:3.4.3'
+ implementation 'io.qameta.allure:allure-selenide:2.20.1'
+ implementation 'org.assertj:assertj-core:3.22.0'
+
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter:5.8.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-params:5.8.2'
+ testImplementation 'org.junit.vintage:junit-vintage-engine:5.8.2'
+}
+
+configurations {
+ cucumberRuntime {
+ extendsFrom testImplementation
+ }
+}
+
+allprojects {
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ }
+
+ apply plugin: 'idea'
+ apply plugin: 'java'
+ apply plugin: 'java-library'
+ apply plugin: 'io.qameta.allure'
+
+ java {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ test {
+ useJUnitPlatform()
+ testLogging.showStandardStreams = true
+ systemProperty "cucumber.filter.tags", System.getProperty("cucumber.filter.tags")
+ }
+ gradle.startParameter.continueOnFailure = true
+}
+
+tasks.register('cucumber') {
+ cleanupAllure
+ dependsOn assemble, testClasses
+ doLast {
+ javaexec {
+ systemProperties['selenide.headless'] = 'false'
+ main = "io.cucumber.core.cli.Main"
+ classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
+ args = [
+ '--plugin', 'pretty',
+ '--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
+ '--glue', 'org.onap.portalng.e2e',
+ 'src/test/resources']
+ }
+ }
+}
+
+tasks.register('cucumberLocalDebug') {
+ cleanupAllure
+ dependsOn assemble, testClasses
+ doLast {
+ javaexec {
+ systemProperties['selenide.headless'] = 'false'
+ main = "io.cucumber.core.cli.Main"
+ classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
+ args = [
+ '--plugin', 'pretty',
+ '--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
+ '--glue', 'org.onap.portalng.e2e',
+ '--tags', '@debug',
+ 'src/test/resources']
+ }
+ }
+}
+
+tasks.register('cucumberCli') {
+ cleanupAllure
+ dependsOn assemble, testClasses
+ doLast {
+ javaexec {
+ systemProperties['selenide.headless'] = 'true'
+ main = "io.cucumber.core.cli.Main"
+ classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
+ args = [
+ '--plugin', 'pretty',
+ '--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
+ '--glue', 'org.onap.portalng.e2e',
+ 'src/test/resources']
+ }
+ }
+}
+
+tasks.register('cleanupAllure') {
+ delete "allure-results"
+} \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7454180
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..800614d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+#
+
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..ca8867d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,169 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MSYS* | MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..fc9e571
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'portal-e2e-test'
+
diff --git a/src/main/java/org/onap/portalng/e2e/RunUiTests.java b/src/main/java/org/onap/portalng/e2e/RunUiTests.java
new file mode 100644
index 0000000..159d575
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/RunUiTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e;
+
+import com.codeborne.selenide.Configuration;
+import com.codeborne.selenide.logevents.SelenideLogger;
+import io.cucumber.junit.CucumberOptions;
+import io.qameta.allure.selenide.AllureSelenide;
+import net.serenitybdd.cucumber.CucumberWithSerenity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+
+@RunWith(CucumberWithSerenity.class)
+@CucumberOptions(
+ glue = {"org.onap.portalng.e2e.step_definitions"},
+ plugin = {"pretty", "io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm"},
+ stepNotifications = true,
+ monochrome = true,
+ features = {"src/test/resources/features"},
+ snippets = CucumberOptions.SnippetType.CAMELCASE)
+public class RunUiTests {
+
+ @BeforeClass
+ public static void setupAllureReports() {
+
+ SelenideLogger.addListener("AllureSelenide", new AllureSelenide()
+ .screenshots(true)
+ .savePageSource(false)
+ );
+ Configuration.headless = true;
+ Configuration.browser = "chrome";
+ Configuration.holdBrowserOpen = false;
+ Configuration.reportsFolder = "target/surefire-reports";
+ }
+
+ @AfterClass
+ public static void tear() {
+ SelenideLogger.removeListener("AllureSelenide");
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/helpers/BeforeAfter.java b/src/main/java/org/onap/portalng/e2e/helpers/BeforeAfter.java
new file mode 100644
index 0000000..5649fe1
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/helpers/BeforeAfter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.helpers;
+
+import io.cucumber.java.After;
+import org.onap.portalng.e2e.steps.user_administration.UserAdministrationSteps;
+
+public class BeforeAfter {
+
+ @After("@delete_user")
+ public void deleteUserAfterTest() {
+ UserAdministrationSteps.deleteUser(UiSessionVariables.USERNAME.get());
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/helpers/CommonUtils.java b/src/main/java/org/onap/portalng/e2e/helpers/CommonUtils.java
new file mode 100644
index 0000000..a316439
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/helpers/CommonUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.helpers;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.SelenideElement;
+
+import static com.codeborne.selenide.Condition.visible;
+
+public class CommonUtils {
+ public static void checkTextOfElement(SelenideElement element, String text) {
+ element.shouldHave(Condition.text(text));
+ }
+ public static void checkPresenceOfElement(SelenideElement element) {
+ element.shouldBe(visible);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/helpers/ConfigFileReader.java b/src/main/java/org/onap/portalng/e2e/helpers/ConfigFileReader.java
new file mode 100644
index 0000000..681b191
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/helpers/ConfigFileReader.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.helpers;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Properties;
+
+public class ConfigFileReader {
+
+ private final Properties properties;
+ private String propertyFileName= "test-local.properties";
+
+
+ public ConfigFileReader(String fileName) {
+ if(fileName != null) this.propertyFileName = fileName;
+ BufferedReader reader;
+ try {
+ ClassLoader classLoader = this.getClass().getClassLoader();
+ try{
+ reader = new BufferedReader(new FileReader(classLoader.getResource(propertyFileName).getFile()));
+ }catch (FileNotFoundException fileNotFoundException){
+ fileNotFoundException.printStackTrace();
+ throw new FileNotFoundException("Configuration file not found. Check your PORTAL_ENV variable.");
+ }
+ properties = new Properties();
+ try {
+ properties.load(reader);
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Configuration.properties not found at " + propertyFileName + "Check your PORTAL_ENV variable.");
+ }
+ }
+
+ public Properties getProperties() {
+ if (properties != null) return properties;
+ else throw new RuntimeException("Properties file: " + propertyFileName + " not found. Check your PORTAL_ENV variable.");
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/helpers/SerenitySessionVariable.java b/src/main/java/org/onap/portalng/e2e/helpers/SerenitySessionVariable.java
new file mode 100644
index 0000000..77ebef7
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/helpers/SerenitySessionVariable.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.helpers;
+
+import net.serenitybdd.core.Serenity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+public interface SerenitySessionVariable {
+
+ Logger log = LoggerFactory.getLogger(SerenitySessionVariable.class);
+
+ String name();
+ default <T> T get() {
+ final Optional<T> maybe = getOptional();
+ if (!maybe.isPresent()) {
+ throw new NoSuchElementException("No value present for: " + name());
+ }
+ return maybe.get();
+ }
+
+ default <T> Optional<T> getOptional() {
+ final T value = Serenity.sessionVariableCalled(this);
+ return Optional.ofNullable(value);
+ }
+
+ default <T> void set(T value) {
+ log.info("Setting {} to {}", this, value);
+ Serenity.setSessionVariable(this).to(value);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/helpers/UiSessionVariables.java b/src/main/java/org/onap/portalng/e2e/helpers/UiSessionVariables.java
new file mode 100644
index 0000000..b7d6bf5
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/helpers/UiSessionVariables.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.helpers;
+
+public enum UiSessionVariables implements SerenitySessionVariable {
+ USERNAME
+}
diff --git a/src/main/java/org/onap/portalng/e2e/hooks/Hooks.java b/src/main/java/org/onap/portalng/e2e/hooks/Hooks.java
new file mode 100644
index 0000000..76f7b75
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/hooks/Hooks.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.hooks;
+
+import com.codeborne.selenide.Configuration;
+import com.codeborne.selenide.WebDriverRunner;
+import io.cucumber.java.AfterAll;
+import io.cucumber.java.Before;
+
+public class Hooks {
+ @Before
+ public void BeforeScenario() {
+ SelenideConfiguration.getBrowser();
+ Configuration.holdBrowserOpen = true;
+ Configuration.baseUrl = SelenideConfiguration.getBaseUrl();
+ Configuration.reportsFolder = "target/surefire-reports";
+ }
+
+ @AfterAll
+ public static void AfterAll() {
+ WebDriverRunner.closeWindow();
+ SelenideConfiguration.closeSession();
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/hooks/SelenideConfiguration.java b/src/main/java/org/onap/portalng/e2e/hooks/SelenideConfiguration.java
new file mode 100644
index 0000000..b9788a8
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/hooks/SelenideConfiguration.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.hooks;
+
+import com.codeborne.selenide.Configuration;
+import com.codeborne.selenide.WebDriverRunner;
+import org.onap.portalng.e2e.helpers.ConfigFileReader;
+
+import java.util.Properties;
+
+import static java.lang.Boolean.parseBoolean;
+
+public class SelenideConfiguration {
+
+ private static final String REPORTS_FOLDER = "target";
+ private static final Properties testProperties = new ConfigFileReader(System.getenv("PORTAL_ENV")).getProperties();
+
+ public static String getBaseUrl() {
+ if (System.getenv("PORTAL_BASE_URL") != null) {
+ testProperties.setProperty("baseUrl", System.getenv("PORTAL_BASE_URL"));
+ }
+ return testProperties.getProperty("baseUrl");
+ }
+
+ public static void getBrowser() {
+ String browser = System.getProperty("selenide.browser", "chrome");
+ boolean headless = parseBoolean(System.getProperty("selenide.headless", "false"));
+
+ Configuration.browser = browser;
+ Configuration.headless = headless;
+ Configuration.screenshots = true;
+ Configuration.reportsFolder = REPORTS_FOLDER;
+ }
+
+ public static void closeSession() {
+ WebDriverRunner.closeWebDriver();
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/pages/LoginPage.java b/src/main/java/org/onap/portalng/e2e/pages/LoginPage.java
new file mode 100644
index 0000000..6e57cfe
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/pages/LoginPage.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.pages;
+
+import org.openqa.selenium.By;
+
+public class LoginPage {
+
+ public static final By SIGN_IN_BTN = By.id("kc-login");
+ public static final By USERNAME_LBL = By.xpath("//label[contains(text(),'Username or email')]");
+ public static final By PASSWORD_LBL = By.xpath("//label[contains(text(),'Password')]");
+ public static final By USERNAME_INPUT = By.id("username");
+ public static final By PASSWORD_INPUT = By.id("password");
+
+ public static final By CURRENT_PAGE_SPAN = By.xpath("//span[@aria-current='page']");
+ public static final By CURRENT_PAGE_HEADING_TITLE = By.xpath("//h2[@class='qa_title']");
+
+ public static final By DROPDOWN_MENU = By.id("dropdownMenu");
+ public static final By LOGOUT_BUTTON = By.xpath("//button[contains(text(), \"Logout\")]");
+ public static final By INPUT_ERROR_SPAN = By.id("input-error");
+
+}
diff --git a/src/main/java/org/onap/portalng/e2e/pages/MainPage.java b/src/main/java/org/onap/portalng/e2e/pages/MainPage.java
new file mode 100644
index 0000000..3d91bb9
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/pages/MainPage.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.pages;
+
+import org.openqa.selenium.By;
+
+public class MainPage {
+
+ public static final By NAV_LINKS = By.xpath("//a[@class='nav-link']");
+ public static final By SPINNER = By.xpath("//div[contains(@class, 'loading-spinner')]");
+
+ public static final By DASHBOARD_BTN = By.className("qa_menu_home");
+ public static final By APP_STARTER_BTN = By.className("qa_menu_app_starter");
+ public static final By USERS_BTN = By.className("qa_menu_users");
+
+}
+
diff --git a/src/main/java/org/onap/portalng/e2e/pages/PageWithTable.java b/src/main/java/org/onap/portalng/e2e/pages/PageWithTable.java
new file mode 100644
index 0000000..941c63f
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/pages/PageWithTable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.pages;
+
+import org.openqa.selenium.By;
+
+public class PageWithTable {
+
+ public static final By PAGINATION = By.xpath("//ul[contains(@class, 'pagination')]");
+ public static final By NEXT_PAGE = By.xpath("//ul[contains(@class, 'pagination')]//li[last()-1]");
+ public static final By TABLE_ROWS = By.xpath("//table//tbody/tr");
+ public static final By ROW_CELLS = By.xpath("td");
+ public static final By ROW_DELETE_BUTTON = By.xpath("td//button[contains(@class, 'qa_delete_button')]");
+}
diff --git a/src/main/java/org/onap/portalng/e2e/pages/PopUpDialogPage.java b/src/main/java/org/onap/portalng/e2e/pages/PopUpDialogPage.java
new file mode 100644
index 0000000..dcb9b9a
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/pages/PopUpDialogPage.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.pages;
+
+import org.openqa.selenium.By;
+
+public class PopUpDialogPage {
+ public static final By DIALOG_HEADER = By.className("qa_modal_header");
+ public static final By DIALOG_BODY = By.className("qa_modal_body");
+ public static final By DIALOG_APPLY_BUTTON = By.className("qa_apply_button");
+ public static final By DIALOG_CANCEL_BUTTON = By.className("qa_cancel_button");
+ public static final By ALERT_SUCCESS = By.className("alert-success");
+ public static final By ALERT_ERROR = By.className("alert-error");
+ public static final By ALERT_WARNING = By.className("alert-danger");
+}
diff --git a/src/main/java/org/onap/portalng/e2e/pages/UserAdministrationPage.java b/src/main/java/org/onap/portalng/e2e/pages/UserAdministrationPage.java
new file mode 100644
index 0000000..77fd534
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/pages/UserAdministrationPage.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.pages;
+
+import org.openqa.selenium.By;
+
+public class UserAdministrationPage {
+
+
+ //ALERT
+ public static final By ALERT_DANGER = By.className("alert-danger");
+ public static final By ALERT_SUCCESS = By.className("alert-success");
+
+ //TITLES
+ public static final By TITLE_H2 = By.tagName("h2");
+ public static final By TITLE_H4 = By.tagName("h4");
+ public static final By TITLE_H5 = By.tagName("h5");
+
+
+ //BUTTONS
+ public static final By BUTTON_CREATE = By.className("qa_create_button");
+ public static final By BUTTON_CANCEL = By.className("qa_submit_cancel");
+ public static final By BUTTON_SAVE = By.className("qa_submit_button");
+ public static final By BUTTON_EDIT = By.className("qa_edit_button");
+
+ //LABELS
+ public static final By LABEL_ID = By.xpath("//label[@for = 'id']");
+ public static final By LABEL_USERNAME = By.xpath("//label[@for = 'username']");
+ public static final By LABEL_EMAIL = By.xpath("//label[@for = 'email']");
+ public static final By LABEL_FIRST_NAME = By.xpath("//label[@for = 'firstName']");
+ public static final By LABEL_LAST_NAME = By.xpath("//label[@for = 'lastName']");
+ //INPUT
+ public static final By INPUT_ID = By.xpath("//input[@id = 'id']");
+ public static final By INPUT_USERNAME = By.xpath("//input[@id = 'username']");
+ public static final By INPUT_EMAIL = By.xpath("//input[@id = 'email']");
+ public static final By INPUT_FIRST_NAME = By.xpath("//input[@id = 'firstName']");
+ public static final By INPUT_LAST_NAME = By.xpath("//input[@id = 'lastName']");
+
+ public static final By INVALID_INPUT = By.className("ng-invalid");
+ public static final By REQUIRED_EMAIL = By.className("qa_required_email");
+ public static final By REQUIRED_USERNAME = By.className("qa_required_user_name");
+ public static final By INVALID_USERNAME = By.className("qa_invalid_user_name");
+ public static final By INVALID_EMAIL = By.className("qa_invalid_email");
+ public static final By WRONG_EMAIL_FORMAT = By.className("qa_wrong_format_email");
+
+ //FORM
+ public static final By FORM_CHECK = By.className("form-check");
+ //TABLE
+ public static final By TABLE_ROWS = By.xpath("//tbody/tr");
+
+ public static final By TITLE_LBL = By.className("qa_title");
+ public static final By USER_DATA_HEADER = By.xpath("//*[contains(text(), 'Set User Data')]");
+ public static final By ROLES_HEADER = By.xpath("//*[contains(text(), 'Set Roles')]");
+
+ public static final By AVAILABLE_ROLES = By.className("qa_available_roles");
+ public static final By ASSIGNED_ROLES = By.className("qa_assigned_roles");
+ //ROLES
+ public static final By ONAP_DESIGNER_ROLE = By.id("onap_designer");
+ public static final By ONAP_OPERATOR_ROLE = By.id("onap_operator");
+ public static final By ONAP_ADMIN_ROLE = By.id("onap_admin");
+ public static final By AVAILABLE_ROLES_HEADER = By.className("qa_available_roles");
+ public static final By ASSIGNED_ROLES_HEADER = By.className("qa_assigned_roles");
+
+ public static By roleCheckbox(String role) {
+ return By.xpath("div/input[contains(@type,checkbox) and contains(@aria-labelledby, '" + role + "')]");
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/step_definitions/common/CommonStepDef.java b/src/main/java/org/onap/portalng/e2e/step_definitions/common/CommonStepDef.java
new file mode 100644
index 0000000..bcbf3b4
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/step_definitions/common/CommonStepDef.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.step_definitions.common;
+
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import org.onap.portalng.e2e.steps.common.CommonSteps;
+import org.onap.portalng.e2e.types.Button;
+import org.onap.portalng.e2e.types.Page;
+
+public class CommonStepDef {
+
+ @Given("User clicks on {} button in main menu")
+ public void userClicksOnButtonInMainMenu(Button item) {
+ CommonSteps.userClicksOnButtonInMainMenu(item);
+ }
+ @Given("User can see {}")
+ public void userCanSeePage(Page page) {
+ CommonSteps.userCanSeePage(page);
+ }
+
+ @Then("User should see error message with text {}")
+ public void userClicksOnSubmitButtonAndErrorMessageIsDisplayed(String errorCode) {
+ CommonSteps.userClicksOnSubmitButtonAndErrorMessageIsDisplayed(errorCode);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/step_definitions/login/LoginStepDef.java b/src/main/java/org/onap/portalng/e2e/step_definitions/login/LoginStepDef.java
new file mode 100644
index 0000000..660fd4e
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/step_definitions/login/LoginStepDef.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.step_definitions.login;
+
+import io.cucumber.java.en.And;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+import org.onap.portalng.e2e.steps.login.LoginSteps;
+import org.onap.portalng.e2e.steps.main.MainSteps;
+
+public class LoginStepDef {
+ @And("User verifies presence of all elements on Sign In form")
+ public void verifySignInForm() {
+ LoginSteps.verifySignInForm();
+ }
+
+ @When("User fills log in form with USERNAME value {string} and PASSWORD value {string}")
+ public void fillFormWithUsernameAndPasswordValues(String username, String password) {
+ LoginSteps.fillFormWithUsernameAndPasswordValues(username, password);
+ }
+
+ @Then("User submits credentials with Sign In button")
+ public void submitCredentialsForLoginBySignInButton() {
+ LoginSteps.submitCredentialsForLoginBySignInButton();
+ }
+
+ @And("User checks if is signed in to the Portal")
+ public void userChecksIfSignedInToThePortal(){
+ LoginSteps.userChecksIfSignedInToThePortal();
+ }
+
+ @And("User clicks on Logout button to log out from the Portal")
+ public void userClicksOnLogoutButton(){
+ LoginSteps.userClicksOnLogoutButton();
+ }
+
+ @Then("User sees error message {string} after incorrect credentials input")
+ public void errorIsShownAfterWrongCredentialsInput(String errorMsg) {
+ LoginSteps.errorIsShownAfterWrongCredentialsInput(errorMsg);
+ }
+
+ @And("User is logged in Portal as ADMIN")
+ public void userIsLoggedInPortalAsAdmin() {
+ MainSteps.userVisitsTheIndexPage();
+ LoginSteps.fillFormWithUsernameAndPasswordValues("onap-admin", "password");
+ LoginSteps.submitCredentialsForLoginBySignInButton();
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/step_definitions/main/MainStepDef.java b/src/main/java/org/onap/portalng/e2e/step_definitions/main/MainStepDef.java
new file mode 100644
index 0000000..f088a7d
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/step_definitions/main/MainStepDef.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.step_definitions.main;
+
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.When;
+import org.onap.portalng.e2e.steps.common.CommonSteps;
+import org.onap.portalng.e2e.steps.main.MainSteps;
+import org.onap.portalng.e2e.steps.user_administration.UserAdministrationSteps;
+import org.onap.portalng.e2e.types.Page;
+
+public class MainStepDef {
+
+ @Given("User opens the Portal page")
+ public void userVisitsTheIndexPage() {
+ MainSteps.userVisitsTheIndexPage();
+ }
+
+ @Given("User visits the {} page")
+ public void userVisitsTheUsersPage(Page page) {
+ MainSteps.userClicksOnUserMenuBtn();
+ CommonSteps.userCanSeePage(page);
+ }
+
+ @When("User clicks on Create User button")
+ public void userClicksOnCreateUserButton() {
+ UserAdministrationSteps.userClicksOnCreateUserButton();
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/step_definitions/user_administration/UserAdministrationStepDef.java b/src/main/java/org/onap/portalng/e2e/step_definitions/user_administration/UserAdministrationStepDef.java
new file mode 100644
index 0000000..8d8dfec
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/step_definitions/user_administration/UserAdministrationStepDef.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.step_definitions.user_administration;
+
+import io.cucumber.java.en.And;
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+import org.onap.portalng.e2e.steps.main.MainSteps;
+import org.onap.portalng.e2e.steps.user_administration.UserAdministrationSteps;
+import org.onap.portalng.e2e.types.UserRole;
+
+public class UserAdministrationStepDef {
+
+ @And("User verifies Create User Form")
+ public void userVerifiesCreateUserForm() {
+ UserAdministrationSteps.userVerifiesCreateUserForm();
+ }
+
+ @And("User fills data necessary for creating the user with USERNAME value {} in the form")
+ public void userFillsDataNecessaryForCreatingTheUserInTheForm(String username) {
+ UserAdministrationSteps.userFillsDataNecessaryForCreatingTheUserInTheForm(username);
+ }
+
+ @When("User set parameter {} with value {} on User create or edit page")
+ public void userFillsSpecificValuesForUserCreation(String input, String username) {
+ UserAdministrationSteps.userFillsSpecificValuesForUserCreation(input, username);
+ }
+
+ @And("User clicks on Save button in the form")
+ public void userClicksOnSaveButtonInTheForm() {
+ UserAdministrationSteps.userClicksOnSaveButtonInTheForm();
+ }
+
+ @And("User clicks on Cancel button in the form")
+ public void userClicksOnCancelButtonInTheForm() {
+ UserAdministrationSteps.userClicksOnCancelButtonInTheForm();
+ }
+
+
+ @And("User should see {} feedback {}")
+ public void userSeeFeedback(String invalidInput, String message) {
+ UserAdministrationSteps.userSeeFeedback(invalidInput, message);
+ }
+
+ @Then("User verifies newly created User in the User List")
+ public void userVerifiesNewUserInUserList() throws InterruptedException {
+ UserAdministrationSteps.userVerifiesNewUserInUserList();
+ }
+
+ @And("User with USERNAME value {} is created")
+ public void userIsCreated(String username) {
+ MainSteps.userClicksOnUserMenuBtn();
+ UserAdministrationSteps.userClicksOnCreateUserButton();
+ UserAdministrationSteps.userVerifiesCreateUserForm();
+ UserAdministrationSteps.userFillsDataNecessaryForCreatingTheUserInTheForm(username);
+ UserAdministrationSteps.userClicksOnSaveButtonInTheForm();
+// UserAdministrationSteps.userVerifiesNewUserInUserList();
+ }
+
+ @Then("User with USERNAME {} is successfully deleted")
+ public void deleteUser(String username) {
+ UserAdministrationSteps.deleteUser(username);
+ }
+
+ @Given("User clicks on EDIT button for user with USERNAME {} on Users page")
+ public void userClickOnBtnForUser(String username) {
+ UserAdministrationSteps.clickOnEditBtn(username);
+ }
+
+ @Given("User checks presence of elements on User edit page")
+ public void userCheckAllElementsOnUpdatePage() {
+ UserAdministrationSteps.userCheckAllElementsOnEditPage();
+ }
+
+ @Given("User fills all possible values to User edit page")
+ public void userFillsValuesToEditFormPage() {
+ UserAdministrationSteps.userFillsValuesToEditFormPage();
+ }
+
+ @Given("User clicks on {} button for user on User edit page")
+ public void userClickOnBtnOnEditPage(String button) {
+ UserAdministrationSteps.userClickOnBtnOnEditPage(button);
+ }
+
+ @When("User fills EMAIL {} in User edit page")
+ public void userFillsEmailValuesForUserCreation(String email) {
+ UserAdministrationSteps.userFillsEmailValuesForUserCreation(email);
+ }
+
+ @Given("User checks {} pop-up on user page with text {}")
+ public void userChecksPopupOnUserPage(String popUpType, String text) {
+ UserAdministrationSteps.userChecksPopupOnUserPage(popUpType, text);
+ }
+
+ @When("User clicks on {} available role checkbox")
+ public void assignAvailableRolesToUser(UserRole userRole) {
+ UserAdministrationSteps.assignAvailableRolesToUser(userRole);
+ }
+
+ @When("{} role is assigned to user")
+ public void selectedRoleIsAssignedToUser(UserRole userRole) {
+ UserAdministrationSteps.verifyThatUserRoleIsAssigned(userRole);
+ }
+
+ @When("User clicks on {} assigned role checkbox")
+ public void unAssignRolesToUser(UserRole userRole) {
+ UserAdministrationSteps.unAssignRoleFromUser(userRole);
+ }
+
+ @When("{} role is unassigned from user")
+ public void selectedRoleIsUnassignedFromUser(UserRole userRole) {
+ UserAdministrationSteps.verifyThatUserRoleIsAvailable(userRole);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/steps/common/CommonSteps.java b/src/main/java/org/onap/portalng/e2e/steps/common/CommonSteps.java
new file mode 100644
index 0000000..fa96438
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/steps/common/CommonSteps.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.steps.common;
+
+import com.codeborne.selenide.Condition;
+import org.onap.portalng.e2e.types.Button;
+import org.onap.portalng.e2e.types.Page;
+
+import java.time.Duration;
+
+import static com.codeborne.selenide.Condition.disappear;
+import static com.codeborne.selenide.Selenide.$;
+import static org.onap.portalng.e2e.helpers.CommonUtils.checkTextOfElement;
+import static org.onap.portalng.e2e.pages.MainPage.*;
+import static org.onap.portalng.e2e.pages.PopUpDialogPage.ALERT_WARNING;
+import static org.onap.portalng.e2e.pages.UserAdministrationPage.TITLE_H2;
+
+
+public class CommonSteps {
+
+ public static void spinnerShouldDisappear() {
+ try {
+ $(SPINNER).shouldBe(Condition.visible, Duration.ofSeconds(3));
+ } catch (Throwable ignored) {
+ } finally {
+ $(SPINNER).shouldBe(disappear, Duration.ofSeconds(80));
+ }
+ }
+
+ public static void userClicksOnButtonInMainMenu(Button button) {
+ switch (button) {
+ case DASHBOARD:
+ checkTextOfElement($(DASHBOARD_BTN), "Dashboard");
+ $(DASHBOARD_BTN).click();
+ break;
+ case APP_STARTER:
+ checkTextOfElement($(APP_STARTER_BTN), "App Starter");
+ $(APP_STARTER_BTN).click();
+ break;
+ case USERS:
+ checkTextOfElement($(USERS_BTN), "Users");
+ $(USERS_BTN).click();
+ break;
+ default:
+ throw new IllegalStateException("Sidebar Menu Menu button" + button + "not recognized");
+ }
+ }
+
+ public static void userCanSeePage(Page page) {
+ switch (page) {
+ case DASHBOARD:
+ spinnerShouldDisappear();
+ checkTextOfElement($(TITLE_H2), "Dashboard");
+ break;
+ case APP_STARTER_PAGE:
+ spinnerShouldDisappear();
+ checkTextOfElement($(TITLE_H2), "App Starter");
+ break;
+ case USERS_PAGE:
+ spinnerShouldDisappear();
+ checkTextOfElement($(TITLE_H2), "User Administration");
+ break;
+ case USER_EDIT_PAGE:
+ spinnerShouldDisappear();
+ checkTextOfElement($(TITLE_H2), "Edit User");
+ break;
+ default:
+ throw new IllegalStateException("Page not recognized");
+ }
+ }
+
+ public static void userClicksOnSubmitButtonAndErrorMessageIsDisplayed(String error) {
+ checkTextOfElement($(ALERT_WARNING), error);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/steps/common/TableSteps.java b/src/main/java/org/onap/portalng/e2e/steps/common/TableSteps.java
new file mode 100644
index 0000000..182c2f6
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/steps/common/TableSteps.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.steps.common;
+
+import com.codeborne.selenide.ElementsCollection;
+import com.codeborne.selenide.Selenide;
+import com.codeborne.selenide.SelenideElement;
+import org.openqa.selenium.By;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.codeborne.selenide.Condition.*;
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$;
+import static java.util.stream.Collectors.toList;
+import static org.onap.portalng.e2e.pages.PageWithTable.*;
+
+public class TableSteps {
+
+ public static Optional<SelenideElement> findRowWithText(String text, boolean exactMatch) {
+ return findRowsWithText(text, exactMatch, false).stream().findAny();
+ }
+
+ public static List<SelenideElement> findRowsWithText(String text, boolean exactMatch, boolean isContaining) {
+ $(TABLE_ROWS).should(exist);
+ List<SelenideElement> foundedRows = findRowsOnActivePage(text, exactMatch, isContaining);
+ while (foundedRows.isEmpty() && hasNextPage()) {
+ goToNextPage();
+ foundedRows = findRowsOnActivePage(text, exactMatch, isContaining);
+ }
+ return foundedRows;
+ }
+
+ public static boolean hasNextPage() {
+ return $(PAGINATION).exists() && $(NEXT_PAGE).has(not(cssClass("disabled")));
+ }
+
+ public static void goToNextPage() {
+ Selenide.executeJavaScript("arguments[0].click()", $(NEXT_PAGE).$(By.xpath("a")).getWrappedElement());
+ CommonSteps.spinnerShouldDisappear();
+ }
+
+
+ public static int countRowsOnAllPages() {
+ int numberOfRows = Selenide.$$(TABLE_ROWS).size();
+ while (hasNextPage()) {
+ goToNextPage();
+ numberOfRows += Selenide.$$(TABLE_ROWS).size();
+ }
+ return numberOfRows;
+ }
+
+ private static List<SelenideElement> findRowsOnActivePage(String text, boolean exactMatch, boolean isContaining) {
+ return $$(TABLE_ROWS).stream()
+ .filter(row -> {
+ ElementsCollection rowValues = row.$$(ROW_CELLS);
+ String textInRow = getTextFromCell(rowValues.first());
+ if (exactMatch) {
+ return text.equals(textInRow);
+ } else if (isContaining) {
+ return textInRow.contains(text);
+ } else {
+ return textInRow.startsWith(text);
+ }
+ }).collect(toList());
+ }
+
+ private static String getTextFromCell(SelenideElement cell) {
+ SelenideElement anchor = cell.find(By.xpath("a"));
+ if (anchor.exists()) {
+ return anchor.getText();
+ } else {
+ return cell.getText();
+ }
+ }
+
+}
diff --git a/src/main/java/org/onap/portalng/e2e/steps/login/LoginSteps.java b/src/main/java/org/onap/portalng/e2e/steps/login/LoginSteps.java
new file mode 100644
index 0000000..5d3e927
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/steps/login/LoginSteps.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.steps.login;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.Selenide;
+import org.onap.portalng.e2e.pages.LoginPage;
+
+import static com.codeborne.selenide.Condition.text;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LoginSteps {
+
+ public static void verifySignInForm() {
+ Selenide.$(LoginPage.USERNAME_LBL).shouldHave(text("Username or email"));
+ Selenide.$(LoginPage.PASSWORD_LBL).shouldHave(Condition.text("Password"));
+ Selenide.$(LoginPage.SIGN_IN_BTN).shouldHave(Condition.value("Sign In"));
+ }
+
+ public static void fillFormWithUsernameAndPasswordValues(String username, String password) {
+ Selenide.$(LoginPage.USERNAME_INPUT).setValue(username);
+ Selenide.$(LoginPage.PASSWORD_INPUT).setValue(password);
+ }
+
+ public static void submitCredentialsForLoginBySignInButton() {
+ Selenide.$(LoginPage.SIGN_IN_BTN).click();
+ }
+
+ public static void userChecksIfSignedInToThePortal(){
+ Selenide.$(LoginPage.CURRENT_PAGE_SPAN).shouldHave(text("Dashboard"));
+ Selenide.$(LoginPage.CURRENT_PAGE_HEADING_TITLE).shouldHave(text("Dashboard"));
+ }
+
+ public static void userClicksOnLogoutButton(){
+ Selenide.$(LoginPage.DROPDOWN_MENU).click();
+ Selenide.$(LoginPage.LOGOUT_BUTTON).click();
+ }
+
+ public static void errorIsShownAfterWrongCredentialsInput(String errorMsg) {
+ assertThat(Selenide.$(LoginPage.INPUT_ERROR_SPAN).getText())
+ .describedAs("Text is not as expected")
+ .isEqualTo(errorMsg);
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/steps/main/MainSteps.java b/src/main/java/org/onap/portalng/e2e/steps/main/MainSteps.java
new file mode 100644
index 0000000..ffb0a96
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/steps/main/MainSteps.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.steps.main;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.Configuration;
+
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.open;
+import static org.onap.portalng.e2e.pages.MainPage.USERS_BTN;
+import static org.onap.portalng.e2e.pages.UserAdministrationPage.TITLE_H2;
+
+public class MainSteps {
+ public static void userVisitsTheIndexPage() {
+ open(Configuration.baseUrl);
+ }
+
+ public static void userClicksOnUserMenuBtn() {
+ $(USERS_BTN).click();
+ }
+
+ public void userCanSeeUserAdministrationPage() {
+ $(TITLE_H2).shouldHave(Condition.text("User Administration"));
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/steps/user_administration/UserAdministrationSteps.java b/src/main/java/org/onap/portalng/e2e/steps/user_administration/UserAdministrationSteps.java
new file mode 100644
index 0000000..1460e37
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/steps/user_administration/UserAdministrationSteps.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.steps.user_administration;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.Selenide;
+import com.codeborne.selenide.SelenideElement;
+import org.junit.Assert;
+import org.onap.portalng.e2e.steps.main.MainSteps;
+import org.onap.portalng.e2e.helpers.UiSessionVariables;
+import org.onap.portalng.e2e.pages.PopUpDialogPage;
+import org.onap.portalng.e2e.steps.common.CommonSteps;
+import org.onap.portalng.e2e.steps.common.TableSteps;
+import org.onap.portalng.e2e.types.Button;
+import org.onap.portalng.e2e.types.UserRole;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+import java.util.Optional;
+
+import static com.codeborne.selenide.Condition.*;
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.onap.portalng.e2e.helpers.CommonUtils.checkPresenceOfElement;
+import static org.onap.portalng.e2e.helpers.CommonUtils.checkTextOfElement;
+import static org.onap.portalng.e2e.pages.PageWithTable.ROW_DELETE_BUTTON;
+import static org.onap.portalng.e2e.pages.PopUpDialogPage.ALERT_SUCCESS;
+import static org.onap.portalng.e2e.pages.PopUpDialogPage.*;
+import static org.onap.portalng.e2e.pages.UserAdministrationPage.*;
+
+public class UserAdministrationSteps {
+
+ public static void userClicksOnCreateUserButton() {
+ $(BUTTON_CREATE).shouldHave(Condition.text("Create User"));
+ $(BUTTON_CREATE).click();
+ }
+
+ public static void userVerifiesCreateUserForm() {
+ $(TITLE_H2).shouldHave(Condition.text("Create User"));
+
+ $$(TITLE_H4).get(0).shouldHave(Condition.text("Set User Data"));
+ $$(TITLE_H4).get(1).shouldHave(Condition.text("Set Roles"));
+
+ $$(TITLE_H5).get(1).shouldHave(Condition.text("Available"));
+ $$(TITLE_H5).get(2).shouldHave(Condition.text("Assigned"));
+
+ $(LABEL_ID).shouldHave(Condition.text("ID"));
+ $(LABEL_USERNAME).shouldHave(Condition.text("Username"));
+ $(LABEL_EMAIL).shouldHave(Condition.text("Email"));
+ $(LABEL_FIRST_NAME).shouldHave(Condition.text("First Name"));
+ $(LABEL_LAST_NAME).shouldHave(Condition.text("Last Name"));
+
+ $(INPUT_ID).shouldBe(Condition.disabled);
+ }
+
+ public static void userFillsDataNecessaryForCreatingTheUserInTheForm(String username) {
+ $(INPUT_USERNAME).setValue(username);
+ $(INPUT_EMAIL).setValue(username + "@test.user");
+ $(INPUT_FIRST_NAME).setValue("E2E");
+ $(INPUT_LAST_NAME).setValue("TestUser");
+ UiSessionVariables.USERNAME.set(username);
+
+ $$(FORM_CHECK).findBy(Condition.text("onap_admin")).$("input").click();
+ }
+ public static void userFillsSpecificValuesForUserCreation(String input, String value) {
+ switch (input) {
+ case "EMAIL":
+ if (value.equals("clear")) {
+ $(INPUT_EMAIL).setValue(" ");
+ $(INPUT_EMAIL).sendKeys(Keys.BACK_SPACE);
+ } else {
+ $(INPUT_EMAIL).setValue(" ");
+ $(INPUT_EMAIL).sendKeys(Keys.BACK_SPACE);
+ $(INPUT_EMAIL).setValue(value);
+ }
+ break;
+ case "USERNAME":
+ if (value.equals("clear")) {
+ $(INPUT_USERNAME).setValue(" ");
+ $(INPUT_USERNAME).sendKeys(Keys.BACK_SPACE);
+ } else {
+ $(INPUT_USERNAME).setValue(value);
+ }
+ break;
+ case "LAST_NAME":
+ $(INPUT_LAST_NAME).setValue(value);
+ break;
+ case "FIRST_NAME":
+ $(INPUT_FIRST_NAME).setValue(value);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + input);
+ }
+ }
+
+ public static void userClicksOnSaveButtonInTheForm() {
+ $(BUTTON_SAVE).click();
+ }
+
+ public static void userClicksOnCancelButtonInTheForm() {
+ $(BUTTON_CANCEL).click();
+ }
+
+ public static void userSeeFeedback(String invalidInput, String message) {
+ switch (invalidInput) {
+ case "REQUIRED_USERNAME":
+ checkTextOfElement($(REQUIRED_USERNAME), message);
+ checkPresenceOfElement($(INVALID_INPUT));
+ break;
+ case "REQUIRED_EMAIL":
+ checkTextOfElement($(REQUIRED_EMAIL), message);
+ checkPresenceOfElement($(INVALID_INPUT));
+ break;
+ case "INVALID_USERNAME":
+ checkTextOfElement($(INVALID_USERNAME), message);
+ checkPresenceOfElement($(INVALID_INPUT));
+ break;
+ case "INVALID_EMAIL":
+ checkTextOfElement($(INVALID_EMAIL), message);
+ checkPresenceOfElement($(INVALID_INPUT));
+ break;
+ case "WRONG_EMAIL_FORMAT":
+ checkTextOfElement($(WRONG_EMAIL_FORMAT), message);
+ checkPresenceOfElement($(INVALID_INPUT));
+ break;
+ default:
+ throw new IllegalArgumentException(invalidInput + " is not implemented yet");
+
+ }
+ }
+
+
+ public static void userVerifiesNewUserInUserList() throws InterruptedException {
+ Selenide.Wait().until(ExpectedConditions.visibilityOf($$(TABLE_ROWS).get(0)));
+
+ for (SelenideElement row : $$(TABLE_ROWS)) {
+ if ($(row).findAll(By.id("td")).findBy(Condition.text("e2e-test-user")).exists()) {
+ Assert.assertEquals("e2e-test-user", $(row).findAll(By.id("td")).get(0).getText());
+ Assert.assertEquals("E2e", $(row).findAll(By.id("td")).get(1).getText());
+ Assert.assertEquals("TestUser", $(row).findAll(By.id("td")).get(2).getText());
+ Assert.assertEquals("e2e@test.user", $(row).findAll(By.id("td")).get(3).getText());
+ Assert.assertEquals("onap_admin", $(row).findAll(By.id("td")).get(4).getText());
+ }
+ }
+ }
+
+ public static void deleteUser(String username) {
+ MainSteps.userClicksOnUserMenuBtn();
+
+ Optional<SelenideElement> userRow = TableSteps.findRowWithText(username, true);
+ if (userRow.isPresent()) {
+ assertThat(userRow.isPresent()).as("User cannot be deleted, user with " + username + " was not found.").isTrue();
+ userRow.get().$(ROW_DELETE_BUTTON).click();
+ $(DIALOG_HEADER).shouldHave(text("Delete User"));
+ $(DIALOG_BODY).shouldHave(text("Are you sure, that you want to delete user: " + username + " ? "));
+ $(DIALOG_APPLY_BUTTON).shouldHave(text("Delete"));
+ $(DIALOG_APPLY_BUTTON).click();
+ $$(ALERT_SUCCESS).last().shouldHave(text("User successfully deleted."));
+ userRow.get().should(not(exist));
+ }
+ }
+
+ public static void clickOnEditBtn(String username) {
+ CommonSteps.userClicksOnButtonInMainMenu(Button.USERS);
+
+ Optional<SelenideElement> userRow = TableSteps.findRowWithText(username, true);
+ assertThat(userRow.isPresent()).as("User cannot be updated, user with " + username + " was not found.").isTrue();
+ userRow.get().$(BUTTON_EDIT).click();
+ userRow.get().should(not(exist));
+ }
+
+ public static void userCheckAllElementsOnEditPage() {
+ checkTextOfElement($(TITLE_LBL), "Edit User");
+ checkPresenceOfElement($(USER_DATA_HEADER));
+ checkPresenceOfElement($(INPUT_ID));
+ checkPresenceOfElement($(INPUT_USERNAME));
+ checkPresenceOfElement($(INPUT_EMAIL));
+ checkPresenceOfElement($(INPUT_FIRST_NAME));
+ checkPresenceOfElement($(INPUT_LAST_NAME));
+ checkTextOfElement($(LABEL_ID), "ID");
+ checkTextOfElement($(LABEL_USERNAME), "Username");
+ checkTextOfElement($(LABEL_EMAIL), "Email");
+ checkTextOfElement($(LABEL_FIRST_NAME), "First Name");
+ checkTextOfElement($(LABEL_LAST_NAME), "Last Name");
+ checkPresenceOfElement($(ROLES_HEADER));
+ checkTextOfElement($(AVAILABLE_ROLES), "Available");
+ checkTextOfElement($(ASSIGNED_ROLES), "Assigned");
+ checkTextOfElement($(ONAP_DESIGNER_ROLE), "onap_designer");
+ checkTextOfElement($(ONAP_OPERATOR_ROLE), "onap_operator");
+ checkTextOfElement($(ONAP_ADMIN_ROLE), "onap_admin");
+ checkTextOfElement($(BUTTON_CANCEL), "Cancel");
+ checkTextOfElement($(BUTTON_SAVE), "Save");
+ }
+
+ public static void userFillsValuesToEditFormPage() {
+ $(INPUT_FIRST_NAME).setValue("firstName");
+ $(INPUT_LAST_NAME).setValue("lastName");
+ $(INPUT_EMAIL).setValue("e2e" + "@test.com");
+ }
+
+ public static void userClickOnBtnOnEditPage(String button) {
+ switch (button) {
+ case "Edit":
+ $(BUTTON_SAVE).click();
+ break;
+ case "Cancel":
+ $(BUTTON_CANCEL).click();
+ break;
+ default:
+ throw new IllegalArgumentException("Button was not implemented");
+ }
+ }
+
+ public static void userFillsEmailValuesForUserCreation(String email) {
+ $(INPUT_EMAIL).clear();
+ $(INPUT_EMAIL).setValue(email);
+ }
+
+ public static void userChecksPopupOnUserPage(String popUpType, String text) {
+ if (popUpType.equals("SUCCESS")) {
+ checkTextOfElement($(PopUpDialogPage.ALERT_SUCCESS), text);
+ } else if (popUpType.equals("ERROR")) {
+ checkTextOfElement($(PopUpDialogPage.ALERT_ERROR), text);
+ }
+ }
+
+ public static void assignAvailableRolesToUser(UserRole userRole) {
+ SelenideElement availableRolesBlock = $(AVAILABLE_ROLES_HEADER).parent();
+ SelenideElement availableRole = availableRolesBlock.find(userRoleCheckBox(userRole));
+ availableRole.click();
+ availableRole.should(not(exist));
+ }
+
+ public static void unAssignRoleFromUser(UserRole userRole) {
+ SelenideElement assignedRolesBlock = $(ASSIGNED_ROLES_HEADER).parent();
+ SelenideElement assignedRole = assignedRolesBlock.find(userRoleCheckBox(userRole));
+ assignedRole.click();
+ assignedRole.should(not(exist));
+ }
+
+ public static void verifyThatUserRoleIsAvailable(UserRole userRole) {
+ SelenideElement availableRolesBlock = $(AVAILABLE_ROLES_HEADER).parent();
+ availableRolesBlock.find(userRoleCheckBox(userRole)).should(exist);
+ }
+
+ public static void verifyThatUserRoleIsAssigned(UserRole userRole) {
+ SelenideElement assignedRolesBlock = $(ASSIGNED_ROLES_HEADER).parent();
+ assignedRolesBlock.find(userRoleCheckBox(userRole)).should(exist);
+ }
+
+ public static By userRoleCheckBox(UserRole userRole) {
+ switch (userRole) {
+ case ONAP_DESIGNER:
+ return roleCheckbox("onap_designer");
+ case ONAP_OPERATOR:
+ return roleCheckbox("onap_operator");
+ case ONAP_ADMIN:
+ return roleCheckbox("onap_admin");
+ default:
+ throw new IllegalArgumentException("Not recognized role");
+ }
+ }
+}
diff --git a/src/main/java/org/onap/portalng/e2e/types/Button.java b/src/main/java/org/onap/portalng/e2e/types/Button.java
new file mode 100644
index 0000000..a434b4f
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/types/Button.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.types;
+
+public enum Button {
+ DROPDOWN_MENU,
+ DASHBOARD,
+ APP_STARTER,
+ USERS
+}
diff --git a/src/main/java/org/onap/portalng/e2e/types/Page.java b/src/main/java/org/onap/portalng/e2e/types/Page.java
new file mode 100644
index 0000000..1ec1e33
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/types/Page.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.types;
+
+public enum Page {
+ DASHBOARD,
+ APP_STARTER_PAGE,
+ USERS_PAGE,
+ USER_EDIT_PAGE
+}
diff --git a/src/main/java/org/onap/portalng/e2e/types/UserRole.java b/src/main/java/org/onap/portalng/e2e/types/UserRole.java
new file mode 100644
index 0000000..e1a0871
--- /dev/null
+++ b/src/main/java/org/onap/portalng/e2e/types/UserRole.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023. Deutsche Telekom 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.onap.portalng.e2e.types;
+
+public enum UserRole {
+ ONAP_DESIGNER("onap_designer"),
+ ONAP_OPERATOR("onap_operator"),
+ ONAP_ADMIN("onap_admin");
+
+ private String value;
+
+ UserRole(final String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/src/main/resources/test-local.properties b/src/main/resources/test-local.properties
new file mode 100644
index 0000000..d1648e9
--- /dev/null
+++ b/src/main/resources/test-local.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+#
+
+baseUrl=http://localhost \ No newline at end of file
diff --git a/src/main/resources/test-remote.properties b/src/main/resources/test-remote.properties
new file mode 100644
index 0000000..a4ea8cc
--- /dev/null
+++ b/src/main/resources/test-remote.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+#
+
+baseUrl=https://remote-url \ No newline at end of file
diff --git a/src/test/resources/cucumber.properties b/src/test/resources/cucumber.properties
new file mode 100644
index 0000000..869613f
--- /dev/null
+++ b/src/test/resources/cucumber.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+#
+
+cucumber.publish.quiet=true \ No newline at end of file
diff --git a/src/test/resources/features/login.feature b/src/test/resources/features/login.feature
new file mode 100644
index 0000000..5fecc34
--- /dev/null
+++ b/src/test/resources/features/login.feature
@@ -0,0 +1,32 @@
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+Feature: Login and Logout
+
+ Scenario: User can sign in to Portal with valid credentials
+ Given User opens the Portal page
+ And User verifies presence of all elements on Sign In form
+ When User fills log in form with USERNAME value 'onap-admin' and PASSWORD value 'password'
+ Then User submits credentials with Sign In button
+ And User checks if is signed in to the Portal
+ And User clicks on Logout button to log out from the Portal
+
+ Scenario: User cannot login to Portal with invalid username or password
+ Given User opens the Portal page
+ And User verifies presence of all elements on Sign In form
+ When User fills log in form with USERNAME value 'onap-admin' and PASSWORD value 'invalidPassword'
+ And User submits credentials with Sign In button
+ Then User sees error message 'Invalid username or password.' after incorrect credentials input
diff --git a/src/test/resources/features/user_administration.feature b/src/test/resources/features/user_administration.feature
new file mode 100644
index 0000000..3e402db
--- /dev/null
+++ b/src/test/resources/features/user_administration.feature
@@ -0,0 +1,121 @@
+# Copyright (c) 2023. Deutsche Telekom 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+Feature: User Administration
+
+ @delete_user
+ Scenario: User can create a new user in Portal
+ Given User is logged in Portal as ADMIN
+ Given User visits the USERS_PAGE page
+ When User clicks on Create User button
+ And User verifies Create User Form
+ And User fills data necessary for creating the user with USERNAME value e2e-test-user in the form
+ And User clicks on Save button in the form
+ Given User visits the USERS_PAGE page
+ Then User verifies newly created User in the User List
+
+ Scenario: User can cancel user creation
+ Given User visits the USERS_PAGE page
+ When User clicks on Create User button
+ And User verifies Create User Form
+ And User clicks on Cancel button in the form
+ And User can see USERS_PAGE
+
+ Scenario: User cannot create a new user in Portal without mandatory parameters
+ Given User visits the USERS_PAGE page
+ When User clicks on Create User button
+ And User verifies Create User Form
+ And User clicks on Save button in the form
+ Then User should see REQUIRED_USERNAME feedback Required
+ And User should see REQUIRED_EMAIL feedback Cannot be empty
+
+ @delete_user
+ Scenario: User cannot be created with already existing USERNAME
+ Given User with USERNAME value e2e-test-user is created
+ And User clicks on USERS button in main menu
+ When User clicks on Create User button
+ And User fills data necessary for creating the user with USERNAME value e2e-test-user in the form
+ And User clicks on Save button in the form
+ Then User should see error message with text Error while creating user account! Error reported by "Keycloak" system: "User exists with same username" Please, try to create user with different username.
+
+ Scenario: User cannot be created with already existing EMAIL
+ Given User with USERNAME value e2e-test-user is created
+ And User clicks on USERS button in main menu
+ When User clicks on Create User button
+ And User fills data necessary for creating the user with USERNAME value e2e-test-user-2 in the form
+ And User set parameter EMAIL with value e2e-test-user@test.user on User create or edit page
+ And User clicks on Save button in the form
+ Then User should see error message with text Error while creating user account! Error reported by "Keycloak" system: "User exists with same email" Please, try to create user with different email address.
+ #deletion after test
+ Then User with USERNAME e2e-test-user is successfully deleted
+
+ Scenario: User cannot be created with USERNAME value as empty string
+ Given User clicks on USERS button in main menu
+ And User clicks on Create User button
+ When User fills data necessary for creating the user with USERNAME value "" in the form
+ And User set parameter EMAIL with value test@test.user on User create or edit page
+ And User clicks on Save button in the form
+ Then User should see INVALID_USERNAME feedback Invalid character
+
+ Scenario: User cannot be created with EMAIL value as empty string
+ Given User clicks on USERS button in main menu
+ And User clicks on Create User button
+ And User fills data necessary for creating the user with USERNAME value e2e-test-user in the form
+ When User set parameter EMAIL with value "" on User create or edit page
+ And User clicks on Save button in the form
+ Then User should see INVALID_EMAIL feedback Invalid character
+ And User should see WRONG_EMAIL_FORMAT feedback Wrong email format
+
+ Scenario: User can be deleted in Portal
+ Given User with USERNAME value e2e-test-user is created
+ Then User with USERNAME e2e-test-user is successfully deleted
+
+ @delete_user
+ Scenario: User can be edited in Portal
+ Given User with USERNAME value e2e-test-user is created
+ And User clicks on EDIT button for user with USERNAME e2e-test-user on Users page
+ And User can see USER_EDIT_PAGE
+ And User checks presence of elements on User edit page
+ And User clicks on Cancel button for user on User edit page
+ And User can see USERS_PAGE
+ And User clicks on EDIT button for user with USERNAME e2e-test-user on Users page
+ And User fills all possible values to User edit page
+ When User clicks on Edit button for user on User edit page
+ Then User checks SUCCESS pop-up on user page with text User successfully updated.
+ And User can see USERS_PAGE
+
+ Scenario: User cannot be created with already existing EMAIL
+ Given User with USERNAME value e2e-test-user-1 is created
+ And User with USERNAME value e2e-test-user-2 is created
+ And User clicks on USERS button in main menu
+ And User can see USERS_PAGE
+ And User clicks on EDIT button for user with USERNAME e2e-test-user-1 on Users page
+ And User can see USER_EDIT_PAGE
+ When User fills EMAIL e2e-test-user-2@test.user in User edit page
+ And User clicks on Edit button for user on User edit page
+ Then User should see error message with text Error, changing user account failed! Error reported by "Keycloak" system: "User exists with same username or email" Please, try to create user with different email address.
+ #deletion after
+ Then User with USERNAME e2e-test-user-1 is successfully deleted
+ Then User with USERNAME e2e-test-user-2 is successfully deleted
+
+ @delete_user
+ Scenario: User role can be assigned or unassigned on Edit page
+ Given User with USERNAME value e2e-test-user is created
+ And User clicks on EDIT button for user with USERNAME e2e-test-user on Users page
+ When User clicks on ONAP_DESIGNER available role checkbox
+ Then ONAP_DESIGNER role is assigned to user
+ And User clicks on ONAP_DESIGNER assigned role checkbox
+ Then ONAP_DESIGNER role is unassigned from user