KEYCLOAK-6736 Base UI tests for mobile and desktop browsers

This commit is contained in:
vmuzikar 2018-08-07 11:10:24 +02:00 committed by Pavel Drozd
parent 72750b9882
commit 65f51b7b83
118 changed files with 2801 additions and 782 deletions

View file

@ -287,10 +287,10 @@ This will start latest Keycloak and import the realm JSON file, which was previo
-Dmigrated.auth.server.version=1.9.8.Final
## UI tests
## Admin Console UI tests
The UI tests are real-life, UI focused integration tests. Hence they do not support the default HtmlUnit browser. Only the following real-life browsers are supported: Mozilla Firefox, Google Chrome and Internet Explorer. For details on how to run the tests with these browsers, please refer to [Different Browsers](#different-browsers) chapter.
The UI tests are focused on the Admin Console as well as on some login scenarios. They are placed in the `console` module and are disabled by default.
The UI tests are focused on the Admin Console. They are placed in the `console` module and are disabled by default.
The tests also use some constants placed in [test-constants.properties](tests/base/src/test/resources/test-constants.properties). A different file can be specified by `-Dtestsuite.constants=path/to/different-test-constants.properties`
@ -304,6 +304,17 @@ mvn -f testsuite/integration-arquillian/tests/other/console/pom.xml \
-Dfirefox_binary=/opt/firefox-45.1.1esr/firefox
```
## Base UI tests
Similarly to Admin Console tests, these tests are focused on UI, specifically on the parts of the server that are accessed by an end user (like Login page, or Account Console).
They are designed to work with mobile browsers (alongside the standard desktop browsers). For details on the supported browsers and their configuration please refer to [Different Browsers chapter](#different-browsers).
#### Execution example
```
mvn -f testsuite/integration-arquillian/tests/other/base-ui/pom.xml \
clean test \
-Pandroid \
-Dappium.avd=Nexus_5X_API_27
```
## Welcome Page tests
The Welcome Page tests need to be run on WildFly/EAP. So that they are disabled by default and are meant to be run separately.
@ -366,31 +377,70 @@ To run the tests run:
To run individual social provider test only you can use option like `-Dtest=SocialLoginTest#linkedinLogin`
## Different Browsers
#### Mozilla Firefox
* **Supported version:** [latest ESR](https://www.mozilla.org/en-US/firefox/organizations/) (Extended Support Release)
* **Driver download required:** no (using the old legacy Firefox driver)
* **Run with:** `-Dbrowser=firefox`; optionally you can specify `-Dfirefox_binary=path/to/firefox/binary`
You can use many different real-world browsers to run the integration tests.
Although technically they can be run with almost every test in the testsuite, they can fail with some of them as the tests often require specific optimizations for given browser. Therefore, only some of the test modules have support to be run with specific browsers.
#### Mozilla Firefox with legacy driver
* **Supported test modules:** `console`
* **Supported version:** [52 ESR](https://www.mozilla.org/en-US/firefox/organizations/) (Extended Support Release)
* **Driver download required:** no
* **Run with:** `-Dbrowser=firefox -DfirefoxLegacyDriver=true`; optionally you can specify `-Dfirefox_binary=path/to/firefox/binary`
#### Mozilla Firefox with GeckoDriver
You can also use Firefox automation with modern Marionette protocol and GeckoDriver. However, this is **highly experimental** and the testsuite may not work as expected.
* **Supported test modules:** `base-ui`
* **Supported version:** as latest as possible (Firefox has better support for Marionette with each version released)
* **Driver download required:** [GeckoDriver](https://github.com/mozilla/geckodriver/releases)
* **Run with:** `-Dbrowser=firefox -DfirefoxLegacyDriver=false -Dwebdriver.gecko.driver=path/to/geckodriver`
#### Google Chrome
* **Supported test modules:** `console`, `base-ui`
* **Supported version:** latest stable
* **Driver download required:** [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) which corresponds with your version of the browser
* **Driver download required:** [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) that corresponds with your version of the browser
* **Run with:** `-Dbrowser=chrome -Dwebdriver.chrome.driver=path/to/chromedriver`
#### Internet Explorer
* **Supported test modules:** `console`, `base-ui`
* **Supported version:** 11
* **Driver download required:** [Internet Explorer Driver Server](http://www.seleniumhq.org/download/); recommended version [3.5.1 32-bit](http://selenium-release.storage.googleapis.com/3.5/IEDriverServer_Win32_3.5.1.zip)
* **Run with:** `-Dbrowser=internetExplorer -Dwebdriver.ie.driver=path/to/IEDriverServer.exe`
#### Apple Safari
* **Supported test modules:** `base-ui`
* **Supported version:** latest stable
* **Driver download required:** no (the driver is bundled with macOS)
* **Run with:** `-Dbrowser=safari`
#### Automatic driver downloads
You can rely on automatic driver downloads which is provided by [Arquillian Drone](http://arquillian.org/arquillian-extension-drone/#_automatic_download). To do so just omit the `-Dwebdriver.{browser}.driver` CLI argument when running the tests.
#### Mobile browsers
The support for testing with the mobile browsers is implemented using the [Appium](http://appium.io/) project.
This means the tests can be run with a real mobile browser in a real mobile OS. However, only emulators/simulators of mobile devices are supported at the moment (no physical devices) in our testsuite.
First, you need to install the Appium server. If you have Node.js and npm installed on your machine, you can do that with: `npm install -g appium`. For further details and requirements please refer to the [official Appium documentation](http://appium.io/docs/en/about-appium/intro/).
The tests will try to start the Appium server automatically but you can do it manually as well (just by executing `appium`).
To use a mobile browser you need to create a virtual device. The most convenient way to do so is to install the desired platform's IDE - either [Android Studio](https://developer.android.com/studio/) (for Android devices) or [Xcode](https://developer.apple.com/xcode/) (for iOS devices) - then you can create a device (smartphone/tablet) there. For details please refer to documentation of those IDEs.
#### Google Chrome on Android
* **Supported test modules:** `base-ui`
* **Supported host OS:** Windows, Linux, macOS
* **Supported browser version:** latest stable
* **Supported mobile OS version:** Android 7.x, 8.x
* **Run with:** `mvn clean test -Pandroid -Dappium.avd=name_of_the_AVD` where AVD is the name of your Android Virtual Device (e.g. `Nexus_5X_API_27`)
**Tips & tricks:**
* If the AVD name contains any spaces, you need to replace them with underscores when specifying the `-Dappium.avd=...`.
* It's probable that a freshly created device will contain an outdated Chrome version. To update to the latest version (without using the Play Store) you need to download an `.apk` for Chrome and install it with `adb install -r path/to/chrome.apk`.
* Chrome on Android uses ChromeDriver similarly to regular desktop Chrome. The ChromeDriver is bundled with the Appium server. To use a newer ChromeDriver please follow the [Appium documentation](http://appium.io/docs/en/writing-running-appium/web/chromedriver/).
#### Apple Safari on iOS
* **Supported test modules:** `base-ui`
* **Supported host OS:** macOS
* **Supported browser version:** _depends on the mobile OS version_
* **Supported mobile OS version:** iOS 11.x
* **Run with:** `mvn clean test -Pios -Dappium.deviceName=device_name` where the device name is your device identification (e.g. `iPhone X`)
## Run X.509 tests
To run the X.509 client certificate authentication tests:

View file

@ -84,7 +84,7 @@ and the URL hierarchy is modeled by the class inheritance hierarchy (subclasses/
The default browser for UI testing is `htmlunit` which is used for fast "headless" testing.
Other browsers can be selected with the `-Dbrowser` property, for example `firefox`.
See [HOW-TO-RUN.md](HOW-TO-RUN.md) and Arquillian Graphene documentation for more details.
See [HOW-TO-RUN.md](HOW-TO-RUN.md) and Arquillian Drone documentation for more details.
### Utils classes
UI testing is sometimes very tricky due to different demands and behaviours of different browsers and their drivers. So there are some very useful Utils classes which are already dealing with some common stability issues while testing. See `UIUtils`, `URLUtils` and `WaitUtils` classes in the Base Testsuite.
@ -97,6 +97,15 @@ UI testing is sometimes very tricky due to different demands and behaviours of d
The base testsuite contains custom Arquillian extensions and most functional tests.
The other test modules depend on this module.
### Base UI Testsuite
Contains most of the UI-focused tests that don't cover Admin Console, i.e. all the parts of the server that are intended to be accessed by an end user.
The tests placed here are exclusively covering the UI functionality of the server, i.e. checking if all the page elements are visible, links clickable etc., and are focused on simplicity and stability.
This differs them from other integration tests and Admin Console UI tests.
They are designed to work with most of the desktop browsers (HtmlUnit included) as well as mobile browsers (Chrome on Android and Safari on iOS). Please see [HOW-TO-RUN.md](HOW-TO-RUN.md) for details on supported browsers.
The tests are place in a separate module (`tests/other/base-ui`) and are run with HtmlUnit by default.
### Admin Console UI Tests
Tests for Keycloak Admin Console are located in a separate module `tests/other/console`

View file

@ -51,7 +51,7 @@
<arquillian-core.version>1.2.1.Final</arquillian-core.version>
<!--the version of shrinkwrap_resolver should align with the version in arquillian-bom-->
<shrinkwrap-resolver.version>2.2.6</shrinkwrap-resolver.version>
<selenium.version>3.11.0</selenium.version>
<selenium.version>3.13.0</selenium.version>
<arquillian-drone.version>2.5.1</arquillian-drone.version>
<arquillian-graphene.version>2.3.2</arquillian-graphene.version>
<arquillian-wildfly-container.version>2.1.0.Final</arquillian-wildfly-container.version>
@ -61,6 +61,7 @@
<undertow-embedded.version>1.0.0.Alpha2</undertow-embedded.version>
<version.org.wildfly.extras.creaper>1.6.1</version.org.wildfly.extras.creaper>
<testcontainers.version>1.5.1</testcontainers.version>
<appium.client.version>6.1.0</appium.client.version>
<!--migration properties-->
<migration.70.version>1.9.8.Final</migration.70.version>

View file

@ -186,7 +186,7 @@
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-arquillian-xml-and-password-blacklists</id>
<id>copy-common-dependencies</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
@ -199,6 +199,7 @@
<includes>
<include>arquillian.xml</include>
<include>password-blacklists/**</include>
<include>log4j.properties</include>
</includes>
<!--<filtering>true</filtering>-->
</resource>

View file

@ -18,7 +18,7 @@
package org.keycloak.testsuite.adapter.page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.keycloak.testsuite.arquillian.annotation.AppServerContext;
import org.keycloak.testsuite.arquillian.annotation.AppServerBrowserContext;
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
import java.net.URL;
@ -30,7 +30,7 @@ import java.net.URL;
public class AppServerContextRoot extends AbstractPageWithInjectedUrl {
@ArquillianResource
@AppServerContext
@AppServerBrowserContext
private URL appServerContextRoot;
@Override

View file

@ -22,9 +22,9 @@ import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.keycloak.testsuite.auth.page.login.OIDCLogin;
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.pages.ConsentPage;
import org.keycloak.testsuite.util.JavascriptBrowser;
import org.keycloak.testsuite.util.UIUtils;
import org.keycloak.testsuite.util.URLUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
@ -35,6 +35,7 @@ import java.net.URL;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.WaitUtils.pause;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
@ -104,8 +105,8 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
createAlbum.click();
WebElement albumNameInput = driver.findElement(By.id("album.name"));
waitUntilElement(albumNameInput).is().present();
Form.setInputValue(albumNameInput, name);
waitUntilElement(albumNameInput).attribute(Form.VALUE).contains(name);
UIUtils.setTextInputValue(albumNameInput, name);
waitUntilElement(albumNameInput).attribute(UIUtils.VALUE_ATTR_NAME).contains(name);
WebElement button = driver.findElement(By.id(buttonId));
waitUntilElement(button).is().clickable();
button.click();
@ -142,7 +143,7 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
public void navigateToAdminAlbum(boolean shouldBeDenied) {
log.debug("Navigating to Admin Album");
URLUtils.navigateToUri(toString() + "/#/admin/album", true);
URLUtils.navigateToUri(toString() + "/#/admin/album");
driver.navigate().refresh(); // This is sometimes necessary for loading the new policy settings
waitForPageToLoad();
@ -157,8 +158,7 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
public void logOut() {
navigateTo();
waitUntilElement(signOutButton).is().clickable(); // Sometimes doesn't work in PhantomJS!
signOutButton.click();
this.loginPage.form().waitForLoginButtonPresent();
clickLink(signOutButton);
}
public void requestEntitlement() {
@ -210,7 +210,7 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
urlWithScopeParam.append(scopesValue);
URLUtils.navigateToUri(urlWithScopeParam.toString(), true);
URLUtils.navigateToUri(urlWithScopeParam.toString());
}
this.loginPage.form().login(username, password);
@ -315,9 +315,9 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
public void accountShareResource(String name, String user) {
accountMyResource(name);
WebElement userIdInput = driver.findElement(By.id("user_id"));
Form.setInputValue(userIdInput, user);
UIUtils.setTextInputValue(userIdInput, user);
pause(200); // We need to wait a bit for the form to "accept" the input (otherwise it registers the input as empty)
waitUntilElement(userIdInput).attribute(Form.VALUE).contains(user);
waitUntilElement(userIdInput).attribute(UIUtils.VALUE_ATTR_NAME).contains(user);
WebElement shareButton = driver.findElement(By.id("share-button"));
waitUntilElement(shareButton).is().clickable();
@ -330,9 +330,9 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
accountMyResource(name);
WebElement userIdInput = driver.findElement(By.id("user_id"));
Form.setInputValue(userIdInput, user);
UIUtils.setTextInputValue(userIdInput, user);
pause(200); // We need to wait a bit for the form to "accept" the input (otherwise it registers the input as empty)
waitUntilElement(userIdInput).attribute(Form.VALUE).contains(user);
waitUntilElement(userIdInput).attribute(UIUtils.VALUE_ATTR_NAME).contains(user);
WebElement shareRemoveScope = driver.findElement(By.id("share-remove-scope-" + name + "-" + scope));
waitUntilElement(shareRemoveScope).is().clickable();
@ -379,13 +379,13 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
}
@Override
public void navigateTo(boolean waitForMatch) {
super.navigateTo(waitForMatch);
public void navigateTo() {
super.navigateTo();
pause(WAIT_AFTER_OPERATION);
}
@Override
public boolean isCurrent() {
return URLUtils.currentUrlStartWith(toString());
return URLUtils.currentUrlStartsWith(toString());
}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.arquillian;
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
@ -80,9 +81,9 @@ public class AppServerTestEnricher {
public static String getAppServerContextRoot(int clusterPortOffset) {
String host = System.getProperty("app.server.host", "localhost");
boolean sslRequired = Boolean.parseBoolean(System.getProperty("app.server.ssl.required"));
int port = sslRequired ? parsePort("app.server.https.port") : parsePort("app.server.http.port");
String scheme = sslRequired ? "https" : "http";
@ -97,6 +98,18 @@ public class AppServerTestEnricher {
}
}
public static String getAppServerBrowserContextRoot() throws MalformedURLException {
return getAppServerBrowserContextRoot(new URL(getAuthServerContextRoot()));
}
public static String getAppServerBrowserContextRoot(URL contextRoot) {
String browserHost = System.getProperty("app.server.browserHost");
if (StringUtils.isEmpty(browserHost)) {
browserHost = contextRoot.getHost();
}
return String.format("%s://%s:%s", contextRoot.getProtocol(), browserHost, contextRoot.getPort());
}
public void updateTestContextWithAppServerInfo(@Observes(precedence = 1) BeforeClass event) {
testContext = testContextInstance.get();
@ -104,7 +117,7 @@ public class AppServerTestEnricher {
if (appServerQualifiers == null) { // no adapter test
log.info("\n\n" + testContext);
return;
}
}
String appServerQualifier = null;
for (String qualifier : appServerQualifiers) {
@ -140,11 +153,12 @@ public class AppServerTestEnricher {
private ContainerInfo updateWithAppServerInfo(ContainerInfo appServerInfo, int clusterPortOffset) {
try {
String appServerContextRootStr = isRelative()
URL appServerContextRoot = new URL(isRelative()
? getAuthServerContextRoot(clusterPortOffset)
: getAppServerContextRoot(clusterPortOffset);
: getAppServerContextRoot(clusterPortOffset));
appServerInfo.setContextRoot(new URL(appServerContextRootStr));
appServerInfo.setContextRoot(appServerContextRoot);
appServerInfo.setBrowserContextRoot(new URL(getAppServerBrowserContextRoot(appServerContextRoot)));
} catch (MalformedURLException ex) {
throw new IllegalArgumentException(ex);
@ -198,12 +212,12 @@ public class AppServerTestEnricher {
/**
* Workaround for WFARQ-44. It cannot be used 'cleanServerBaseDir' property.
*
* It copies deployments and configuration into $JBOSS_HOME/standalone-test from where
*
* It copies deployments and configuration into $JBOSS_HOME/standalone-test from where
* the container is started for the test
*
*
* @param baseDir string representing folder name, relative to app.server.home, from which the copy is made
* @throws IOException
* @throws IOException
*/
public static void prepareServerDir(String baseDir) throws IOException {
log.debug("Creating cleanServerBaseDir from: " + baseDir);

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.testsuite.arquillian;
import org.apache.commons.lang.StringUtils;
import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.jboss.arquillian.container.spi.event.StartContainer;
import org.jboss.arquillian.container.spi.event.StartSuiteContainers;
@ -61,7 +62,7 @@ public class AuthServerTestEnricher {
protected static final Logger log = Logger.getLogger(AuthServerTestEnricher.class);
@Inject
@Inject
private Instance<ContainerController> containerConroller;
@Inject
private Instance<ContainerRegistry> containerRegistry;
@ -120,6 +121,18 @@ public class AuthServerTestEnricher {
return String.format("%s://%s:%s", scheme, host, port + clusterPortOffset);
}
public static String getAuthServerBrowserContextRoot() throws MalformedURLException {
return getAuthServerBrowserContextRoot(new URL(getAuthServerContextRoot()));
}
public static String getAuthServerBrowserContextRoot(URL contextRoot) {
String browserHost = System.getProperty("auth.server.browserHost");
if (StringUtils.isEmpty(browserHost)) {
browserHost = contextRoot.getHost();
}
return String.format("%s://%s:%s", contextRoot.getProtocol(), browserHost, contextRoot.getPort());
}
public static OnlineManagementClient getManagementClient() {
try {
return ManagementClient.online(OnlineOptions
@ -259,7 +272,10 @@ public class AuthServerTestEnricher {
private ContainerInfo updateWithAuthServerInfo(ContainerInfo authServerInfo, int clusterPortOffset) {
try {
authServerInfo.setContextRoot(new URL(getAuthServerContextRoot(clusterPortOffset)));
URL contextRoot = new URL(getAuthServerContextRoot(clusterPortOffset));
authServerInfo.setContextRoot(contextRoot);
authServerInfo.setBrowserContextRoot(new URL(getAuthServerBrowserContextRoot(contextRoot)));
} catch (MalformedURLException ex) {
throw new IllegalArgumentException(ex);
}

View file

@ -16,6 +16,7 @@ import java.util.Objects;
public class ContainerInfo implements Comparable<ContainerInfo> {
private URL contextRoot;
private URL browserContextRoot;
private Container arquillianContainer;
public ContainerInfo(Container arquillianContainer) {
@ -53,6 +54,14 @@ public class ContainerInfo implements Comparable<ContainerInfo> {
this.contextRoot = contextRoot;
}
public void setBrowserContextRoot(URL browserContextRoot) {
this.browserContextRoot = browserContextRoot;
}
public URL getBrowserContextRoot() {
return browserContextRoot;
}
public boolean isUndertow() {
return getQualifier().toLowerCase().contains("undertow");
}

View file

@ -79,7 +79,7 @@ public class KeycloakArquillianExtension implements LoadableExtension {
.override(ResourceProvider.class, ContainerCustomizableURLResourceProvider.class, URLProvider.class);
builder
.override(Configurator.class, WebDriverFactory.class, KeycloakWebDriverConfigurator.class)
.observer(KeycloakWebDriverConfigurator.class)
.observer(HtmlUnitScreenshots.class)
.observer(KeycloakDronePostSetup.class);

View file

@ -0,0 +1,36 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.arquillian.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
@Documented
@Retention(RUNTIME)
@Target({ElementType.FIELD})
public @interface AppServerBrowserContext
{
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.arquillian.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
@Documented
@Retention(RUNTIME)
@Target({ElementType.FIELD})
public @interface AuthServerBrowserContext
{
}

View file

@ -26,7 +26,9 @@ import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
import org.keycloak.testsuite.arquillian.SuiteContext;
import org.keycloak.testsuite.arquillian.TestContext;
import org.keycloak.testsuite.arquillian.annotation.AppServerBrowserContext;
import org.keycloak.testsuite.arquillian.annotation.AppServerContext;
import org.keycloak.testsuite.arquillian.annotation.AuthServerBrowserContext;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContext;
import java.lang.annotation.Annotation;
@ -108,6 +110,12 @@ public class URLProvider extends URLResourceProvider {
return appServerBackendsInfo.get(0).getContextRoot();
}
if (AuthServerBrowserContext.class.isAssignableFrom(a.annotationType())) {
return suiteContext.get().getAuthServerInfo().getBrowserContextRoot();
}
if (AppServerBrowserContext.class.isAssignableFrom(a.annotationType())) {
return testContext.get().getAppServerInfo().getBrowserContextRoot();
}
}
return url;

View file

@ -0,0 +1,137 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author tkyjovsk
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class AccountFields extends FieldsBase {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(xpath = "//label[@for='username']")
private WebElement usernameLabel;
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(xpath = "//label[@for='email']")
private WebElement emailLabel;
@FindBy(id = "firstName")
private WebElement firstNameInput;
@FindBy(xpath = "//label[@for='firstName']")
private WebElement firstNameLabel;
@FindBy(id = "lastName")
private WebElement lastNameInput;
@FindBy(xpath = "//label[@for='lastName']")
private WebElement lastNameLabel;
public void setUsername(String username) {
UIUtils.setTextInputValue(usernameInput, username);
}
public AccountFields setEmail(String email) {
UIUtils.setTextInputValue(emailInput, email);
return this;
}
public AccountFields setFirstName(String firstName) {
UIUtils.setTextInputValue(firstNameInput, firstName);
return this;
}
public AccountFields setLastName(String lastName) {
UIUtils.setTextInputValue(lastNameInput, lastName);
return this;
}
public String getUsername() {
return UIUtils.getTextInputValue(usernameInput);
}
public String getEmail() {
return UIUtils.getTextInputValue(emailInput);
}
public String getFirstName() {
return UIUtils.getTextInputValue(firstNameInput);
}
public String getLastName() {
return UIUtils.getTextInputValue(lastNameInput);
}
public void setValues(UserRepresentation user) {
setUsername(user.getUsername());
setEmail(user.getEmail());
setFirstName(user.getFirstName());
setLastName(user.getLastName());
}
public boolean isUsernamePresent() {
try {
return usernameInput.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public String getUsernameLabel() {
return getTextFromElement(usernameLabel);
}
public String getEmailLabel() {
return getTextFromElement(emailLabel);
}
public String getFirstNameLabel() {
return getTextFromElement(firstNameLabel);
}
public String getLastNameLabel() {
return getTextFromElement(lastNameLabel);
}
public boolean hasUsernameError() {
return hasFieldError(usernameInput);
}
public boolean hasEmailError() {
return hasFieldError(emailInput);
}
public boolean hasFirstNameError() {
return hasFieldError(firstNameInput);
}
public boolean hasLastNameError() {
return hasFieldError(lastNameInput);
}
}

View file

@ -18,7 +18,7 @@
package org.keycloak.testsuite.auth.page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContext;
import org.keycloak.testsuite.arquillian.annotation.AuthServerBrowserContext;
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
import java.net.URL;
@ -33,7 +33,7 @@ import java.net.URL;
public class AuthServerContextRoot extends AbstractPageWithInjectedUrl {
@ArquillianResource
@AuthServerContext
@AuthServerBrowserContext
private URL authServerContextRoot;
@Override

View file

@ -1,5 +1,5 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,27 +15,17 @@
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account;
package org.keycloak.testsuite.auth.page;
import org.keycloak.testsuite.page.Form;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
*
* @author tkyjovsk
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ContactInfoFields extends Form {
@FindBy(id = "user.attributes.street")
private WebElement streetInput;
@FindBy(id = "user.attributes.locality")
private WebElement localityInput;
@FindBy(id = "user.attributes.region")
private WebElement regionInput;
@FindBy(id = "user.attributes.postal_code")
private WebElement postalCodeInput;
@FindBy(id = "user.attributes.country")
private WebElement counryInput;
public class FieldsBase extends Form {
protected boolean hasFieldError(WebElement field) {
return field.findElement(By.xpath("../..")).getAttribute("class").contains("has-error");
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author tkyjovsk
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PasswordFields extends FieldsBase {
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(xpath = "//label[@for='password']")
private WebElement passwordLabel;
@FindBy(id = "password-new")
private WebElement newPasswordInput;
@FindBy(xpath = "//label[@for='password-new']")
private WebElement newPasswordLabel;
@FindBy(id = "password-confirm")
private WebElement confirmPasswordInput;
@FindBy(xpath = "//label[@for='password-confirm']")
private WebElement confirmPasswordLabel;
public void setPassword(String password) {
UIUtils.setTextInputValue(passwordInput, password);
}
public void setNewPassword(String newPassword) {
UIUtils.setTextInputValue(newPasswordInput, newPassword);
}
public void setConfirmPassword(String confirmPassword) {
UIUtils.setTextInputValue(confirmPasswordInput, confirmPassword);
}
public void setPasswords(String password, String newPassword, String confirmPassword) {
setPassword(password);
setNewPassword(newPassword);
setConfirmPassword(confirmPassword);
}
public boolean isConfirmPasswordPresent() {
try {
return confirmPasswordInput.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public String getPasswordLabel() {
return getTextFromElement(passwordLabel);
}
public String getNewPasswordLabel() {
return getTextFromElement(newPasswordLabel);
}
public String getConfirmPasswordLabel() {
return getTextFromElement(confirmPasswordLabel);
}
public boolean hasPasswordError() {
return hasFieldError(passwordInput);
}
public boolean hasNewPasswordError() {
return hasFieldError(newPasswordInput);
}
public boolean hasConfirmPasswordError() {
return hasFieldError(confirmPasswordInput);
}
}

View file

@ -21,7 +21,7 @@ import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.page.Form.setInputValue;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
@ -47,9 +47,9 @@ public class WelcomePage extends AuthServer {
}
public void setPassword(String username, String password) {
setInputValue(usernameInput, username);
setInputValue(passwordInput, password);
setInputValue(passwordConfirmationInput, password);
setTextInputValue(usernameInput, username);
setTextInputValue(passwordInput, password);
setTextInputValue(passwordConfirmationInput, password);
clickLink(createButton);

View file

@ -79,7 +79,7 @@ public class Account extends AccountManagement {
}
public boolean isCurrent() {
return URLUtils.currentUrlStartWith(toString()); // Sometimes after login the URL ends with /# or similar
return URLUtils.currentUrlStartsWith(toString()); // Sometimes after login the URL ends with /# or similar
}
}

View file

@ -1,89 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.account;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.page.Form;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElementIsNotPresent;
/**
*
* @author tkyjovsk
*/
public class AccountFields extends Form {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(id = "firstName")
private WebElement firstNameInput;
@FindBy(id = "lastName")
private WebElement lastNameInput;
public void setUsername(String username) {
Form.setInputValue(usernameInput, username);
}
public AccountFields setEmail(String email) {
Form.setInputValue(emailInput, email);
return this;
}
public AccountFields setFirstName(String firstName) {
Form.setInputValue(firstNameInput, firstName);
return this;
}
public AccountFields setLastName(String lastName) {
Form.setInputValue(lastNameInput, lastName);
return this;
}
public String getEmail() {
return Form.getInputValue(emailInput);
}
public String getFirstName() {
return Form.getInputValue(firstNameInput);
}
public String getLastName() {
return Form.getInputValue(lastNameInput);
}
public void setValues(UserRepresentation user) {
setUsername(user.getUsername());
setEmail(user.getEmail());
setFirstName(user.getFirstName());
setLastName(user.getLastName());
}
public void waitForUsernameInputPresent() {
waitUntilElement(usernameInput).is().present();
}
public void waitForUsernameInputNotPresent() {
waitUntilElementIsNotPresent(usernameInput);
}
}

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.auth.page.account;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.PasswordFields;
import javax.ws.rs.core.UriBuilder;

View file

@ -1,59 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.account;
import org.keycloak.testsuite.page.Form;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
*
* @author tkyjovsk
*/
public class PasswordFields extends Form {
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(id = "password-new")
private WebElement newPasswordInput;
@FindBy(id = "password-confirm")
private WebElement confirmPasswordInput;
public void setPassword(String password) {
setInputValue(passwordInput, password);
}
public void setNewPassword(String newPassword) {
setInputValue(newPasswordInput, newPassword);
}
public void setConfirmPassword(String confirmPassword) {
setInputValue(confirmPasswordInput, confirmPassword);
}
public void setPasswords(String password, String newPassword, String confirmPassword) {
setPassword(password);
setNewPassword(newPassword);
setConfirmPassword(confirmPassword);
}
public void waitForConfirmPasswordInputPresent() {
waitUntilElement(confirmPasswordInput).is().present();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,8 +17,6 @@
package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import javax.ws.rs.core.UriBuilder;
/**
@ -26,17 +24,8 @@ import javax.ws.rs.core.UriBuilder;
* @author tkyjovsk
*/
public abstract class Authenticate extends LoginActions {
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("authenticate");
}
@Page
private LoginForm login;
public LoginForm loginForm() {
return login;
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.login;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class FeedbackMessage {
@FindBy(css = ".alert")
private WebElement alertRoot;
public boolean isPresent() {
try {
return alertRoot.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public String getText() {
return getTextFromElement(alertRoot.findElement(By.className("kc-feedback-text")));
}
public String getType() {
String cssClass = alertRoot.getAttribute("class");
Matcher classMatcher = Pattern.compile("alert-(.+)").matcher(cssClass);
if (!classMatcher.find()) {
throw new RuntimeException("Failed to identify feedback message type");
}
return classMatcher.group(1);
}
public boolean isSuccess() {
return getType().equals("success");
}
public boolean isWarning() {
return getType().equals("warning");
}
public boolean isError() {
return getType().equals("error");
}
public boolean isInfo() {
return getType().equals("info");
}
}

View file

@ -17,25 +17,20 @@
package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.openqa.selenium.By;
import javax.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
*
* @author Petr Mensik
* @author tkyjovsk
*/
public abstract class Login extends AuthRealm {
public abstract class Login extends LoginBase {
public static final String PROTOCOL = "protocol";
public static final String OIDC = "openid-connect";
public static final String SAML = "saml";
public static final String LOGIN_ACTION = "login-action";
private String keycloakThemeCssName;
@Override
public UriBuilder createUriBuilder() {
@ -58,23 +53,4 @@ public abstract class Login extends AuthRealm {
return form;
}
public void setKeycloakThemeCssName(String name) {
keycloakThemeCssName = name;
}
protected By getKeycloakThemeLocator() {
if (keycloakThemeCssName == null) {
throw new IllegalStateException("keycloakThemeCssName property must be set");
}
return By.cssSelector("link[href*='login/" + keycloakThemeCssName + "/css/login.css']");
}
public void waitForKeycloakThemeNotPresent() {
waitUntilElement(getKeycloakThemeLocator()).is().not().present();
}
public void waitForKeycloakThemePresent() {
waitUntilElement(getKeycloakThemeLocator()).is().present();
}
}

View file

@ -16,22 +16,19 @@
*/
package org.keycloak.testsuite.auth.page.login;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.util.URLUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import javax.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
*
* @author tkyjovsk
*/
public class LoginActions extends AuthRealm {
@FindBy(id = "kc-page-title")
protected WebElement heading;
public class LoginActions extends LoginBase {
@Override
public UriBuilder createUriBuilder() {
@ -42,23 +39,12 @@ public class LoginActions extends AuthRealm {
@FindBy(css = "input[type='submit']")
private WebElement submitButton;
@FindBy(css = "div[id='kc-form-options'] span a")
private WebElement backToLoginForm;
@FindBy(xpath = "//span[@class='kc-feedback-text' and string-length(text())>1]")
private WebElement feedbackText;
public String getFeedbackText() {
waitUntilElement(feedbackText, "Feedback message should be present").is().visible();
return feedbackText.getText();
}
public void backToLoginPage() {
backToLoginForm.click();
}
public void submit() {
submitButton.click();
clickLink(submitButton);
}
@Override
public boolean isCurrent() {
return URLUtils.currentUrlStartsWith(toString() + "?"); // ignore the query string
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.console.page.fragment.LocaleDropdown;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class LoginBase extends AuthRealm {
@Page
protected FeedbackMessage feedbackMessage;
@FindBy(id = "kc-page-title")
protected WebElement title;
@FindBy(id = "kc-header-wrapper")
protected WebElement header;
@FindBy(id = "kc-locale-dropdown")
private LocaleDropdown localeDropdown;
protected String keycloakThemeCssName;
public FeedbackMessage feedbackMessage() {
return feedbackMessage;
}
public String getTitleText() {
return getTextFromElement(title);
}
public String getHeaderText() {
return getTextFromElement(header);
}
protected By getKeycloakThemeLocator() {
if (keycloakThemeCssName == null) {
throw new IllegalStateException("keycloakThemeCssName property must be set");
}
return By.cssSelector("link[href*='login/" + keycloakThemeCssName + "/css/login.css']");
}
public void waitForKeycloakThemeNotPresent() {
waitUntilElement(getKeycloakThemeLocator()).is().not().present();
}
public void waitForKeycloakThemePresent() {
waitUntilElement(getKeycloakThemeLocator()).is().present();
}
public void setKeycloakThemeCssName(String name) {
keycloakThemeCssName = name;
}
public LocaleDropdown localeDropdown() {
return localeDropdown;
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.login;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LoginError extends LoginBase {
@FindBy(xpath = "//div[@id='kc-error-message']/p[@class='instruction']")
private WebElement errorMessage;
@FindBy(id = "backToApplication")
private WebElement backToApplicationLink;
public String getErrorMessage() {
return getTextFromElement(errorMessage);
}
public void backToApplication() {
clickLink(backToApplicationLink);
}
@Override
public boolean isCurrent() {
return getTitleText().equals("We're sorry...");
}
}

View file

@ -18,16 +18,17 @@ package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.account.AccountFields;
import org.keycloak.testsuite.auth.page.account.PasswordFields;
import org.keycloak.testsuite.auth.page.AccountFields;
import org.keycloak.testsuite.auth.page.PasswordFields;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.admin.Users.getPasswordOf;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElementIsNotPresent;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
*
@ -44,18 +45,16 @@ public class LoginForm extends Form {
@FindBy(name = "login")
private WebElement loginButton;
// @FindBy(name = "cancel")
// private WebElement cancelButton;
@FindBy(xpath = "//div[@id='kc-registration']/span/a")
private WebElement registerLink;
@FindBy(linkText = "Forgot Password?")
private WebElement forgottenPassword;
@FindBy(id = "rememberMe")
private WebElement rememberMe;
@FindBy(xpath = ".//label[@for='password']")
private WebElement labelPassword;
@FindBy(xpath = "//input[@id='rememberMe']/parent::label")
private WebElement rememberMeLabel;
public void setUsername(String username) {
accountFields.setUsername(username);
@ -94,61 +93,84 @@ public class LoginForm extends Form {
}
}
// @Override
// public void cancel() {
// waitUntilElement(cancelButton).is().present();
// cancelButton.click();
// }
public void waitForUsernameInputPresent() {
accountFields.waitForUsernameInputPresent();
public boolean isRememberMe() {
return rememberMe.isSelected();
}
public void waitForRegisterLinkNotPresent() {
waitUntilElementIsNotPresent(registerLink);
public boolean isUsernamePresent() {
return accountFields.isUsernamePresent();
}
public void waitForResetPasswordLinkNotPresent() {
waitUntilElement(forgottenPassword).is().not().present();
public boolean isRegisterLinkPresent() {
try {
return registerLink.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void waitForRememberMePresent() {
waitUntilElement(rememberMe).is().present();
public boolean isForgotPasswordLinkPresent() {
try {
return forgottenPassword.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void waitForRememberMeNotPresent() {
waitUntilElementIsNotPresent(rememberMe);
public boolean isRememberMePresent() {
try {
return rememberMe.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void waitForLoginButtonPresent() {
waitUntilElement(loginButton).is().present();
public boolean isLoginButtonPresent() {
try {
return loginButton.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public TotpSetupForm totpForm() {
return totpForm;
}
public String getUsernameLabel() {
return accountFields.getUsernameLabel();
}
public String getPasswordLabel() {
return passwordFields.getPasswordLabel();
}
public String getRememberMeLabel() {
return getTextFromElement(rememberMeLabel);
}
public class TotpSetupForm extends Form {
@FindBy(id = "totp")
private WebElement totpInputField;
@FindBy(id = "totpSecret")
private WebElement totpSecret;
@FindBy(xpath = ".//input[@value='Submit']")
private WebElement submit;
public void waitForTotpInputFieldPresent() {
waitUntilElement(totpInputField).is().present();
}
public void setTotp(String value) {
setInputValue(totpInputField, value);
UIUtils.setTextInputValue(totpInputField, value);
}
public String getTotpSecret() {
return totpSecret.getAttribute(VALUE);
return totpSecret.getAttribute(UIUtils.VALUE_ATTR_NAME);
}
public void submit() {
clickLink(submit);
}

View file

@ -16,40 +16,56 @@
*/
package org.keycloak.testsuite.auth.page.login;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.testsuite.util.DroneUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class OAuthGrant extends LoginActions {
public class OAuthGrant extends RequiredActions {
@FindBy(css = "input[name=\"accept\"]")
private WebElement acceptButton;
@FindBy(css = "input[name=\"cancel\"]")
private WebElement cancelButton;
@FindBy(xpath = "//div[@id='kc-oauth']/ul/li/span")
private List<WebElement> scopesToApprove;
@Override
public String getActionId() {
return AuthenticatedClientSessionModel.Action.OAUTH_GRANT.name();
}
public void accept() {
acceptButton.click();
clickLink(acceptButton);
}
public void cancel() {
cancelButton.click();
clickLink(cancelButton);
}
public boolean isCurrent(WebDriver driver1) {
if (driver1 == null) driver1 = driver;
waitForPageToLoad();
return driver1.getPageSource().contains("Do you grant these access privileges");
DroneUtils.addWebDriver(driver1);
boolean ret = super.isCurrent();
DroneUtils.removeWebDriver();
return ret;
}
@Override
public boolean isCurrent() {
return isCurrent(null);
public void assertClientScopes(List<String> expectedScopes) {
List<String> actualScopes = scopesToApprove.stream().map(WebElement::getText).collect(Collectors.toList());
assertTrue("Expected and actual Client Scopes to approve don't match",
CollectionUtil.collectionEquals(expectedScopes, actualScopes)); // order of scopes doesn't matter
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.models.UserModel;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class OTPSetup extends RequiredActions {
@Page
private LoginForm.TotpSetupForm form;
@FindBy(id = "kc-totp-secret-qr-code")
private WebElement barcodeImg;
@FindBy(id = "kc-totp-secret-key")
private WebElement secretKey;
@FindBy(id = "mode-manual")
private WebElement manualModeLink;
@FindBy(id = "mode-barcode")
private WebElement barcodeModeLink;
@FindBy(id = "kc-totp-type")
private WebElement otpType;
@FindBy(id = "kc-totp-algorithm")
private WebElement otpAlgorithm;
@FindBy(id = "kc-totp-digits")
private WebElement otpDigits;
@FindBy(id = "kc-totp-period")
private WebElement otpPeriod;
@FindBy(id = "kc-totp-counter")
private WebElement otpCounter;
public void setTotp(String value) {
form.setTotp(value);
}
public boolean isBarcodePresent() {
try {
return barcodeImg.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public String getSecretKey() {
return secretKey.getText().replace(" ", "");
}
public boolean isSecretKeyPresent() {
try {
return secretKey.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void clickManualMode() {
clickLink(manualModeLink);
}
public void clickBarcodeMode() {
clickLink(barcodeModeLink);
}
public String getOtpType() {
return otpType.getText();
}
public String getOtpAlgorithm() {
return otpAlgorithm.getText();
}
public String getOtpDigits() {
return otpDigits.getText();
}
public String getOtpPeriod() {
return otpPeriod.getText();
}
public String getOtpCounter() {
return otpCounter.getText();
}
@Override
public String getActionId() {
return UserModel.RequiredAction.CONFIGURE_TOTP.name();
}
}

View file

@ -17,18 +17,47 @@
package org.keycloak.testsuite.auth.page.login;
import org.keycloak.testsuite.auth.page.login.LoginForm.TotpSetupForm;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
*
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
*/
public class OneTimeCode extends Authenticate {
@FindBy(id = "kc-totp-login-form")
private TotpSetupForm form;
@FindBy(xpath = ".//label[@for='totp']")
private WebElement totpInputLabel;
public TotpSetupForm form() {
return form;
}
public String getTotpLabel() {
return getTextFromElement(totpInputLabel);
}
public boolean isTotpLabelPresent() {
try {
return totpInputLabel.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void sendCode(String code) {
form.setTotp(code);
submit();
}
@Override
public boolean isCurrent() {
return super.isCurrent() && isTotpLabelPresent();
}
}

View file

@ -18,17 +18,19 @@ package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.account.AccountFields;
import org.keycloak.testsuite.auth.page.account.ContactInfoFields;
import org.keycloak.testsuite.auth.page.account.PasswordFields;
import org.keycloak.testsuite.auth.page.AccountFields;
import org.keycloak.testsuite.auth.page.PasswordFields;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import javax.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.admin.Users.getPasswordOf;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
*
* @author Filip Kiss
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class Registration extends LoginActions {
@ -44,8 +46,8 @@ public class Registration extends LoginActions {
@Page
private PasswordFields passwordFields;
@Page
private ContactInfoFields contactInfoFields;
@FindBy(xpath = "//a[contains(., 'Back to Login')]")
private WebElement backToLoginLink;
public void register(UserRepresentation user) {
setValues(user);
@ -62,16 +64,23 @@ public class Registration extends LoginActions {
passwordFields.setConfirmPassword(confirmPassword);
}
public void waitForUsernameInputPresent() {
accountFields.waitForUsernameInputPresent();
}
public void waitForUsernameInputNotPresent() {
accountFields.waitForUsernameInputNotPresent();
public boolean isUsernamePresent() {
return accountFields.isUsernamePresent();
}
public void waitForConfirmPasswordInputPresent() {
passwordFields.waitForConfirmPasswordInputPresent();
public boolean isConfirmPasswordPresent() {
return passwordFields.isConfirmPasswordPresent();
}
public AccountFields accountFields() {
return accountFields;
}
public PasswordFields passwordFields() {
return passwordFields;
}
public void backToLogin() {
clickLink(backToLoginLink);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.auth.page.login;
import org.keycloak.testsuite.util.URLUtils;
import javax.ws.rs.core.UriBuilder;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class RequiredActions extends LoginActions {
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("required-action");
}
public abstract String getActionId();
@Override
public boolean isCurrent() {
return URLUtils.currentUrlWithQueryEquals(toString(), "execution=" + getActionId());
}
}

View file

@ -16,20 +16,19 @@
*/
package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account.AccountFields;
import org.keycloak.testsuite.auth.page.account.PasswordFields;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import javax.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
*
* @author vramik
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ResetCredentials extends LoginActions {
@ -38,27 +37,25 @@ public class ResetCredentials extends LoginActions {
return super.createUriBuilder().path("reset-credentials");
}
@Page
private AccountFields accountFields;
@Page
private PasswordFields passwordFields;
@FindBy(id = "username")
private WebElement usernameOrEmailInput;
@FindBy(xpath = "//a[contains(., 'Back to Login')]")
private WebElement backToLoginLink;
@FindBy(id = "kc-info")
private WebElement info;
public void resetCredentials(String value) {
accountFields.setUsername(value);
submit();
}
public void updatePassword(String password) {
passwordFields.setNewPassword(password);
passwordFields.setConfirmPassword(password);
public void resetCredentials(String usernameOrEmail) {
setTextInputValue(usernameOrEmailInput, usernameOrEmail);
submit();
}
public void backToLogin() {
clickLink(backToLoginLink);
}
public String getInfoMessage() {
waitUntilElement(info, "Info message should be visible").is().present();
return info.getText();
return getTextFromElement(info);
}
}

View file

@ -16,14 +16,16 @@
*/
package org.keycloak.testsuite.auth.page.login;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class TermsAndConditions extends LoginActions {
public class TermsAndConditions extends RequiredActions {
@FindBy(id = "kc-accept")
private WebElement acceptButton;
@ -33,19 +35,17 @@ public class TermsAndConditions extends LoginActions {
@FindBy(id = "kc-terms-text")
private WebElement textElem;
@Override
public boolean isCurrent() {
return heading.getText().equals("Terms and Conditions");
public String getActionId() {
return "terms_and_conditions";
}
public void acceptTerms() {
acceptButton.click();
WaitUtils.waitForPageToLoad();
clickLink(acceptButton);
}
public void declineTerms() {
declineButton.click();
WaitUtils.waitForPageToLoad();
clickLink(declineButton);
}
public String getAcceptButtonText() {
@ -57,7 +57,7 @@ public class TermsAndConditions extends LoginActions {
}
public String getText() {
return textElem.getText();
return getTextFromElement(textElem);
}
}

View file

@ -18,18 +18,24 @@
package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.account.AccountFields;
import org.keycloak.testsuite.auth.page.AccountFields;
/**
*
* @author tkyjovsk
*/
public class UpdateAccount extends Authenticate {
public class UpdateAccount extends RequiredActions {
@Page
private AccountFields accountFields;
@Override
public String getActionId() {
return UserModel.RequiredAction.UPDATE_PROFILE.name();
}
public void updateAccount(UserRepresentation user) {
updateAccount(user.getEmail(), user.getFirstName(), user.getLastName());
}

View file

@ -18,21 +18,31 @@
package org.keycloak.testsuite.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account.PasswordFields;
import org.keycloak.models.UserModel;
import org.keycloak.testsuite.auth.page.PasswordFields;
/**
*
* @author tkyjovsk
*/
public class UpdatePassword extends Authenticate {
public class UpdatePassword extends RequiredActions {
@Page
private PasswordFields passwordFields;
@Override
public String getActionId() {
return UserModel.RequiredAction.UPDATE_PASSWORD.name();
}
public void updatePasswords(String newPassword, String confirmPassword) {
passwordFields.setNewPassword(newPassword);
passwordFields.setConfirmPassword(confirmPassword);
submit();
}
public PasswordFields fields() {
return passwordFields;
}
}

View file

@ -16,26 +16,35 @@
*/
package org.keycloak.testsuite.auth.page.login;
import org.keycloak.models.UserModel;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
*
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
*/
public class VerifyEmail extends Authenticate {
public class VerifyEmail extends RequiredActions {
@FindBy(xpath = "//div[@id='kc-content-wrapper']/p[contains(@class, 'instruction')][1]")
private WebElement instruction;
@FindBy(id = "kc-error-message")
private WebElement error;
@FindBy(xpath = "//div[@id='kc-content-wrapper']/p[contains(@class, 'instruction')][2]/a[text()='Click here']")
private WebElement resendLink;
@Override
public String getActionId() {
return UserModel.RequiredAction.VERIFY_EMAIL.name();
}
public String getInstructionMessage() {
return instruction.getText();
return getTextFromElement(instruction);
}
public String getErrorMessage() {
return error.getText();
public void clickResend() {
clickLink(resendLink);
}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.DataTable;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -98,23 +99,23 @@ public class AdminEvents extends Events {
}
public void setResourcePathInput(String value) {
setInputValue(resourcePathInput, value);
UIUtils.setTextInputValue(resourcePathInput, value);
}
public void setRealmInput(String value) {
setInputValue(realmInput, value);
UIUtils.setTextInputValue(realmInput, value);
}
public void setClientInput(String value) {
setInputValue(clientInput, value);
UIUtils.setTextInputValue(clientInput, value);
}
public void setUserInput(String value) {
setInputValue(userInput, value);
UIUtils.setTextInputValue(userInput, value);
}
public void setIpAddressInput(String value) {
setInputValue(ipAddressInput, value);
UIUtils.setTextInputValue(ipAddressInput, value);
}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -106,7 +107,7 @@ public class Config extends Events {
public void setExpiration(String value, String unit) {
expirationUnitSelect.selectByVisibleText(unit);
Form.setInputValue(expirationInput, value);
UIUtils.setTextInputValue(expirationInput, value);
}
public void setSaveAdminEvents(boolean value) {

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.DataTable;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -89,11 +90,11 @@ public class LoginEvents extends Events {
}
public void setClientInput(String value) {
setInputValue(clientInput, value);
UIUtils.setTextInputValue(clientInput, value);
}
public void setUserInput(String value) {
setInputValue(userInput, value);
UIUtils.setTextInputValue(userInput, value);
}
}
}

View file

@ -1,33 +0,0 @@
package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class Dropdown {
@Root
private WebElement dropDownRoot; // MUST be .kc-dropdown
@FindBy(id = "kc-current-locale-link")
private WebElement localeLink;
@ArquillianResource
private WebDriver driver;
public String getSelected() {
return localeLink.getText();
}
public void selectByText(String text) {
localeLink.click();
clickLink(dropDownRoot.findElement(By.xpath("./ul/li/a[text()='" + text + "']")));
}
}

View file

@ -23,7 +23,7 @@ import org.openqa.selenium.support.FindBy;
import java.util.ArrayList;
import java.util.List;
import static org.keycloak.testsuite.page.Form.getInputValue;
import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
/**
*
@ -37,7 +37,7 @@ public class InputList {
public List<String> getValues() {
List<String> values = new ArrayList<>();
for (WebElement input: inputs) {
values.add(getInputValue(input));
values.add(getTextInputValue(input));
}
return values;
}

View file

@ -0,0 +1,62 @@
package org.keycloak.testsuite.console.page.fragment;
import io.appium.java_client.ios.IOSDriver;
import org.jboss.arquillian.graphene.fragment.Root;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LocaleDropdown {
@Root
private WebElement root;
@FindBy(tagName = "ul")
private WebElement dropDownMenu;
@FindBy(id = "kc-current-locale-link")
private WebElement currentLocaleLink;
@ArquillianResource
private WebDriver driver;
public String getSelected() {
return getTextFromElement(currentLocaleLink);
}
public void selectByText(String text) {
// open the menu
if (driver instanceof FirefoxDriver) { // GeckoDriver hack
Actions actions = new Actions(driver);
actions.moveToElement(root).perform();
pause(500);
}
else if (driver instanceof IOSDriver) { // TODO: Fix this! It's a very, very, ... very nasty hack for Safari on iOS - see KEYCLOAK-7947
((IOSDriver) driver).executeScript("arguments[0].setAttribute('style', 'display: block')", dropDownMenu);
}
else {
root.click();
}
// click desired locale
clickLink(dropDownMenu.findElement(By.xpath("./li/a[text()='" + text + "']")));
}
public void selectAndAssert(String text) {
assertNotEquals(text, getSelected());
selectByText(text);
assertEquals(text, getSelected());
}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.drone;
import java.util.concurrent.TimeUnit;
import io.appium.java_client.AppiumDriver;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.drone.spi.DroneContext;
import org.jboss.arquillian.drone.spi.DronePoint;
@ -44,11 +45,11 @@ public class KeycloakDronePostSetup {
Object drone = droneContext.get(dronePoint).getInstance();
if (drone instanceof WebDriver) {
if (drone instanceof WebDriver && !(drone instanceof AppiumDriver)) {
WebDriver webDriver = (WebDriver) drone;
configureDriverSettings(webDriver);
} else {
log.warn("Drone is not instanceof WebDriver! Drone is " + drone);
log.warn("Drone is not instanceof WebDriver for a desktop browser! Drone is " + drone);
}
if (drone instanceof GrapheneProxyInstance) {

View file

@ -17,107 +17,109 @@
package org.keycloak.testsuite.drone;
import java.util.ArrayList;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.drone.spi.DroneContext;
import org.jboss.arquillian.drone.spi.event.BeforeDroneInstantiated;
import org.jboss.arquillian.drone.webdriver.configuration.WebDriverConfiguration;
import org.jboss.arquillian.drone.webdriver.spi.BrowserCapabilities;
import org.jboss.arquillian.drone.webdriver.spi.BrowserCapabilitiesRegistry;
import org.jboss.logging.Logger;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.drone.spi.Configurator;
import org.jboss.arquillian.drone.spi.DronePoint;
import org.jboss.arquillian.drone.webdriver.configuration.WebDriverConfiguration;
import org.jboss.arquillian.drone.webdriver.factory.BrowserCapabilitiesList;
import org.jboss.arquillian.drone.webdriver.factory.BrowserCapabilitiesList.PhantomJS;
import org.jboss.arquillian.drone.webdriver.factory.WebDriverFactory;
import org.jboss.logging.Logger;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class KeycloakWebDriverConfigurator extends WebDriverFactory implements Configurator<WebDriver, WebDriverConfiguration> {
public class KeycloakWebDriverConfigurator {
protected final Logger log = Logger.getLogger(KeycloakWebDriverConfigurator.class);
@Override
public int getPrecedence() {
return 1;
@Inject
private Instance<BrowserCapabilitiesRegistry> registryInstance;
public void createConfiguration(@Observes BeforeDroneInstantiated event, DroneContext droneContext) {
WebDriverConfiguration webDriverCfg = droneContext.get(event.getDronePoint()).getConfigurationAs(WebDriverConfiguration.class);
DesiredCapabilities capabilitiesToAdd = new DesiredCapabilities();
updateCapabilityKeys("htmlUnit", webDriverCfg, capabilitiesToAdd);
updateCapabilityKeys("appium", webDriverCfg, capabilitiesToAdd);
configurePhantomJSDriver(webDriverCfg, capabilitiesToAdd);
BrowserCapabilities browserCap = registryInstance.get().getEntryFor(webDriverCfg.getBrowser());
webDriverCfg.setBrowserInternal(new KcBrowserCapabilities(capabilitiesToAdd, browserCap));
}
@Override
public WebDriverConfiguration createConfiguration(ArquillianDescriptor descriptor, DronePoint<WebDriver> dronePoint) {
WebDriverConfiguration webDriverCfg = super.createConfiguration(descriptor, dronePoint);
if (webDriverCfg.getBrowser().equals("htmlUnit")) {
updateCapabilities(webDriverCfg);
} else if (webDriverCfg.getBrowser().equals("phantomjs")) {
configurePhantomJSDriver(webDriverCfg);
private void configurePhantomJSDriver(WebDriverConfiguration webDriverCfg, DesiredCapabilities capabilitiesToAdd) {
if (!webDriverCfg.getBrowser().equals("phantomjs")) {
return;
}
return webDriverCfg;
}
String cliArgs = System.getProperty("keycloak.phantomjs.cli.args");
private void configurePhantomJSDriver(WebDriverConfiguration webDriverCfg) {
webDriverCfg.setBrowserInternal(new PhantomJS() {
@Override
public Map<String, ?> getRawCapabilities() {
List<String> cliArgs = new ArrayList<>();
String cliArgsProperty = System.getProperty("keycloak.phantomjs.cli.args");
if (cliArgs == null) {
cliArgs = "--ignore-ssl-errors=true --web-security=false";
}
if (cliArgsProperty != null) {
cliArgs = Arrays.asList(cliArgsProperty.split(" "));
} else {
cliArgs.add("--ignore-ssl-errors=true");
cliArgs.add("--web-security=false");
}
Map<String, Object> mergedCapabilities = new HashMap<>(super.getRawCapabilities());
mergedCapabilities.put(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cliArgs.toArray(new String[cliArgs.size()]));
return mergedCapabilities;
}
});
capabilitiesToAdd.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cliArgs);
}
// This is to ensure that default value of capabilities like "version" will be used just for the HtmlUnitDriver, but not for other drivers.
// Hence in configs we have "htmlUnit.version" instead of "version"
protected void updateCapabilities(WebDriverConfiguration configuration) {
Map<String, Object> newCapabilities = new HashMap<>();
for (Map.Entry<String, ?> capability : configuration.getCapabilities().asMap().entrySet()) {
if (capability.getKey().startsWith("htmlUnit.")) {
newCapabilities.put(capability.getKey().substring(9), capability.getValue());
}
private void updateCapabilityKeys(String browser, WebDriverConfiguration webDriverCfg, DesiredCapabilities capabilitiesToAdd, String... exclude) {
if (!webDriverCfg.getBrowser().toLowerCase().equals(browser.toLowerCase())) {
return;
}
log.debug("Adding new capabilities for HtmlUnitDriver: " + newCapabilities);
List excludeList = Arrays.asList(exclude);
KcHtmlUnitCapabilities mergedBrowser = new KcHtmlUnitCapabilities(newCapabilities);
configuration.setBrowserInternal(mergedBrowser);
String key = browser + ".";
int keyLength = key.length();
for (Map.Entry<String, ?> capability : webDriverCfg.getCapabilities().asMap().entrySet()) {
if (!excludeList.contains(capability.getKey()) && capability.getKey().startsWith(key)) {
capabilitiesToAdd.setCapability(capability.getKey().substring(keyLength), capability.getValue());
}
}
}
public class KcBrowserCapabilities implements BrowserCapabilities {
private Capabilities capabilitiesToAdd;
private BrowserCapabilities origBrowserCapabilities;
private static class KcHtmlUnitCapabilities extends BrowserCapabilitiesList.HtmlUnit {
public KcBrowserCapabilities(Capabilities capabilitiesToAdd, BrowserCapabilities origBrowserCapabilities) {
this.capabilitiesToAdd = capabilitiesToAdd;
this.origBrowserCapabilities = origBrowserCapabilities;
}
private final Map<String, Object> newCapabilities;
public KcHtmlUnitCapabilities(Map<String, Object> newCapabilities) {
this.newCapabilities = newCapabilities;
@Override
public String getImplementationClassName() {
return origBrowserCapabilities.getImplementationClassName();
}
@Override
public Map<String, ?> getRawCapabilities() {
Map<String, ?> parent = super.getRawCapabilities();
Map<String, Object> merged = new HashMap<>(parent);
merged.putAll(newCapabilities);
return merged;
Map<String, Object> ret = new HashMap<>(origBrowserCapabilities.getRawCapabilities());
ret.putAll(capabilitiesToAdd.asMap());
return ret;
}
@Override
public String getReadableName() {
return origBrowserCapabilities.getReadableName();
}
@Override
public int getPrecedence() {
return origBrowserCapabilities.getPrecedence();
}
}
}

View file

@ -92,15 +92,11 @@ public abstract class AbstractPage {
}
public void navigateTo() {
navigateTo(true);
}
public void navigateTo(boolean waitForMatch) {
URLUtils.navigateToUri(buildUri().toASCIIString(), waitForMatch);
URLUtils.navigateToUri(buildUri().toASCIIString());
}
public boolean isCurrent() {
return URLUtils.currentUrlEqual(toString());
return URLUtils.currentUrlEquals(toString());
}
public void assertCurrent() {

View file

@ -19,7 +19,6 @@ package org.keycloak.testsuite.page;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -53,22 +52,6 @@ public class Form {
guardAjax(cancel).click();
}
public static String getInputValue(WebElement input) {
return input.getAttribute(VALUE);
}
public static final String VALUE = "value";
public static void setInputValue(WebElement input, String value) {
if (input.isEnabled()) {
input.clear();
if (value != null) {
input.sendKeys(value);
}
} else {
// TODO log warning
}
}
public WebElement saveBtn() {
return save;
}

View file

@ -46,7 +46,7 @@ public abstract class AbstractPage {
protected URI getAuthServerRoot() {
try {
return KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getContextRoot().toURI()).path("/auth/").build();
return KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getBrowserContextRoot().toURI()).path("/auth/").build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.testsuite.pages;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
import org.jboss.arquillian.graphene.page.Page;
@ -51,12 +52,11 @@ public class AppServerWelcomePage extends AppServerContextRoot {
}
public void navigateToConsole() {
URLUtils.navigateToUri(getInjectedUrl().toString() + "/console", true);
loginPage.form().waitForLoginButtonPresent();
URLUtils.navigateToUri(getInjectedUrl().toString() + "/console");
}
public void login(String username, String password) {
loginPage.form().waitForLoginButtonPresent();
assertTrue(loginPage.form().isLoginButtonPresent());
loginPage.form().login(username, password);
waitForPageToLoad();
}

View file

@ -49,7 +49,7 @@ public class GitHubLoginPage extends AbstractSocialLoginPage {
@Override
public void logout() {
log.info("performing logout from GitHub");
URLUtils.navigateToUri("https://github.com/logout", true);
URLUtils.navigateToUri("https://github.com/logout");
UIUtils.clickLink(logoutButton);
}
}

View file

@ -1,8 +1,11 @@
package org.keycloak.testsuite.util;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
@ -15,6 +18,8 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
*/
public final class UIUtils {
public static final String VALUE_ATTR_NAME = "value";
public static boolean selectContainsOption(Select select, String optionText) {
for (WebElement option : select.getOptions()) {
if (option.getText().equals(optionText)) {
@ -58,7 +63,7 @@ public final class UIUtils {
* @param element
*/
public static void navigateToLink(WebElement element) {
URLUtils.navigateToUri(element.getAttribute("href"), true);
URLUtils.navigateToUri(element.getAttribute("href"));
}
/**
@ -80,4 +85,36 @@ public final class UIUtils {
element.sendKeys(keys);
jsExecutor.executeScript("arguments[0].setAttribute('style', '" + styleBckp + "');", element);
}
public static String getTextInputValue(WebElement input) {
return input.getAttribute(VALUE_ATTR_NAME);
}
public static void setTextInputValue(WebElement input, String value) {
input.click();
input.clear();
if (value != null) {
input.sendKeys(value);
}
WebDriver driver = getCurrentDriver();
if (driver instanceof AndroidDriver) {
AndroidDriver androidDriver = (AndroidDriver) driver;
androidDriver.hideKeyboard(); // stability improvement
}
}
/**
* Contains some browser-specific tweaks for getting an element text.
*
* @param element
* @return
*/
public static String getTextFromElement(WebElement element) {
String text = element.getText();
if (getCurrentDriver() instanceof SafariDriver) {
return text.trim(); // Safari on macOS sometimes for no obvious reason surrounds the text with spaces
}
return text;
}
}

View file

@ -8,6 +8,8 @@ import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
@ -23,12 +25,11 @@ public final class URLUtils {
private static Logger log = Logger.getLogger(URLUtils.class);
public static void navigateToUri(String uri, boolean waitForMatch) {
navigateToUri(uri, waitForMatch, true);
public static void navigateToUri(String uri) {
navigateToUri(uri, true);
}
// TODO: remove waitForMatch
private static void navigateToUri(String uri, boolean waitForMatch, boolean enableIEWorkaround) {
private static void navigateToUri(String uri, boolean enableIEWorkaround) {
WebDriver driver = getCurrentDriver();
log.info("starting navigation");
@ -62,14 +63,14 @@ public final class URLUtils {
&& (driver.getCurrentUrl().matches("^[^#]+/#state=[^#/&]+&code=[^#/&]+$")
|| driver.getCurrentUrl().matches("^.+/auth/admin/[^/]+/console/$"))) {
log.info("IE workaround: reloading the page after deleting the cookies...");
navigateToUri(uri, waitForMatch, false);
navigateToUri(uri, false);
}
else {
log.info("navigation complete");
}
}
public static boolean currentUrlEqual(String url) {
public static boolean currentUrlEquals(String url) {
return urlCheck(urlToBe(url));
}
@ -77,12 +78,32 @@ public final class URLUtils {
return urlCheck(not(urlToBe(url)));
}
public static boolean currentUrlStartWith(String url) {
return urlCheck(urlMatches("^" + Pattern.quote(url) + ".*$"));
public static boolean currentUrlWithQueryEquals(String expectedUrl, String... expectedQuery) {
List<String> expectedQueryList = Arrays.asList(expectedQuery);
ExpectedCondition<Boolean> condition = (WebDriver driver) -> {
String[] urlParts = driver.getCurrentUrl().split("\\?", 2);
if (urlParts.length != 2) {
throw new RuntimeException("Current URL doesn't contain query string");
}
List<String> queryParts = Arrays.asList(urlParts[1].split("&"));
return urlParts[0].equals(expectedUrl) && queryParts.containsAll(expectedQueryList);
};
return urlCheck(condition);
}
public static boolean currentUrlStartsWith(String url) {
return currentUrlMatches("^" + Pattern.quote(url) + ".*$");
}
public static boolean currentUrlDoesntStartWith(String url) {
return urlCheck(urlMatches("^(?!" + Pattern.quote(url) + ").+$"));
return currentUrlMatches("^(?!" + Pattern.quote(url) + ").+$");
}
public static boolean currentUrlMatches(String regex) {
return urlCheck(urlMatches(regex));
}
private static boolean urlCheck(ExpectedCondition condition) {

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
@ -87,18 +88,24 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
bburkeUser = createUserRepresentation("bburke", "bburke@redhat.com", "Bill", "Burke", true);
setPasswordFor(bburkeUser, PASSWORD);
deleteAllCookiesForTestRealm();
resetTestRealmSession();
}
public void createTestUserWithAdminClient() {
createTestUserWithAdminClient(true);
}
public void createTestUserWithAdminClient(boolean setRealmRoles) {
ApiUtil.removeUserByUsername(testRealmResource(), "test");
log.debug("creating test user");
String id = createUserAndResetPasswordWithAdminClient(testRealmResource(), testUser, PASSWORD);
testUser.setId(id);
assignClientRoles(testRealmResource(), id, "realm-management", "view-realm");
if (setRealmRoles) {
assignClientRoles(testRealmResource(), id, "realm-management", "view-realm");
}
}
public static UserRepresentation createUserRepresentation(String username, String email, String firstName, String lastName, boolean enabled) {
@ -111,10 +118,29 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
return user;
}
public void deleteAllCookiesForTestRealm() {
public static UserRepresentation createUserRepresentation(String username, String email, String firstName, String lastName, boolean enabled, String password) {
UserRepresentation user = createUserRepresentation(username, email, firstName, lastName, enabled);
setPasswordFor(user, password);
return user;
}
public static UserRepresentation createUserRepresentation(String username, String password) {
UserRepresentation user = createUserRepresentation(username, null, null, null, true, password);
return user;
}
protected void deleteAllCookiesForTestRealm() {
deleteAllCookiesForRealm(testRealmAccountPage);
}
protected void deleteAllSessionsInTestRealm() {
deleteAllSessionsInRealm(testRealmAccountPage.getAuthRealm());
}
private void resetTestRealmSession() {
resetRealmSession(testRealmAccountPage.getAuthRealm());
}
public void listCookies() {
log.info("LIST OF COOKIES: ");
for (Cookie c : driver.manage().getCookies()) {
@ -127,4 +153,8 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
return adminClient.realm(testRealmPage.getAuthRealm());
}
protected UserResource testUserResource() {
return testRealmResource().users().get(testUser.getId());
}
}

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.testsuite;
import io.appium.java_client.AppiumDriver;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.jboss.arquillian.container.test.api.RunAsClient;
@ -29,6 +30,7 @@ import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.AuthenticationManagementResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RealmsResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
@ -83,6 +85,7 @@ import java.util.concurrent.TimeoutException;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
import static org.keycloak.testsuite.util.URLUtils.navigateToUri;
/**
*
@ -94,6 +97,8 @@ public abstract class AbstractKeycloakTest {
protected static final boolean AUTH_SERVER_SSL_REQUIRED = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required", "false"));
protected static final String ENGLISH_LOCALE_NAME = "English";
protected Logger log = Logger.getLogger(this.getClass());
@ArquillianResource
@ -173,6 +178,8 @@ public abstract class AbstractKeycloakTest {
if (!isImportAfterEachMethod()) {
testContext.setTestRealmReps(testRealmReps);
}
afterAbstractKeycloakTestRealmImport();
}
oauth.init(adminClient, driver);
@ -194,6 +201,8 @@ public abstract class AbstractKeycloakTest {
protected void postAfterAbstractKeycloak() {
}
protected void afterAbstractKeycloakTestRealmImport() {}
@After
public void afterAbstractKeycloakTest() {
if (resetTimeOffset) {
@ -269,11 +278,49 @@ public abstract class AbstractKeycloakTest {
protected void deleteAllCookiesForRealm(String realmName) {
// masterRealmPage.navigateTo();
driver.navigate().to(OAuthClient.AUTH_SERVER_ROOT + "/realms/" + realmName + "/account"); // Because IE webdriver freezes when loading a JSON page (realm page), we need to use this alternative
navigateToUri(accountPage.getAuthRoot() + "/realms/" + realmName + "/account"); // Because IE webdriver freezes when loading a JSON page (realm page), we need to use this alternative
log.info("deleting cookies in '" + realmName + "' realm");
driver.manage().deleteAllCookies();
}
// this is useful mainly for smartphones as cookies deletion doesn't work there
protected void deleteAllSessionsInRealm(String realmName) {
log.info("removing all sessions from '" + realmName + "' realm...");
try {
adminClient.realm(realmName).logoutAll();
log.info("sessions successfully deleted");
}
catch (NotFoundException e) {
log.warn("realm not found");
}
}
protected void resetRealmSession(String realmName) {
deleteAllCookiesForRealm(realmName);
if (driver instanceof AppiumDriver) { // smartphone drivers don't support cookies deletion
try {
log.info("resetting realm session");
final RealmRepresentation realmRep = adminClient.realm(realmName).toRepresentation();
deleteAllSessionsInRealm(realmName); // logout users
if (realmRep.isInternationalizationEnabled()) { // reset the locale
String locale = getDefaultLocaleName(realmRep.getRealm());
loginPage.localeDropdown().selectByText(locale);
log.info("locale reset to " + locale);
}
} catch (NotFoundException e) {
log.warn("realm not found");
}
}
}
protected String getDefaultLocaleName(String realmName) {
return ENGLISH_LOCALE_NAME;
}
public void setDefaultPageUriParameters() {
masterRealmPage.setAuthRealm(MASTER);
loginPage.setAuthRealm(MASTER);

View file

@ -91,7 +91,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
//configure OTP for test user
testRealmAccountManagementPage.navigateTo();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
String totpSecret = testRealmLoginPage.form().totpForm().getTotpSecret();
testRealmLoginPage.form().totpForm().setTotp(totp.generateTOTP(totpSecret));
testRealmLoginPage.form().totpForm().submit();
@ -117,7 +116,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -134,7 +132,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
//test OTP is required
testRealmAccountManagementPage.navigateTo();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -170,7 +167,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -192,7 +188,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
//test OTP is required
testRealmAccountManagementPage.navigateTo();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -238,7 +233,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -285,7 +279,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -339,7 +332,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);
@ -389,7 +381,6 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
configureOTP();
testRealmLoginPage.form().login(testUser);
testRealmLoginPage.form().totpForm().waitForTotpInputFieldPresent();
//verify that the page is login page, not totp setup
assertCurrentUrlStartsWith(testLoginOneTimeCodePage);

View file

@ -83,6 +83,7 @@ import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
import static org.keycloak.testsuite.utils.io.IOUtil.loadJson;
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
@ -738,7 +739,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
clientPage.navigateTo();
// Check for correct logout
this.jsDriverTestRealmLoginPage.form().waitForLoginButtonPresent();
assertCurrentUrlStartsWithLoginUrlOf(jsDriverTestRealmLoginPage);
} else {
throw ex;
}

View file

@ -87,7 +87,6 @@ import org.keycloak.testsuite.adapter.page.SecurePortal;
import org.keycloak.testsuite.adapter.page.SecurePortalWithCustomSessionConfig;
import org.keycloak.testsuite.adapter.page.TokenMinTTLPage;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.AppServerTestEnricher;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.keycloak.testsuite.arquillian.containers.ContainerConstants;
import org.keycloak.testsuite.auth.page.account.Applications;
@ -261,6 +260,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
loginEventsPage.setConsoleRealm(DEMO);
applicationsPage.setAuthRealm(DEMO);
loginEventsPage.setConsoleRealm(DEMO);
oAuthGrantPage.setAuthRealm(DEMO);
}
@Before
@ -433,7 +433,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
public void testLoginSSOAndLogout() {
// test login to customer-portal which does a bearer request to customer-db
customerPortal.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertCurrentUrlEquals(customerPortal);
@ -508,7 +508,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
public void testLoginSSOIdle() {
// test login to customer-portal which does a bearer request to customer-db
customerPortal.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertCurrentUrlEquals(customerPortal);
@ -534,7 +534,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
// test login to customer-portal which does a bearer request to customer-db
customerPortal.navigateTo();
log.info("Current url: " + driver.getCurrentUrl());
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
log.info("Current url: " + driver.getCurrentUrl());
@ -566,7 +566,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
// test login to customer-portal which does a bearer request to customer-db
customerPortal.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertCurrentUrlEquals(customerPortal);
@ -723,7 +723,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
public void testTokenMinTTL() {
// Login
tokenMinTTLPage.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertCurrentUrlEquals(tokenMinTTLPage);
@ -767,7 +767,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
// Test I need to reauthenticate with prompt=login
String appUri = tokenMinTTLPage.getUriBuilder().queryParam(OIDCLoginProtocol.PROMPT_PARAM, OIDCLoginProtocol.PROMPT_VALUE_LOGIN).build().toString();
URLUtils.navigateToUri(appUri, true);
URLUtils.navigateToUri(appUri);
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
AccessToken token = tokenMinTTLPage.getAccessToken();
@ -805,14 +805,14 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
String portalUri = securePortal.getUriBuilder().build().toString();
UriBuilder uriBuilder = securePortal.getUriBuilder();
String appUri = uriBuilder.clone().queryParam(OAuth2Constants.UI_LOCALES_PARAM, "de en").build().toString();
URLUtils.navigateToUri(appUri, true);
URLUtils.navigateToUri(appUri);
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
// check the ui_locales param is there
Map<String, String> parameters = getQueryFromUrl(driver.getCurrentUrl());
assertThat(parameters.get(OAuth2Constants.UI_LOCALES_PARAM), allOf(containsString("de"), containsString("en")));
String appUriDe = uriBuilder.clone().queryParam(OAuth2Constants.UI_LOCALES_PARAM, "de").build().toString();
URLUtils.navigateToUri(appUriDe, true);
URLUtils.navigateToUri(appUriDe);
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
// check that the page is in german

View file

@ -136,7 +136,7 @@ public class OIDCPublicKeyRotationAdapterTest extends AbstractServletsAdapterTes
// Try to login again. It should fail now because not yet allowed to download new keys
tokenMinTTLPage.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
URLAssert.assertCurrentUrlStartsWith(tokenMinTTLPage.getInjectedUrl().toString());
@ -281,7 +281,7 @@ public class OIDCPublicKeyRotationAdapterTest extends AbstractServletsAdapterTes
private void loginToTokenMinTtlApp() {
tokenMinTTLPage.navigateTo();
testRealmLoginPage.form().waitForUsernameInputPresent();
assertTrue(testRealmLoginPage.form().isUsernamePresent());
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertCurrentUrlEquals(tokenMinTTLPage);

View file

@ -369,7 +369,7 @@ public class SocialLoginTest extends AbstractKeycloakTest {
}
private void assertAccount() {
assertTrue(URLUtils.currentUrlStartWith(accountPage.toString())); // Sometimes after login the URL ends with /# or similar
assertTrue(URLUtils.currentUrlStartsWith(accountPage.toString())); // Sometimes after login the URL ends with /# or similar
assertEquals(getConfig("profile.firstName"), accountPage.getFirstName());
assertEquals(getConfig("profile.lastName"), accountPage.getLastName());

View file

@ -22,7 +22,6 @@ import org.junit.Test;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.AdminRoles;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.ActionURIUtils;
@ -89,7 +88,7 @@ public class CookiesPathTest extends AbstractKeycloakTest {
cookies.stream().forEach(cookie -> Assert.assertThat(cookie.getPath(), Matchers.endsWith("/auth/realms/foobar/")));
// lets back to "/realms/foo/account" to test the cookies for "foo" realm are still there and haven't been (correctly) sent to "foobar"
URLUtils.navigateToUri( oauth.AUTH_SERVER_ROOT + "/realms/foo/account", true);
URLUtils.navigateToUri( oauth.AUTH_SERVER_ROOT + "/realms/foo/account");
cookies = driver.manage().getCookies();
Assert.assertTrue("There should be cookies sent!", cookies.size() > 0);

View file

@ -105,7 +105,7 @@ public class TrustStoreEmailTest extends AbstractTestRealmKeycloakTest {
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
assertEquals("You need to verify your email address to activate your account.",
testRealmVerifyEmailPage.getFeedbackText());
testRealmVerifyEmailPage.feedbackMessage().getText());
String verifyEmailUrl = assertEmailAndGetUrl(MailServerConfiguration.FROM, user.getEmail(),
"Someone has created a Test account with this email address.", true);
@ -159,6 +159,6 @@ public class TrustStoreEmailTest extends AbstractTestRealmKeycloakTest {
// Email wasn't send, but we won't notify end user about that. Admin is aware due to the error in the logs and the SEND_VERIFY_EMAIL_ERROR event.
assertEquals("You need to verify your email address to activate your account.",
testRealmVerifyEmailPage.getFeedbackText());
testRealmVerifyEmailPage.feedbackMessage().getText());
}
}

View file

@ -37,8 +37,8 @@ import java.nio.charset.Charset;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.URLUtils.currentUrlDoesntStartWith;
import static org.keycloak.testsuite.util.URLUtils.currentUrlEqual;
import static org.keycloak.testsuite.util.URLUtils.currentUrlStartWith;
import static org.keycloak.testsuite.util.URLUtils.currentUrlEquals;
import static org.keycloak.testsuite.util.URLUtils.currentUrlStartsWith;
/**
*
@ -62,7 +62,7 @@ public class URLAssert {
public static void assertCurrentUrlEquals(final String url) {
assertTrue("Expected URL: " + url + "; actual: " + DroneUtils.getCurrentDriver().getCurrentUrl(),
currentUrlEqual(url));
currentUrlEquals(url));
}
@ -82,7 +82,7 @@ public class URLAssert {
public static void assertCurrentUrlStartsWith(final String url){
assertTrue("URL expected to begin with:" + url + "; actual URL: " + DroneUtils.getCurrentDriver().getCurrentUrl(),
currentUrlStartWith(url));
currentUrlStartsWith(url));
}

View file

@ -27,20 +27,32 @@
<property name="downloadBinaries">${webdriverDownloadBinaries}</property>
<property name="githubUsername">${github.username}</property>
<property name="githubToken">${github.secretToken}</property>
<property name="ieDriverArch">${ieDriverArch}</property>
<!-- htmlunit -->
<property name="htmlUnit.version">${htmlUnitBrowserVersion}</property>
<property name="htmlUnitWebClientOptions">cssEnabled=false;historyPageCacheLimit=1</property>
<!-- firefox -->
<property name="firefox_binary">${firefox_binary}</property>
<property name="firefoxBinary">${firefox_binary}</property> <!-- we need to use 'firefoxBinary' instead of 'firefox_binary' due to some weird conflict with Appium -->
<property name="firefoxLogLevel">OFF</property>
<property name="firefoxLegacy">${firefoxLegacyDriver}</property>
<!-- chrome -->
<property name="chromeBinary">${chromeBinary}</property>
<property name="chromeArguments">${chromeArguments}</property>
<!-- internet explorer -->
<property name="ieDriverArch">${ieDriverArch}</property>
<!-- appium -->
<property name="appium.platformName">${appium.platformName}</property>
<property name="appium.deviceName">${appium.deviceName}</property>
<property name="appium.browserName">${appium.browserName}</property>
<property name="appium.avd">${appium.avd}</property>
<property name="appium.automationName">${appium.automationName}</property>
<property name="appium.noReset">${appium.noReset}</property>
<property name="appium.fullReset">${appium.fullReset}</property>
<property name="appium.nativeWebScreenshot">true</property> <!-- there's some issue when taking screenshot using the chromedriver therefore we need to take screenshots of the whole screen (using adb) instead -->
</extension>
<extension qualifier="drone">
@ -66,7 +78,7 @@
<property name="htmlUnitWebClientOptions">cssEnabled=false;historyPageCacheLimit=1</property>
<!-- firefox -->
<property name="firefox_binary">${firefox_binary}</property>
<property name="firefoxBinary">${firefox_binary}</property>
<property name="firefoxLogLevel">OFF</property>
<property name="firefoxLegacy">${firefoxLegacyDriver}</property>
@ -77,7 +89,7 @@
<extension qualifier="graphene-secondbrowser">
<property name="browser">${browser}</property>
<property name="firefox_binary">${firefox_binary}</property>
<property name="firefoxBinary">${firefox_binary}</property>
</extension>
<engine>

View file

@ -43,6 +43,7 @@
<app.server.mode>manual</app.server.mode>
<app.server.host>localhost</app.server.host>
<app.server.browserHost/> <!-- if set, this host will be used by the browser instead of app.server.host -->
<app.server.port.offset>200</app.server.port.offset>
<app.server.http.port>8280</app.server.http.port>
<app.server.https.port>8643</app.server.https.port>
@ -202,6 +203,7 @@
<app.server.mode>${app.server.mode}</app.server.mode>
<app.server.host>${app.server.host}</app.server.host>
<app.server.browserHost>${app.server.browserHost}</app.server.browserHost> <!-- if set, this host will be used by the browser -->
<app.server.port.offset>${app.server.port.offset}</app.server.port.offset>
<app.server.http.port>${app.server.http.port}</app.server.http.port>
<app.server.https.port>${app.server.https.port}</app.server.https.port>

View file

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2018 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>integration-arquillian-tests-other</artifactId>
<groupId>org.keycloak.testsuite</groupId>
<version>4.3.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-arquillian-tests-base-ui</artifactId>
<name>Base UI TestSuite</name>
<properties>
<droneInstantiationTimeoutInSeconds>600</droneInstantiationTimeoutInSeconds>
<firefoxLegacyDriver>false</firefoxLegacyDriver> <!-- using the new GeckoDriver -->
<keycloak.theme.dir>${auth.server.home}/themes</keycloak.theme.dir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${testsuite.constants}</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-theme-files</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${keycloak.theme.dir}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/themes</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<keycloak.theme.dir>${keycloak.theme.dir}</keycloak.theme.dir>
</systemProperties>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-appium-extension</artifactId>
<version>${arquillian-drone.version}</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>android</id>
<properties>
<browser>appium</browser>
<appium.platformName>android</appium.platformName>
<appium.browserName>chrome</appium.browserName>
<appium.deviceName>doesn't matter</appium.deviceName> <!-- ignored but required by AndroidDriver -->
<appium.automationName>Appium</appium.automationName>
<auth.server.browserHost>10.0.2.2</auth.server.browserHost>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireProperty>
<property>appium.avd</property>
<regex>\S+.*</regex>
</requireProperty>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>ios</id>
<properties>
<browser>appium</browser>
<appium.platformName>ios</appium.platformName>
<appium.browserName>safari</appium.browserName>
<appium.automationName>XCUITest</appium.automationName>
<appium.noReset>true</appium.noReset>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireProperty>
<property>appium.deviceName</property>
<regex>\S+.*</regex>
</requireProperty>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View file

@ -0,0 +1,2 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -0,0 +1,2 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -0,0 +1,2 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -0,0 +1,12 @@
#encoding: utf-8
locale_test=Přísný jazyk
termsText=[TEST LOCALE] souhlas s podmínkami
notMatchPasswordMessage=[TEST LOCALE] hesla se neshodují
firstName=[TEST LOCALE] křestní jméno
updateProfileMessage=[TEST LOCALE] aktualizovat profil
verifyEmailMessage=[TEST LOCALE] je třeba ověřit emailovou adresu
invalidTotpMessage=[TEST LOCALE] vložen chybný kód
oauthGrantTitle=[TEST LOCALE] Udělit přístup {0}
rememberMe=[TEST LOCALE] Zapamatuj si mě
invalidUserMessage=[TEST LOCALE] Chybné jméno nebo heslo
emailForgotTitle=[TEST LOCALE] Zapomenuté heslo

View file

@ -0,0 +1,2 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -0,0 +1,61 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.ui;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractAuthTest;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractUiTest extends AbstractAuthTest {
public static final String LOCALIZED_THEME = "localized-theme";
public static final String CUSTOM_LOCALE_NAME = "Přísný jazyk";
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation testRealmRep = new RealmRepresentation();
testRealmRep.setId(TEST);
testRealmRep.setRealm(TEST);
testRealmRep.setEnabled(true);
configureInternationalizationForRealm(testRealmRep);
testRealms.add(testRealmRep);
}
protected void configureInternationalizationForRealm(RealmRepresentation realm) {
// fetch the supported locales for the special test theme that includes some fake test locales
Set<String> supportedLocales = adminClient.serverInfo().getInfo().getThemes().get("login").stream()
.filter(x -> x.getName().equals(LOCALIZED_THEME))
.flatMap(x -> Arrays.stream(x.getLocales()))
.collect(Collectors.toSet());
realm.setInternationalizationEnabled(true);
realm.setSupportedLocales(supportedLocales);
realm.setLoginTheme(LOCALIZED_THEME);
realm.setAdminTheme(LOCALIZED_THEME);
realm.setAccountTheme(LOCALIZED_THEME);
realm.setEmailTheme(LOCALIZED_THEME);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.ui.login;
import org.junit.Before;
import org.keycloak.testsuite.ui.AbstractUiTest;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractLoginTest extends AbstractUiTest {
@Before
public void addTestUser() {
createTestUserWithAdminClient(false);
}
protected void assertLoginFailed(String message) {
assertCurrentUrlDoesntStartWith(testRealmAccountPage);
assertTrue("Feedback message should be an error", loginPage.feedbackMessage().isError());
assertEquals(message, loginPage.feedbackMessage().getText());
}
protected void assertLoginSuccessful() {
assertCurrentUrlStartsWith(testRealmAccountPage);
}
}

View file

@ -0,0 +1,302 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.ui.login;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.login.Registration;
import org.keycloak.testsuite.auth.page.login.ResetCredentials;
import org.keycloak.testsuite.auth.page.login.UpdateAccount;
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LoginPageTest extends AbstractLoginTest {
@Page
private UpdateAccount updateAccountPage;
@Page
private UpdatePassword updatePasswordPage;
@Page
private Registration registrationPage;
@Page
private ResetCredentials resetCredentialsPage;
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
updateAccountPage.setAuthRealm(TEST);
updatePasswordPage.setAuthRealm(TEST);
registrationPage.setAuthRealm(TEST);
resetCredentialsPage.setAuthRealm(TEST);
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealmRep = testRealms.get(0);
testRealmRep.setDisplayNameHtml("Test realm <b>HTML</b>");
testRealmRep.setRememberMe(true);
testRealmRep.setResetPasswordAllowed(true);
testRealmRep.setRegistrationAllowed(true);
}
@Before
public void beforeLoginTest() {
deleteAllCookiesForTestRealm();
testRealmAccountPage.navigateTo();
assertCurrentUrlStartsWithLoginUrlOf(testRealmAccountPage);
assertFalse(testRealmLoginPage.feedbackMessage().isPresent());
}
@Test
public void wrongCredentials() {
assertFalse(testRealmLoginPage.form().isRememberMe());
testRealmLoginPage.form().rememberMe(true);
assertTrue(testRealmLoginPage.form().isRememberMe());
testRealmLoginPage.form().login("some-user", "badPwd");
assertTrue(testRealmLoginPage.form().isRememberMe());
assertLoginFailed("Invalid username or password.");
}
@Test
public void disabledUser() {
testUser.setEnabled(false);
testUserResource().update(testUser);
testRealmLoginPage.form().login(testUser);
assertLoginFailed("Account is disabled, contact admin.");
}
@Test
public void labelsTest() {
assertEquals("test realm html", testRealmLoginPage.getHeaderText().toLowerCase()); // we need to convert to lower case as Safari handles getText() differently
assertEquals("Username or email", testRealmLoginPage.form().getUsernameLabel());
assertEquals("Password", testRealmLoginPage.form().getPasswordLabel());
}
@Test
public void loginSuccessful() {
testRealmLoginPage.form().login(testUser);
assertLoginSuccessful();
}
@Test
public void internationalizationTest() {
final String rememberMeLabel = "[TEST LOCALE] Zapamatuj si mě";
// required action set up
testUser.setRequiredActions(Arrays.asList(updatePasswordPage.getActionId(), updateAccountPage.getActionId()));
testUserResource().update(testUser);
assertEquals("Remember me", testRealmLoginPage.form().getRememberMeLabel());
testRealmLoginPage.localeDropdown().selectByText(CUSTOM_LOCALE_NAME);
assertEquals(rememberMeLabel, testRealmLoginPage.form().getRememberMeLabel());
testRealmLoginPage.form().login();
assertLoginFailed("[TEST LOCALE] Chybné jméno nebo heslo");
assertEquals(rememberMeLabel, testRealmLoginPage.form().getRememberMeLabel());
testRealmLoginPage.form().login(testUser);
if (updatePasswordPage.isCurrent()) {
updatePassword();
updateProfile();
}
else {
updateProfile();
updatePassword();
}
assertLoginSuccessful();
}
private void updateProfile() {
assertEquals("[TEST LOCALE] aktualizovat profil", updateAccountPage.feedbackMessage().getText());
updateAccountPage.submit(); // should be pre-filled
}
private void updatePassword() {
updatePasswordPage.updatePasswords("some wrong", "password");
assertEquals("[TEST LOCALE] hesla se neshodují", updatePasswordPage.feedbackMessage().getText());
updatePasswordPage.updatePasswords("matchingPassword", "matchingPassword");
}
@Test
public void registerTest() {
testRealmLoginPage.form().register();
registrationPage.assertCurrent();
registrationPage.localeDropdown().selectByText(CUSTOM_LOCALE_NAME);
registrationPage.submit();
assertTrue(registrationPage.feedbackMessage().isError());
assertEquals("[TEST LOCALE] křestní jméno", registrationPage.accountFields().getFirstNameLabel());
registrationPage.backToLogin();
testRealmLoginPage.form().register();
registrationPage.localeDropdown().selectByText(ENGLISH_LOCALE_NAME);
final String username = "vmuzikar";
final String email = "vmuzikar@redhat.com";
final String firstName = "Vaclav";
final String lastName = "Muzikar";
final UserRepresentation newUser = createUserRepresentation(username, email, firstName, lastName, true, "password");
// empty form
registrationPage.submit();
assertRegistrationFields(null, null, null, null, false, true);
// email filled in
registrationPage.accountFields().setEmail(email);
registrationPage.submit();
assertRegistrationFields(null, null, email, null, false, true);
// first name filled in
registrationPage.accountFields().setEmail(null);
registrationPage.accountFields().setFirstName(firstName);
registrationPage.submit();
assertRegistrationFields(firstName, null, null, null, false, true);
// last name filled in
registrationPage.accountFields().setFirstName(null);
registrationPage.accountFields().setLastName(lastName);
registrationPage.submit();
assertRegistrationFields(null, lastName, null, null, false, true);
// username filled in
registrationPage.accountFields().setLastName(null);
registrationPage.accountFields().setUsername(username);
registrationPage.submit();
assertRegistrationFields(null, null, null, username, false, true);
// password mismatch
registrationPage.accountFields().setValues(newUser);
registrationPage.passwordFields().setPassword("wrong");
registrationPage.passwordFields().setConfirmPassword("password");
registrationPage.submit();
assertRegistrationFields(firstName, lastName, email, username, true, false);
// success
registrationPage.register(newUser);
assertLoginSuccessful();
}
private void assertRegistrationFields(String firstName, String lastName, String email, String username, boolean password, boolean passwordConfirm) {
assertTrue(registrationPage.feedbackMessage().isError());
final String errorMsg = registrationPage.feedbackMessage().getText();
if (firstName != null) {
assertEquals(firstName, registrationPage.accountFields().getFirstName());
assertFalse(registrationPage.accountFields().hasFirstNameError());
assertFalse(errorMsg.contains("first name"));
}
else {
assertTrue(registrationPage.accountFields().hasFirstNameError());
assertTrue(errorMsg.contains("first name"));
}
if (lastName != null) {
assertEquals(lastName, registrationPage.accountFields().getLastName());
assertFalse(registrationPage.accountFields().hasLastNameError());
assertFalse(errorMsg.contains("last name"));
}
else {
assertTrue(registrationPage.accountFields().hasLastNameError());
assertTrue(errorMsg.contains("last name"));
}
if (email != null) {
assertEquals(email, registrationPage.accountFields().getEmail());
assertFalse(registrationPage.accountFields().hasEmailError());
assertFalse(errorMsg.contains("email"));
}
else {
assertTrue(registrationPage.accountFields().hasEmailError());
assertTrue(errorMsg.contains("email"));
}
if (username != null) {
assertEquals(username, registrationPage.accountFields().getUsername());
assertFalse(registrationPage.accountFields().hasUsernameError());
assertFalse(errorMsg.contains("username"));
}
else {
assertTrue(registrationPage.accountFields().hasUsernameError());
assertTrue(errorMsg.contains("username"));
}
if (password) {
assertFalse(registrationPage.passwordFields().hasPasswordError());
assertFalse(errorMsg.contains("Please specify password."));
}
else {
assertTrue(registrationPage.passwordFields().hasPasswordError());
assertTrue(errorMsg.contains("Please specify password."));
}
if (passwordConfirm) {
assertFalse(registrationPage.passwordFields().hasConfirmPasswordError());
assertFalse(registrationPage.feedbackMessage().getText().contains("Password confirmation doesn't match."));
}
else {
assertTrue(registrationPage.passwordFields().hasConfirmPasswordError());
assertTrue(registrationPage.feedbackMessage().getText().contains("Password confirmation doesn't match."));
}
}
@Test
public void resetCredentialsTest() {
testRealmLoginPage.form().forgotPassword();
resetCredentialsPage.localeDropdown().selectByText(CUSTOM_LOCALE_NAME);
resetCredentialsPage.assertCurrent();
resetCredentialsPage.backToLogin();
testRealmLoginPage.form().forgotPassword();
assertEquals("[TEST LOCALE] Zapomenuté heslo", resetCredentialsPage.getTitleText());
// empty form
assertFalse(resetCredentialsPage.feedbackMessage().isPresent());
resetCredentialsPage.submit();
resetCredentialsPage.assertCurrent();
assertTrue(resetCredentialsPage.feedbackMessage().isPresent());
assertTrue(resetCredentialsPage.feedbackMessage().isError());
// non-empty form
resetCredentialsPage.resetCredentials(testUser.getUsername());
// there will be probably an error sending email, so no further action here
}
}

View file

@ -0,0 +1,524 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.testsuite.ui.login;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.models.utils.Base32;
import org.keycloak.models.utils.HmacOTP;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.login.LoginError;
import org.keycloak.testsuite.auth.page.login.OAuthGrant;
import org.keycloak.testsuite.auth.page.login.OTPSetup;
import org.keycloak.testsuite.auth.page.login.OneTimeCode;
import org.keycloak.testsuite.auth.page.login.RequiredActions;
import org.keycloak.testsuite.auth.page.login.TermsAndConditions;
import org.keycloak.testsuite.auth.page.login.UpdateAccount;
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import org.keycloak.testsuite.auth.page.login.VerifyEmail;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.zxing.BarcodeFormat.QR_CODE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.keycloak.models.ClientScopeModel.CONSENT_SCREEN_TEXT;
import static org.keycloak.models.ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class RequiredActionsTest extends AbstractLoginTest {
public static final String GRANT_REALM = "grant-realm";
public static final String CONSENT_TEXT = "Příliš žluťoučký kůň úpěl ďábelské ódy";
private UserRepresentation grantRealmUser = createUserRepresentation("test", PASSWORD);
public static final String TOTP = "totp";
public static final String HOTP = "hotp";
@Page
private TermsAndConditions termsAndConditionsPage;
@Page
private UpdatePassword updatePasswordPage;
@Page
private UpdateAccount updateAccountPage;
@Page
private VerifyEmail verifyEmailPage;
@Page
private OTPSetup otpSetupPage;
@Page
private OneTimeCode oneTimeCodePage;
@Page
private OAuthGrant oAuthGrantPage;
@Page
private LoginError loginErrorPage;
private TimeBasedOTP otpGenerator = new TimeBasedOTP();
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
termsAndConditionsPage.setAuthRealm(TEST);
updatePasswordPage.setAuthRealm(TEST);
updateAccountPage.setAuthRealm(TEST);
verifyEmailPage.setAuthRealm(TEST);
otpSetupPage.setAuthRealm(TEST);
oneTimeCodePage.setAuthRealm(TEST);
oAuthGrantPage.setAuthRealm(GRANT_REALM);
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealmRep = new RealmRepresentation();
testRealmRep.setId(GRANT_REALM);
testRealmRep.setRealm(GRANT_REALM);
configureInternationalizationForRealm(testRealmRep);
testRealmRep.setEnabled(true);
testRealms.add(testRealmRep);
}
// Some actions we need to do after the realm is created and configured
@Override
protected void afterAbstractKeycloakTestRealmImport() {
super.afterAbstractKeycloakTestRealmImport();
// create test user
createUserAndResetPasswordWithAdminClient(adminClient.realm(GRANT_REALM), grantRealmUser, PASSWORD);
}
@Test
public void termsAndConditions() {
RequiredActionProviderRepresentation termsAndCondRep = testRealmResource().flows().getRequiredAction(termsAndConditionsPage.getActionId());
termsAndCondRep.setEnabled(true);
testRealmResource().flows().updateRequiredAction(termsAndConditionsPage.getActionId(), termsAndCondRep);
initiateRequiredAction(termsAndConditionsPage);
termsAndConditionsPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
termsAndConditionsPage.acceptTerms();
assertLoginSuccessful();
deleteAllSessionsInTestRealm();
initiateRequiredAction(termsAndConditionsPage);
assertEquals("[TEST LOCALE] souhlas s podmínkami", termsAndConditionsPage.getText());
termsAndConditionsPage.declineTerms();
loginErrorPage.assertCurrent();
assertNoAccess();
}
@Test
public void updatePassword() {
initiateRequiredAction(updatePasswordPage);
updatePasswordPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
assertTrue(updatePasswordPage.feedbackMessage().isWarning());
assertEquals("You need to change your password to activate your account.", updatePasswordPage.feedbackMessage().getText());
assertEquals("New Password", updatePasswordPage.fields().getNewPasswordLabel());
assertEquals("Confirm password", updatePasswordPage.fields().getConfirmPasswordLabel());
updatePasswordPage.updatePasswords("some wrong", "password");
assertTrue(updatePasswordPage.feedbackMessage().isError());
assertEquals("[TEST LOCALE] hesla se neshodují", updatePasswordPage.feedbackMessage().getText());
updatePasswordPage.localeDropdown().selectAndAssert(ENGLISH_LOCALE_NAME);
updatePasswordPage.updatePasswords("matchingPassword", "matchingPassword");
assertLoginSuccessful();
}
@Test
public void updateProfile() {
initiateRequiredAction(updateAccountPage);
// prefilled profile
assertTrue(updateAccountPage.feedbackMessage().isWarning());
updateAccountPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
assertEquals("[TEST LOCALE] aktualizovat profil", updateAccountPage.feedbackMessage().getText());
updateAccountPage.localeDropdown().selectAndAssert(ENGLISH_LOCALE_NAME);
assertFalse(updateAccountPage.fields().isUsernamePresent());
assertEquals("Email", updateAccountPage.fields().getEmailLabel());
assertEquals("First name", updateAccountPage.fields().getFirstNameLabel());
assertEquals("Last name", updateAccountPage.fields().getLastNameLabel());
assertFalse(updateAccountPage.fields().hasEmailError());
assertFalse(updateAccountPage.fields().hasFirstNameError());
assertFalse(updateAccountPage.fields().hasLastNameError());
assertEquals(testUser.getEmail(), updateAccountPage.fields().getEmail());
assertEquals(testUser.getFirstName(), updateAccountPage.fields().getFirstName());
assertEquals(testUser.getLastName(), updateAccountPage.fields().getLastName());
updateAccountPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
// empty form
updateAccountPage.updateAccount(null, null, null);
assertTrue(updateAccountPage.feedbackMessage().isError());
String errorMsg = updateAccountPage.feedbackMessage().getText();
assertTrue(errorMsg.contains("first name") && errorMsg.contains("last name") && errorMsg.contains("email"));
assertTrue(updateAccountPage.fields().hasEmailError());
assertTrue(updateAccountPage.fields().hasFirstNameError());
assertTrue(updateAccountPage.fields().hasLastNameError());
final String email = "vmuzikar@redhat.com";
final String firstName = "Vaclav";
final String lastName = "Muzikar";
// email filled in
updateAccountPage.fields().setEmail(email);
updateAccountPage.submit();
assertTrue(updateAccountPage.feedbackMessage().isError());
errorMsg = updateAccountPage.feedbackMessage().getText();
assertTrue(errorMsg.contains("first name") && errorMsg.contains("last name") && !errorMsg.contains("email"));
assertFalse(updateAccountPage.fields().hasEmailError());
assertTrue(updateAccountPage.fields().hasFirstNameError());
assertTrue(updateAccountPage.fields().hasLastNameError());
assertEquals(email, updateAccountPage.fields().getEmail());
// first name filled in
updateAccountPage.fields().setFirstName(firstName);
updateAccountPage.submit();
assertTrue(updateAccountPage.feedbackMessage().isError());
errorMsg = updateAccountPage.feedbackMessage().getText();
assertTrue(!errorMsg.contains("first name") && errorMsg.contains("last name") && !errorMsg.contains("email"));
assertFalse(updateAccountPage.fields().hasEmailError());
assertFalse(updateAccountPage.fields().hasFirstNameError());
assertTrue(updateAccountPage.fields().hasLastNameError());
assertEquals(email, updateAccountPage.fields().getEmail());
assertEquals(firstName, updateAccountPage.fields().getFirstName());
// last name filled in
updateAccountPage.fields().setFirstName(null);
updateAccountPage.fields().setLastName(lastName);
updateAccountPage.submit();
assertTrue(updateAccountPage.feedbackMessage().isError());
errorMsg = updateAccountPage.feedbackMessage().getText();
assertTrue(errorMsg.contains("first name") && !errorMsg.contains("last name") && !errorMsg.contains("email"));
assertFalse(updateAccountPage.fields().hasEmailError());
assertTrue(updateAccountPage.fields().hasFirstNameError());
assertFalse(updateAccountPage.fields().hasLastNameError());
assertEquals(email, updateAccountPage.fields().getEmail());
assertEquals(lastName, updateAccountPage.fields().getLastName());
// success
assertEquals("[TEST LOCALE] křestní jméno", updateAccountPage.fields().getFirstNameLabel());
updateAccountPage.updateAccount(email, firstName, lastName);
assertLoginSuccessful();
}
@Test
public void verifyEmail() {
initiateRequiredAction(verifyEmailPage);
verifyEmailPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
boolean firstAttempt = true;
while (true) {
assertTrue(verifyEmailPage.feedbackMessage().isWarning());
assertEquals("[TEST LOCALE] je třeba ověřit emailovou adresu", verifyEmailPage.feedbackMessage().getText());
assertEquals("An email with instructions to verify your email address has been sent to you.", verifyEmailPage.getInstructionMessage());
if (firstAttempt) {
verifyEmailPage.clickResend();
firstAttempt = false;
}
else {
break;
}
}
}
@Test
public void configureManualTotp() {
setRealmOtpType(TOTP);
testManualOtp();
}
@Test
public void configureManualHotp() {
setRealmOtpType(HOTP);
testManualOtp();
}
@Test
public void configureBarcodeTotp() throws Exception {
setRealmOtpType(TOTP);
testBarcodeOtp();
}
@Test
public void configureBarcodeHotp() throws Exception {
setRealmOtpType(HOTP);
testBarcodeOtp();
}
@Test
public void clientConsent() {
testRealmPage.setAuthRealm(GRANT_REALM);
testRealmAccountPage.setAuthRealm(GRANT_REALM);
testRealmLoginPage.setAuthRealm(GRANT_REALM);
final List<String> defaultClientScopesToApprove = Arrays.asList("Email address", "User profile");
// custom consent text
initiateClientScopesConsent(true, CONSENT_TEXT);
oAuthGrantPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
List<String> clientScopesToApprove = new LinkedList<>(defaultClientScopesToApprove);
clientScopesToApprove.add(CONSENT_TEXT);
oAuthGrantPage.assertClientScopes(clientScopesToApprove);
// default consent text
initiateClientScopesConsent(true, null);
clientScopesToApprove = new LinkedList<>(defaultClientScopesToApprove);
clientScopesToApprove.add("Account");
oAuthGrantPage.assertClientScopes(clientScopesToApprove);
// consent with missing client
initiateClientScopesConsent(false, CONSENT_TEXT);
oAuthGrantPage.assertClientScopes(defaultClientScopesToApprove);
// test buttons
oAuthGrantPage.cancel();
assertNoAccess();
testRealmLoginPage.form().login(grantRealmUser);
assertEquals("[TEST LOCALE] Udělit přístup Account", oAuthGrantPage.getTitleText());
oAuthGrantPage.accept();
assertLoginSuccessful();
}
private void testManualOtp() {
initiateRequiredAction(otpSetupPage);
otpSetupPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
otpSetupPage.clickManualMode();
assertFalse(otpSetupPage.isBarcodePresent());
assertTrue(otpSetupPage.feedbackMessage().isWarning());
assertEquals("You need to set up Mobile Authenticator to activate your account.", otpSetupPage.feedbackMessage().getText());
// empty input
otpSetupPage.submit();
assertTrue(otpSetupPage.feedbackMessage().isError());
assertEquals("Please specify authenticator code.", otpSetupPage.feedbackMessage().getText());
// TODO: remove this once is KEYCLOAK-7081 fixed
otpSetupPage.clickManualMode();
otpSetupPage.clickManualMode();
final String replacePattern = "^.+: ";
// extract data
String type = otpSetupPage.getOtpType().replaceAll(replacePattern, "");
if (type.equals("Time-based")) type = TOTP;
else if (type.equals("Counter-based")) type = HOTP;
String secret = otpSetupPage.getSecretKey();
int digits = Integer.parseInt(otpSetupPage.getOtpDigits().replaceAll(replacePattern, ""));
String algorithm = otpSetupPage.getOtpAlgorithm().replaceAll(replacePattern, "");
Integer period = type.equals(TOTP) ? Integer.parseInt(otpSetupPage.getOtpPeriod().replaceAll(replacePattern, "")) : null;
Integer counter = type.equals(HOTP) ? Integer.parseInt(otpSetupPage.getOtpCounter().replaceAll(replacePattern, "")) : null;
// the actual test
testOtp(type, algorithm, digits, period, counter, secret);
}
private void testBarcodeOtp() throws Exception {
assumeFalse(driver instanceof HtmlUnitDriver); // HtmlUnit browser cannot take screenshots
TakesScreenshot screenshotDriver = (TakesScreenshot) driver;
QRCodeReader qrCodeReader = new QRCodeReader();
initiateRequiredAction(otpSetupPage);
otpSetupPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
otpSetupPage.clickManualMode();
otpSetupPage.clickBarcodeMode();
assertTrue(otpSetupPage.isBarcodePresent());
assertFalse(otpSetupPage.isSecretKeyPresent());
assertTrue(otpSetupPage.feedbackMessage().isWarning());
assertEquals("You need to set up Mobile Authenticator to activate your account.", otpSetupPage.feedbackMessage().getText());
// empty input
otpSetupPage.submit();
assertTrue(otpSetupPage.feedbackMessage().isError());
assertEquals("Please specify authenticator code.", otpSetupPage.feedbackMessage().getText());
// take a screenshot of the QR code
byte[] screenshot = screenshotDriver.getScreenshotAs(OutputType.BYTES);
BufferedImage screenshotImg = ImageIO.read(new ByteArrayInputStream(screenshot));
BinaryBitmap screenshotBinaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(screenshotImg)));
Result qrCode = qrCodeReader.decode(screenshotBinaryBitmap);
// parse the QR code string
Pattern qrUriPattern = Pattern.compile("^otpauth:\\/\\/(?<type>.+)\\/(?<realm>.+):(?<user>.+)\\?secret=(?<secret>.+)&digits=(?<digits>.+)&algorithm=(?<algorithm>.+)&issuer=(?<issuer>.+)&(?:period=(?<period>.+)|counter=(?<counter>.+))$");
Matcher qrUriMatcher = qrUriPattern.matcher(qrCode.getText());
assertTrue(qrUriMatcher.find());
// extract data
String type = qrUriMatcher.group("type");
String realm = qrUriMatcher.group("realm");
String user = qrUriMatcher.group("user");
String secret = qrUriMatcher.group("secret");
int digits = Integer.parseInt(qrUriMatcher.group("digits"));
String algorithm = qrUriMatcher.group("algorithm");
String issuer = qrUriMatcher.group("issuer");
Integer period = type.equals(TOTP) ? Integer.parseInt(qrUriMatcher.group("period")) : null;
Integer counter = type.equals(HOTP) ? Integer.parseInt(qrUriMatcher.group("counter")) : null;
RealmRepresentation realmRep = testRealmResource().toRepresentation();
String expectedRealmName = realmRep.getDisplayName() != null && !realmRep.getDisplayName().isEmpty() ? realmRep.getDisplayName() : realmRep.getRealm();
// basic assertations
assertEquals(QR_CODE, qrCode.getBarcodeFormat());
assertEquals(expectedRealmName, realm);
assertEquals(expectedRealmName, issuer);
assertEquals(testUser.getUsername(), user);
// the actual test
testOtp(type, algorithm, digits, period, counter, secret);
}
private void testOtp(String type, String algorithm, int digits, Integer period, Integer counter, String secret) {
switch (algorithm) {
case "SHA1":
algorithm = TimeBasedOTP.HMAC_SHA1;
break;
case "SHA256":
algorithm = TimeBasedOTP.HMAC_SHA256;
break;
case "SHA512":
algorithm = TimeBasedOTP.HMAC_SHA512;
break;
default:
throw new AssertionError("Wrong algorithm type");
}
HmacOTP otpGenerator;
String secretDecoded = new String(Base32.decode(secret));
String code;
switch (type) {
case TOTP:
otpGenerator = new TimeBasedOTP(algorithm, digits, period, 0);
code = ((TimeBasedOTP) otpGenerator).generateTOTP(secretDecoded);
break;
case HOTP:
otpGenerator = new HmacOTP(digits, algorithm, 0);
code = otpGenerator.generateHOTP(secretDecoded, counter);
break;
default:
throw new AssertionError("Wrong OTP type");
}
// fill in the form
otpSetupPage.setTotp(code);
otpSetupPage.submit();
assertLoginSuccessful();
// try the code is working
deleteAllSessionsInTestRealm();
testRealmAccountPage.navigateTo();
testRealmLoginPage.form().login(testUser);
oneTimeCodePage.assertCurrent();
assertEquals("One-time code", oneTimeCodePage.getTotpLabel());
// bad attempt
oneTimeCodePage.submit();
assertTrue(oneTimeCodePage.feedbackMessage().isError());
assertEquals("[TEST LOCALE] vložen chybný kód", oneTimeCodePage.feedbackMessage().getText());
oneTimeCodePage.sendCode("XXXXXX");
assertTrue(oneTimeCodePage.feedbackMessage().isError());
assertEquals("[TEST LOCALE] vložen chybný kód", oneTimeCodePage.feedbackMessage().getText());
// generate new code
code = type.equals(TOTP) ? ((TimeBasedOTP) otpGenerator).generateTOTP(secretDecoded) : otpGenerator.generateHOTP(secretDecoded, ++counter);
oneTimeCodePage.sendCode(code);
assertLoginSuccessful();
}
private void setRealmOtpType(String otpType) {
RealmRepresentation realmRep = testRealmResource().toRepresentation();
realmRep.setOtpPolicyType(otpType);
testRealmResource().update(realmRep);
}
private void initiateRequiredAction(RequiredActions requiredActionPage) {
testUser.setRequiredActions(Collections.singletonList(requiredActionPage.getActionId()));
testUserResource().update(testUser);
testRealmAccountPage.navigateTo();
assertCurrentUrlStartsWithLoginUrlOf(testRealmAccountPage);
testRealmLoginPage.form().login(testUser);
requiredActionPage.assertCurrent();
}
private void initiateClientScopesConsent(boolean displayOnConsentScreen, String consentScreenText) {
ClientRepresentation accountClientRep = testRealmResource().clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
ClientResource accountClient = testRealmResource().clients().get(accountClientRep.getId());
accountClientRep.setConsentRequired(true);
accountClientRep.getAttributes().put(DISPLAY_ON_CONSENT_SCREEN, String.valueOf(displayOnConsentScreen));
accountClientRep.getAttributes().put(CONSENT_SCREEN_TEXT, consentScreenText);
accountClient.update(accountClientRep);
testRealmAccountPage.navigateTo();
testRealmLoginPage.form().login(grantRealmUser);
oAuthGrantPage.assertCurrent();
}
private void assertNoAccess() {
assertEquals("No access", loginErrorPage.getErrorMessage());
loginErrorPage.backToApplication();
assertCurrentUrlStartsWithLoginUrlOf(testRealmLoginPage);
}
}

View file

@ -1,6 +1,6 @@
package org.keycloak.testsuite.console.page.authentication;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -70,7 +70,7 @@ public class PasswordPolicy extends Authentication {
private void setPolicyValue(Type policy, String value) {
WebElement input = getPolicyRow(policy).findElement(By.tagName("input"));
Form.setInputValue(input, value);
UIUtils.setTextInputValue(input, value);
}
private WebElement getPolicyRow(Type policy) {

View file

@ -22,6 +22,7 @@
package org.keycloak.testsuite.console.page.authentication.flows;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -59,8 +60,8 @@ public class CreateFlowForm extends Form {
}
public void setValues(String alias, String description, FlowType flowType) {
setInputValue(aliasInput, alias);
setInputValue(descriptionTextarea, description);
UIUtils.setTextInputValue(aliasInput, alias);
UIUtils.setTextInputValue(descriptionTextarea, description);
flowTypeSelect.selectByVisibleText(flowType.getName());
save();
}

View file

@ -22,6 +22,7 @@
package org.keycloak.testsuite.console.page.authentication.otppolicy;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -55,14 +56,14 @@ public class OTPPolicyForm extends Form {
this.otpHashAlg.selectByValue(otpHashAlg.getName());
this.digits.selectByVisibleText("" + digits.getName());
setInputValue(this.lookAhead, lookAhead);
UIUtils.setTextInputValue(this.lookAhead, lookAhead);
switch (otpType) {
case TIME_BASED:
setInputValue(period, periodOrCounter);
UIUtils.setTextInputValue(period, periodOrCounter);
break;
case COUNTER_BASED:
setInputValue(counter, periodOrCounter);
UIUtils.setTextInputValue(counter, periodOrCounter);
break;
}
save();

View file

@ -3,11 +3,12 @@ package org.keycloak.testsuite.console.page.clients;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.Timer;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
import static org.keycloak.testsuite.page.Form.getInputValue;
import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
import static org.keycloak.testsuite.util.WaitUtils.*;
/**
@ -30,11 +31,11 @@ public class CreateClientForm extends Form {
}
public String getClientId() {
return getInputValue(clientIdInput);
return getTextInputValue(clientIdInput);
}
public void setClientId(String clientId) {
setInputValue(clientIdInput, clientId);
UIUtils.setTextInputValue(clientIdInput, clientId);
}
public String getProtocol() {

View file

@ -41,6 +41,7 @@ import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -102,14 +103,14 @@ public class ResourcePermissionForm extends Form {
private GroupPolicy groupPolicy;
public void populate(ResourcePermissionRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
resourceTypeSwitch.setOn(expected.getResourceType() != null);
if (expected.getResourceType() != null) {
setInputValue(resourceType, expected.getResourceType());
UIUtils.setTextInputValue(resourceType, expected.getResourceType());
} else {
resourceTypeSwitch.setOn(false);
resourceSelect.update(expected.getResources());
@ -132,11 +133,11 @@ public class ResourcePermissionForm extends Form {
public ResourcePermissionRepresentation toRepresentation() {
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setDecisionStrategy(DecisionStrategy.valueOf(decisionStrategy.getFirstSelectedOption().getText().toUpperCase()));
representation.setPolicies(policySelect.getSelected());
String inputValue = getInputValue(resourceType);
String inputValue = UIUtils.getTextInputValue(resourceType);
if (!"".equals(inputValue)) {
representation.setResourceType(inputValue);

View file

@ -44,6 +44,7 @@ import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
import org.keycloak.testsuite.console.page.fragment.SingleStringSelect2;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -108,8 +109,8 @@ public class ScopePermissionForm extends Form {
private GroupPolicy groupPolicy;
public void populate(ScopePermissionRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
decisionStrategy.selectByValue(expected.getDecisionStrategy().name());
Set<String> resources = expected.getResources();
@ -141,8 +142,8 @@ public class ScopePermissionForm extends Form {
public ScopePermissionRepresentation toRepresentation() {
ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setDecisionStrategy(DecisionStrategy.valueOf(decisionStrategy.getFirstSelectedOption().getText().toUpperCase()));
representation.setPolicies(policySelect.getSelected());
representation.setResources(resourceSelect.getSelected());

View file

@ -33,6 +33,7 @@ import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -85,8 +86,8 @@ public class AggregatePolicyForm extends Form {
private GroupPolicy groupPolicy;
public void populate(AggregatePolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
Set<String> selectedPolicies = policySelect.getSelected();
@ -128,8 +129,8 @@ public class AggregatePolicyForm extends Form {
public AggregatePolicyRepresentation toRepresentation() {
AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
representation.setPolicies(policySelect.getSelected());

View file

@ -28,6 +28,7 @@ import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -57,8 +58,8 @@ public class ClientPolicyForm extends Form {
protected ModalDialog modalDialog;
public void populate(ClientPolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
clientsInput.update(expected.getClients());
@ -76,8 +77,8 @@ public class ClientPolicyForm extends Form {
public ClientPolicyRepresentation toRepresentation() {
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
representation.setClients(clientsInput.getSelected());

View file

@ -22,6 +22,7 @@ import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
@ -64,9 +65,9 @@ public class GroupPolicyForm extends Form {
private WebDriver driver;
public void populate(GroupPolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
setInputValue(groupsClaim, expected.getGroupsClaim());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(groupsClaim, expected.getGroupsClaim());
logic.selectByValue(expected.getLogic().name());
@ -135,10 +136,10 @@ public class GroupPolicyForm extends Form {
public GroupPolicyRepresentation toRepresentation() {
GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
String groupsClaimValue = getInputValue(groupsClaim);
String groupsClaimValue = UIUtils.getTextInputValue(groupsClaim);
representation.setGroupsClaim(groupsClaim == null || "".equals(groupsClaimValue.trim()) ? null : groupsClaimValue);
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));

View file

@ -20,6 +20,7 @@ import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -46,8 +47,8 @@ public class JSPolicyForm extends Form {
protected ModalDialog modalDialog;
public void populate(JSPolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
JavascriptExecutor scriptExecutor = (JavascriptExecutor) driver;
@ -67,8 +68,8 @@ public class JSPolicyForm extends Form {
public JSPolicyRepresentation toRepresentation() {
JSPolicyRepresentation representation = new JSPolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
JavascriptExecutor scriptExecutor = (JavascriptExecutor) driver;

View file

@ -30,6 +30,7 @@ import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.testsuite.console.page.fragment.AbstractMultipleSelect2;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -65,8 +66,8 @@ public class RolePolicyForm extends Form {
protected ModalDialog modalDialog;
public void populate(RolePolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
Set<RolePolicyRepresentation.RoleDefinition> roles = expected.getRoles();
@ -124,8 +125,8 @@ public class RolePolicyForm extends Form {
public RolePolicyRepresentation toRepresentation() {
RolePolicyRepresentation representation = new RolePolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
Set<RolePolicyRepresentation.RoleDefinition> roles = realmRoleSelect.getSelected();

View file

@ -20,6 +20,7 @@ import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.RulePolicyRepresentation;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -76,11 +77,11 @@ public class RulePolicyForm extends Form {
private WebElement resolveModuleButton;
public void populate(RulePolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
setInputValue(artifactGroupId, expected.getArtifactGroupId());
setInputValue(artifactId, expected.getArtifactId());
setInputValue(artifactVersion, expected.getArtifactVersion());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(artifactGroupId, expected.getArtifactGroupId());
UIUtils.setTextInputValue(artifactId, expected.getArtifactId());
UIUtils.setTextInputValue(artifactVersion, expected.getArtifactVersion());
clickLink(resolveModuleButton);
waitGui().withTimeout(150, TimeUnit.SECONDS).until().element(id("moduleName")).is().enabled(); // The module load time could be long at some conditions
@ -90,7 +91,7 @@ public class RulePolicyForm extends Form {
sessionName.selectByVisibleText(expected.getSessionName());
setInputValue(scannerPeriod, expected.getScannerPeriod());
UIUtils.setTextInputValue(scannerPeriod, expected.getScannerPeriod());
scannerPeriodUnit.selectByVisibleText(expected.getScannerPeriodUnit());
logic.selectByValue(expected.getLogic().name());
@ -107,15 +108,15 @@ public class RulePolicyForm extends Form {
public RulePolicyRepresentation toRepresentation() {
RulePolicyRepresentation representation = new RulePolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
representation.setArtifactGroupId(getInputValue(artifactGroupId));
representation.setArtifactId(getInputValue(artifactId));
representation.setArtifactVersion(getInputValue(artifactVersion));
representation.setArtifactGroupId(UIUtils.getTextInputValue(artifactGroupId));
representation.setArtifactId(UIUtils.getTextInputValue(artifactId));
representation.setArtifactVersion(UIUtils.getTextInputValue(artifactVersion));
representation.setModuleName(moduleName.getFirstSelectedOption().getText());
representation.setSessionName(sessionName.getFirstSelectedOption().getText());
representation.setScannerPeriod(getInputValue(scannerPeriod));
representation.setScannerPeriod(UIUtils.getTextInputValue(scannerPeriod));
representation.setScannerPeriodUnit(scannerPeriodUnit.getFirstSelectedOption().getText());
return representation;

View file

@ -20,6 +20,7 @@ import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -82,21 +83,21 @@ public class TimePolicyForm extends Form {
protected ModalDialog modalDialog;
public void populate(TimePolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
setInputValue(notBefore, expected.getNotBefore());
setInputValue(notOnOrAfter, expected.getNotOnOrAfter());
setInputValue(dayMonth, expected.getDayMonth());
setInputValue(dayMonthEnd, expected.getDayMonthEnd());
setInputValue(month, expected.getMonth());
setInputValue(monthEnd, expected.getMonthEnd());
setInputValue(year, expected.getYear());
setInputValue(yearEnd, expected.getYearEnd());
setInputValue(hour, expected.getHour());
setInputValue(hourEnd, expected.getHourEnd());
setInputValue(minute, expected.getMinute());
setInputValue(minuteEnd, expected.getMinuteEnd());
UIUtils.setTextInputValue(notBefore, expected.getNotBefore());
UIUtils.setTextInputValue(notOnOrAfter, expected.getNotOnOrAfter());
UIUtils.setTextInputValue(dayMonth, expected.getDayMonth());
UIUtils.setTextInputValue(dayMonthEnd, expected.getDayMonthEnd());
UIUtils.setTextInputValue(month, expected.getMonth());
UIUtils.setTextInputValue(monthEnd, expected.getMonthEnd());
UIUtils.setTextInputValue(year, expected.getYear());
UIUtils.setTextInputValue(yearEnd, expected.getYearEnd());
UIUtils.setTextInputValue(hour, expected.getHour());
UIUtils.setTextInputValue(hourEnd, expected.getHourEnd());
UIUtils.setTextInputValue(minute, expected.getMinute());
UIUtils.setTextInputValue(minuteEnd, expected.getMinuteEnd());
if (save) {
save();
@ -111,21 +112,21 @@ public class TimePolicyForm extends Form {
public TimePolicyRepresentation toRepresentation() {
TimePolicyRepresentation representation = new TimePolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
representation.setDayMonth(getInputValue(dayMonth));
representation.setDayMonthEnd(getInputValue(dayMonthEnd));
representation.setMonth(getInputValue(month));
representation.setMonthEnd(getInputValue(monthEnd));
representation.setYear(getInputValue(year));
representation.setYearEnd(getInputValue(yearEnd));
representation.setHour(getInputValue(hour));
representation.setHourEnd(getInputValue(hourEnd));
representation.setMinute(getInputValue(minute));
representation.setMinuteEnd(getInputValue(minuteEnd));
representation.setNotBefore(getInputValue(notBefore));
representation.setNotOnOrAfter(getInputValue(notOnOrAfter));
representation.setDayMonth(UIUtils.getTextInputValue(dayMonth));
representation.setDayMonthEnd(UIUtils.getTextInputValue(dayMonthEnd));
representation.setMonth(UIUtils.getTextInputValue(month));
representation.setMonthEnd(UIUtils.getTextInputValue(monthEnd));
representation.setYear(UIUtils.getTextInputValue(year));
representation.setYearEnd(UIUtils.getTextInputValue(yearEnd));
representation.setHour(UIUtils.getTextInputValue(hour));
representation.setHourEnd(UIUtils.getTextInputValue(hourEnd));
representation.setMinute(UIUtils.getTextInputValue(minute));
representation.setMinuteEnd(UIUtils.getTextInputValue(minuteEnd));
representation.setNotBefore(UIUtils.getTextInputValue(notBefore));
representation.setNotOnOrAfter(UIUtils.getTextInputValue(notOnOrAfter));
return representation;
}

View file

@ -28,6 +28,7 @@ import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -57,8 +58,8 @@ public class UserPolicyForm extends Form {
protected ModalDialog modalDialog;
public void populate(UserPolicyRepresentation expected, boolean save) {
setInputValue(name, expected.getName());
setInputValue(description, expected.getDescription());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(description, expected.getDescription());
logic.selectByValue(expected.getLogic().name());
usersInput.update(expected.getUsers());
@ -76,8 +77,8 @@ public class UserPolicyForm extends Form {
public UserPolicyRepresentation toRepresentation() {
UserPolicyRepresentation representation = new UserPolicyRepresentation();
representation.setName(getInputValue(name));
representation.setDescription(getInputValue(description));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDescription(UIUtils.getTextInputValue(description));
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
representation.setUsers(usersInput.getSelected());

View file

@ -25,6 +25,7 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
@ -78,16 +79,16 @@ public class ResourceForm extends Form {
}
}
setInputValue(name, expected.getName());
setInputValue(displayName, expected.getDisplayName());
setInputValue(type, expected.getType());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(displayName, expected.getDisplayName());
UIUtils.setTextInputValue(type, expected.getType());
for (String uri : expected.getUris()) {
setInputValue(newUri, uri);
UIUtils.setTextInputValue(newUri, uri);
addUriButton.click();
}
setInputValue(iconUri, expected.getIconUri());
UIUtils.setTextInputValue(iconUri, expected.getIconUri());
Set<ScopeRepresentation> scopes = expected.getScopes();
@ -123,15 +124,15 @@ public class ResourceForm extends Form {
public ResourceRepresentation toRepresentation() {
ResourceRepresentation representation = new ResourceRepresentation();
representation.setName(getInputValue(name));
representation.setDisplayName(getInputValue(displayName));
representation.setType(getInputValue(type));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDisplayName(UIUtils.getTextInputValue(displayName));
representation.setType(UIUtils.getTextInputValue(type));
for (WebElement uriInput : driver.findElements(By.xpath("//input[@ng-model='resource.uris[i]']"))) {
representation.addUri(getInputValue(uriInput));
representation.addUri(UIUtils.getTextInputValue(uriInput));
}
representation.setIconUri(getInputValue(iconUri));
representation.setIconUri(UIUtils.getTextInputValue(iconUri));
representation.setScopes(scopesInput.getSelected());
return representation;
@ -152,7 +153,7 @@ public class ResourceForm extends Form {
private List<WebElement> selection;
public void select(String name) {
setInputValue(search, name);
UIUtils.setTextInputValue(search, name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.page.clients.authorization.scope;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -43,9 +44,9 @@ public class ScopeForm extends Form {
protected ModalDialog modalDialog;
public void populate(ScopeRepresentation expected) {
setInputValue(name, expected.getName());
setInputValue(displayName, expected.getDisplayName());
setInputValue(iconUri, expected.getIconUri());
UIUtils.setTextInputValue(name, expected.getName());
UIUtils.setTextInputValue(displayName, expected.getDisplayName());
UIUtils.setTextInputValue(iconUri, expected.getIconUri());
save();
}
@ -57,9 +58,9 @@ public class ScopeForm extends Form {
public ScopeRepresentation toRepresentation() {
ScopeRepresentation representation = new ScopeRepresentation();
representation.setName(getInputValue(name));
representation.setDisplayName(getInputValue(displayName));
representation.setIconUri(getInputValue(iconUri));
representation.setName(UIUtils.getTextInputValue(name));
representation.setDisplayName(UIUtils.getTextInputValue(displayName));
representation.setIconUri(UIUtils.getTextInputValue(iconUri));
return representation;
}

View file

@ -22,6 +22,7 @@
package org.keycloak.testsuite.console.page.clients.clustering;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@ -45,7 +46,7 @@ public class ClientClusteringForm extends Form {
private WebElement hostNameInput;
private void setNodeReRegistrationTimeout(String value) {
setInputValue(nodeReRegistrationTimeoutInput, value);
UIUtils.setTextInputValue(nodeReRegistrationTimeoutInput, value);
}
private void setNodeReRegistrationTimeoutUnit(String value) {
@ -60,7 +61,7 @@ public class ClientClusteringForm extends Form {
public void addNode(String hostName) {
registerNodeManuallyLink.click();
// waitforElement(hostNameInput);
setInputValue(hostNameInput, hostName);
UIUtils.setTextInputValue(hostNameInput, hostName);
save();
}
}

Some files were not shown because too many files have changed in this diff Show more