applicationLinks;
+
+ public boolean containsApplication(String application) {
+ boolean contains = false;
+ for (WebElement appLink : applicationLinks) {
+ if (appLink.getText().equals(application)) {
+ contains = true;
+ break;
+ }
+ }
+ return contains;
+ }
+
+ public void clickApplication(String application) {
+ for (WebElement appLink : applicationLinks) {
+ if (appLink.getText().equals(application)) {
+ appLink.click();
+ }
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/Constants.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Autheticator.java
similarity index 70%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/Constants.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Autheticator.java
index 2c50f935bd0..b0a42c29e0f 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/Constants.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Autheticator.java
@@ -15,19 +15,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.auth.page.account;
-package org.keycloak.testsuite.admin.util;
+import javax.ws.rs.core.UriBuilder;
/**
*
- * @author Petr Mensik
+ * @author tkyjovsk
*/
-public final class Constants {
+public class Autheticator extends AccountManagement {
- private Constants() {
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("totp");
}
-
- public static String CURRENT_REALM = "master";
-
- public static final String ADMIN_PSSWD = "admin";
+
+
+
}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ChangePassword.java
similarity index 59%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/AbstractKeycloakTest.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ChangePassword.java
index 4c756a10dd0..f7ee0da1721 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ChangePassword.java
@@ -15,34 +15,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.auth.page.account;
-package org.keycloak.testsuite.admin;
-
-import java.util.concurrent.TimeUnit;
+import javax.ws.rs.core.UriBuilder;
import org.jboss.arquillian.graphene.page.Page;
-import org.junit.After;
-import org.junit.Before;
-import org.keycloak.testsuite.admin.page.AbstractPage;
/**
*
* @author Petr Mensik
- * @param
*/
-public abstract class AbstractKeycloakTest
extends AbstractTest {
-
+public class ChangePassword extends AccountManagement {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("password");
+ }
+
@Page
- protected P page;
-
- @Before
- public void before() {
- driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS);
- driver.manage().window().maximize();
- loginAsAdmin();
- }
-
- @After
- public void after() {
- logOut();
- }
+ private PasswordFields passwordFields;
+
+ public void changePasswords(String password, String newPassword, String confirmPassword) {
+ passwordFields.setPasswords(password, newPassword, confirmPassword);
+ save();
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java
new file mode 100644
index 00000000000..6d1a96c7621
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java
@@ -0,0 +1,24 @@
+package org.keycloak.testsuite.auth.page.account;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+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;
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/PasswordFields.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/PasswordFields.java
new file mode 100644
index 00000000000..1d36bf520db
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/PasswordFields.java
@@ -0,0 +1,38 @@
+package org.keycloak.testsuite.auth.page.account;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @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);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Sessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Sessions.java
new file mode 100644
index 00000000000..884b6ca43c0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Sessions.java
@@ -0,0 +1,58 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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 java.util.LinkedList;
+import java.util.List;
+import javax.ws.rs.core.UriBuilder;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Sessions extends AccountManagement {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("sessions");
+ }
+
+ @FindBy(id = "logout-all-sessions")
+ private WebElement logoutAllLink;
+
+ public void logoutAll() {
+ logoutAllLink.click();
+ }
+
+ public List> getSessions() {
+ List> table = new LinkedList>();
+ for (WebElement r : driver.findElements(By.tagName("tr"))) {
+ List row = new LinkedList();
+ for (WebElement col : r.findElements(By.tagName("td"))) {
+ row.add(col.getText());
+ }
+ table.add(row);
+ }
+ table.remove(0);
+ return table;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java
new file mode 100644
index 00000000000..97dc1da3a64
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java
@@ -0,0 +1,24 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @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;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java
new file mode 100644
index 00000000000..e06c5b96ab5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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 javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementNotPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public abstract class Login extends AuthRealm {
+
+ public static final String PROTOCOL = "protocol";
+ public static final String OIDC = "openid-connect";
+ public static final String SAML = "saml";
+
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("protocol/{" + PROTOCOL + "}/auth");
+ }
+
+ public void setProtocol(String protocol) {
+ setUriParameter(PROTOCOL, protocol);
+ }
+
+ public String getProtocol() {
+ return getUriParameter(PROTOCOL).toString();
+ }
+
+ @Page
+ private LoginForm form;
+
+ public LoginForm form() {
+ return form;
+ }
+
+ @FindBy(css = "link[href*='login/keycloak/css/login.css']")
+ private WebElement keycloakTheme;
+
+ public void waitForKeycloakThemeNotPresent() {
+ waitGuiForElementNotPresent(keycloakTheme);
+ }
+
+ public void waitForKeycloakThemePresent() {
+ waitGuiForElement(keycloakTheme);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java
new file mode 100644
index 00000000000..34416770023
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java
@@ -0,0 +1,43 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class LoginActions extends AuthRealm {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("login-actions");
+ }
+
+ @FindBy(css = "input[type='submit']")
+ private WebElement submitButton;
+
+ @FindBy(css = "div[id='kc-form-options'] span a")
+ private WebElement backToLoginForm;
+
+ @FindBy(css = "span.kc-feedback-text")
+ private WebElement feedbackText;
+
+ public String getFeedbackText() {
+ waitGuiForElementPresent(feedbackText, "Feedback message should be visible");
+ return feedbackText.getText();
+ }
+
+ public void backToLoginPage() {
+ backToLoginForm.click();
+ }
+
+ public void submit() {
+ submitButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java
new file mode 100644
index 00000000000..61075f22304
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElementNotPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class LoginForm extends Form {
+
+ @Page
+ private AccountFields accountFields;
+ @Page
+ private PasswordFields passwordFields;
+
+ @FindBy(name = "login")
+ private WebElement loginButton;
+// @FindBy(name = "cancel")
+// private WebElement cancelButton;
+
+ @FindBy(linkText = "Register")
+ private WebElement registerLink;
+ @FindBy(linkText = "Forgot Password?")
+ private WebElement forgottenPassword;
+
+ public void setUsername(String username) {
+ accountFields.setUsername(username);
+ }
+
+ public void setPassword(String password) {
+ passwordFields.setPassword(password);
+ }
+
+ public void login(UserRepresentation user) {
+ login(user.getUsername(), getPasswordOf(user));
+ }
+
+ public void login(String username, String password) {
+ setUsername(username);
+ setPassword(password);
+ login();
+ }
+
+ public void register() {
+ waitForUsernameInputPresent();
+ waitAjaxForElement(registerLink);
+ registerLink.click();
+ }
+
+ public void login() {
+ waitAjaxForElement(loginButton);
+ loginButton.click();
+ }
+
+ public void forgotPassword() {
+ waitAjaxForElement(forgottenPassword);
+ forgottenPassword.click();
+ }
+
+// @Override
+// public void cancel() {
+// waitAjaxForElement(cancelButton);
+// cancelButton.click();
+// }
+
+ public void waitForUsernameInputPresent() {
+ accountFields.waitForUsernameInputPresent();
+ }
+
+ public void waitForRegisterLinkNotPresent() {
+ waitAjaxForElementNotPresent(registerLink);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java
new file mode 100644
index 00000000000..d002da8e0f3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java
@@ -0,0 +1,13 @@
+package org.keycloak.testsuite.auth.page.login;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class OIDCLogin extends Login {
+
+ public OIDCLogin() {
+ setProtocol(OIDC);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java
new file mode 100644
index 00000000000..53bef179eda
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java
@@ -0,0 +1,15 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import java.net.URI;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * Used by util class LoginAssert. Implementing classes: AuthRealm, AdminConsole.
+ * @author tkyjovsk
+ */
+public interface PageWithLoginUrl {
+
+ WebDriver getDriver();
+ URI getOIDCLoginUrl();
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java
new file mode 100644
index 00000000000..da70da454df
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.auth.page.account.AccountFields;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import org.keycloak.testsuite.auth.page.account.ContactInfoFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class Registration extends LoginActions {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("registration");
+ }
+
+ @Page
+ private AccountFields accountFields;
+
+ @Page
+ private PasswordFields passwordFields;
+
+ @Page
+ private ContactInfoFields contactInfoFields;
+
+ public void register(UserRepresentation user) {
+ setValues(user);
+ submit();
+ }
+
+ public void setValues(UserRepresentation user) {
+ setValues(user, getPasswordOf(user));
+ }
+
+ public void setValues(UserRepresentation user, String confirmPassword) {
+ accountFields.setValues(user);
+ passwordFields.setPassword(getPasswordOf(user));
+ passwordFields.setConfirmPassword(confirmPassword);
+ }
+
+ public void waitForUsernameInputPresent() {
+ accountFields.waitForUsernameInputPresent();
+ }
+
+ public void waitForUsernameInputNotPresent() {
+ accountFields.waitForUsernameInputNotPresent();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java
new file mode 100644
index 00000000000..83ab9b5bc6f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java
@@ -0,0 +1,62 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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 javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class ResetCredentials extends LoginActions {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("reset-credentials");
+ }
+
+ @Page
+ private AccountFields accountFields;
+ @Page
+ private PasswordFields passwordFields;
+
+ @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);
+ submit();
+ }
+
+ public void waitForInfoMessagePresent() {
+ waitGuiForElementPresent(info, "Info message should be visible");
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
new file mode 100644
index 00000000000..b1e5c2527cc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UpdateAccount extends Authenticate {
+
+ @Page
+ private AccountFields accountFields;
+
+ public void updateAccount(UserRepresentation user) {
+ updateAccount(user.getEmail(), user.getFirstName(), user.getLastName());
+ }
+
+ public void updateAccount(String email, String firstName, String lastName) {
+ accountFields.setEmail(email);
+ accountFields.setFirstName(firstName);
+ accountFields.setLastName(lastName);
+ submit();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java
new file mode 100644
index 00000000000..b0cc4c36043
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java
@@ -0,0 +1,21 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UpdatePassword extends Authenticate {
+
+ @Page
+ private PasswordFields passwordFields;
+
+ public void updatePasswords(String newPassword, String confirmPassword) {
+ passwordFields.setNewPassword(newPassword);
+ passwordFields.setConfirmPassword(confirmPassword);
+ submit();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
new file mode 100644
index 00000000000..575ce7b18bd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
@@ -0,0 +1,9 @@
+package org.keycloak.testsuite.auth.page.login;
+
+/**
+ *
+ * @author vramik
+ */
+public class VerifyEmail extends Authenticate {
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
new file mode 100644
index 00000000000..47bff5df308
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page;
+
+import java.net.URI;
+import org.keycloak.testsuite.auth.page.AuthServer;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
+import org.keycloak.testsuite.console.page.fragment.Menu;
+import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AdminConsole extends AuthServer implements PageWithLoginUrl {
+
+ public static final String ADMIN_REALM = "adminRealm";
+
+ public AdminConsole() {
+ setUriParameter(ADMIN_REALM, MASTER);
+ }
+
+ public AdminConsole setAdminRealm(String adminRealm) {
+ setUriParameter(ADMIN_REALM, adminRealm);
+ return this;
+ }
+
+ public String getAdminRealm() {
+ return getUriParameter(ADMIN_REALM).toString();
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("admin/{" + ADMIN_REALM + "}/console");
+ }
+
+ @Page
+ private Menu menu;
+
+ @FindBy(xpath = "//div[@class='modal-dialog']")
+ protected ModalDialog modalDialog;
+
+ /**
+ *
+ * @return OIDC Login URL for adminRealm parameter
+ */
+ @Override
+ public URI getOIDCLoginUrl() {
+ return OIDCLoginProtocolService.authUrl(UriBuilder.fromPath(getAuthRoot()))
+ .build(getAdminRealm());
+ }
+
+ @FindBy(css = ".btn-danger")
+ protected WebElement dangerButton;
+
+ //@FindByJQuery(".btn-primary:visible")
+ @FindBy(css = ".btn-primary")
+ protected WebElement primaryButton;
+
+ @FindBy(css = "navbar-brand")
+ protected WebElement brandLink;
+
+ public void logOut() {
+ menu.logOut();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java
new file mode 100644
index 00000000000..35be14d37ab
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java
@@ -0,0 +1,47 @@
+package org.keycloak.testsuite.console.page;
+
+import javax.ws.rs.core.UriBuilder;
+import static org.keycloak.testsuite.console.page.AdminConsoleRealm.CONSOLE_REALM;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleCreate extends AdminConsole {
+
+ public static final String ENTITY = "entity";
+
+ public AdminConsoleCreate() {
+ setUriParameter(CONSOLE_REALM, TEST);
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("/");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return "/create/{" + ENTITY + "}/{" + CONSOLE_REALM + "}";
+ }
+
+ public AdminConsoleCreate setEntity(String entity) {
+ setUriParameter(ENTITY, entity);
+ return this;
+ }
+
+ public String getEntity() {
+ return getUriParameter(ENTITY).toString();
+ }
+
+ public AdminConsoleCreate setConsoleRealm(String consoleRealm) {
+ setUriParameter(CONSOLE_REALM, consoleRealm);
+ return this;
+ }
+
+ public String getConsoleRealm() {
+ return getUriParameter(CONSOLE_REALM).toString();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java
new file mode 100644
index 00000000000..4fd57baa4c3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java
@@ -0,0 +1,121 @@
+package org.keycloak.testsuite.console.page;
+
+import org.keycloak.admin.client.resource.RealmResource;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+import org.keycloak.testsuite.util.WaitUtils;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleRealm extends AdminConsoleRealmsRoot {
+
+ public static final String CONSOLE_REALM = "consoleRealm";
+
+ public AdminConsoleRealm() {
+ setUriParameter(CONSOLE_REALM, TEST);
+ }
+
+ public AdminConsoleRealm setConsoleRealm(String realm) {
+ setUriParameter(CONSOLE_REALM, realm);
+ return this;
+ }
+
+ public String getConsoleRealm() {
+ return getUriParameter(CONSOLE_REALM).toString();
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + CONSOLE_REALM + "}";
+ }
+
+ @FindBy(xpath = "//div[./h2[text()='Configure']]")
+ private ConfigureMenu configureMenu;
+
+ public ConfigureMenu configure() {
+ waitGuiForElement(By.xpath("//div[./h2[text()='Configure']]"));
+ return configureMenu;
+ }
+
+ public RealmResource realmResource() {
+ return realmsResource().realm(getConsoleRealm());
+ }
+
+ public class ConfigureMenu {
+
+ @FindBy(partialLinkText = "Realm Settings")
+ private WebElement realmSettingsLink;
+ @FindBy(partialLinkText = "Clients")
+ private WebElement clientsLink;
+ @FindBy(partialLinkText = "Roles")
+ private WebElement rolesLink;
+ @FindBy(partialLinkText = "Identity Providers")
+ private WebElement identityProvidersLink;
+ @FindBy(partialLinkText = "User Feferation")
+ private WebElement userFederationLink;
+ @FindBy(partialLinkText = "Authentication")
+ private WebElement authenticationLink;
+
+ public void realmSettings() {
+ realmSettingsLink.click();
+ }
+
+ public void clients() {
+ clientsLink.click();
+ }
+
+ public void roles() {
+ rolesLink.click();
+ }
+
+ public void identityProviders() {
+ identityProvidersLink.click();
+ }
+
+ public void userFederation() {
+ userFederationLink.click();
+ }
+
+ public void authentication() {
+ authenticationLink.click();
+ }
+
+ }
+
+ @FindBy(xpath = "//div[./h2[text()='Manage']]")
+ protected ManageMenu manageMenu;
+
+ public ManageMenu manage() {
+ WaitUtils.waitGuiForElement(By.xpath("//div[./h2[text()='Manage']]"));
+ return manageMenu;
+ }
+
+ public class ManageMenu {
+
+ @FindBy(partialLinkText = "Users")
+ private WebElement usersLink;
+ @FindBy(partialLinkText = "Sessions")
+ private WebElement sessionsLink;
+ @FindBy(partialLinkText = "Events")
+ private WebElement eventsLink;
+
+ public void users() {
+ usersLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ public void events() {
+ eventsLink.click();
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java
new file mode 100644
index 00000000000..bb001ea6717
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java
@@ -0,0 +1,49 @@
+package org.keycloak.testsuite.console.page;
+
+import java.util.List;
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.admin.client.resource.RealmsResource;
+import org.keycloak.testsuite.console.page.fragment.RealmSelector;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleRealmsRoot extends AdminConsole {
+
+ @FindBy(xpath = "//tr[@data-ng-repeat='r in realms']//a[contains(@class,'ng-binding')]")
+ private List realmLinks;
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("/");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return "/realms";
+ }
+
+ public void clickRealm(String realm) {
+ boolean linkFound = false;
+ for (WebElement realmLink : realmLinks) {
+ if (realmLink.getText().equals(realm)) {
+ linkFound = true;
+ realmLink.click();
+ }
+ }
+ if (!linkFound) {
+ throw new IllegalStateException("A link for realm '" + realm + "' not found on the Realms page.");
+ }
+ }
+
+ @FindBy(css = "realm-selector")
+ protected RealmSelector realmSelector;
+
+ public RealmsResource realmsResource() {
+ return keycloak.realms();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java
new file mode 100644
index 00000000000..5cd74bf3c00
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Authentication extends AdminConsoleRealm {
+
+ @FindBy(xpath = "//h1[text()='Authentication']/..")
+ private AuthenticationTabs authenticationTabs;
+
+ public AuthenticationTabs tabs() {
+ return authenticationTabs;
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/authentication";
+ }
+
+ public class AuthenticationTabs {
+ @FindBy(linkText = "Flows")
+ private WebElement flowsTab;
+ @FindBy(linkText = "Required Actions")
+ private WebElement requiredActionsTab;
+ @FindBy(linkText = "Password Policy")
+ private WebElement passwordPolicyTab;
+ @FindBy(linkText = "Bindings")
+ private WebElement binding;
+ @FindBy(linkText = "OTP Policy")
+ private WebElement otpPolicy;
+
+ public void flows() {
+ flowsTab.click();
+ }
+
+ public void requiredActions() {
+ requiredActionsTab.click();
+ }
+
+ public void passwordPolicy() {
+ passwordPolicyTab.click();
+ }
+
+ public void binding() {
+ binding.click();
+ }
+
+ public void otpPolicy() {
+ otpPolicy.click();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java
new file mode 100644
index 00000000000..6e8d4a453ed
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java
@@ -0,0 +1,147 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * Created by mhajas on 8/21/15.
+ */
+public class Bindings extends Authentication{
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/flow-binding";
+ }
+
+ @FindBy(id = "browser")
+ private Select BrowserFlowSelect;
+
+ public void changeBrowserFlowSelect(BrowserFlowSelectValues value) {
+ BrowserFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "registration")
+ private Select RegistrationFlowSelect;
+
+ public void changeRegistrationFlowSelect(RegistrationFlowSelectValues value) {
+ RegistrationFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "grant")
+ private Select DirectGrantFlowSelect;
+
+ public void changeDirectGrantFlowSelect(DirectGrantFlowSelectValues value) {
+ DirectGrantFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "resetCredentials")
+ private Select ResetCredentialsSelect;
+
+ public void changeResetCredentialsSelect(ResetCredentialsSelectValues value) {
+ ResetCredentialsSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "clientAuthentication")
+ private Select ClientAuthenticationSelect;
+
+ public void changeClientAuthenticationSelect(ClientAuthenticationSelectValues value) {
+ ClientAuthenticationSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(xpath = "//button[text()='Save']")
+ private WebElement saveButton;
+
+ public void clickSave() {
+ saveButton.click();
+ }
+
+ @FindBy(xpath = "//button[text()='Cancel']")
+ private WebElement cancelButton;
+
+ public void clickCancel() {
+ cancelButton.click();
+ }
+
+ public enum BrowserFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private BrowserFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum RegistrationFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private RegistrationFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum DirectGrantFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private DirectGrantFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum ResetCredentialsSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials"), NOTHING("");
+
+ private String name;
+
+ private ResetCredentialsSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum ClientAuthenticationSelectValues {
+
+ CLIENTS("clients");
+
+ private String name;
+
+ private ClientAuthenticationSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java
new file mode 100644
index 00000000000..199406440c0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java
@@ -0,0 +1,212 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Flows extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/flows";
+ }
+
+ @FindBy(tagName = "select")
+ private Select flowSelect;
+
+ public void changeFlowSelect(FlowSelectValues value) {
+ flowSelect.selectByVisibleText(value.getName());
+ }
+
+ public enum FlowSelectValues {
+
+ DIRECT_GRANT("Direct grant"), REGISTRATION("Registration"), BROWSER("Browser"),
+ RESET_CREDENTIALS("Reset credentials"), CLIENTS("Clients");
+
+ private String name;
+
+ private FlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ @FindBy(linkText = "New")
+ private WebElement newButton;
+
+ @FindBy(linkText = "Copy")
+ private WebElement copyButton;
+
+ public void clickNew() {
+ newButton.click();
+ }
+
+ public void clickCopy() {
+ copyButton.click();
+ }
+
+ // Direct grant
+ public void setPasswordRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setOTPOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Registration
+ public void setRegistrationFormRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration form')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationFormDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration form')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationUserCreationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration User Creation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationUserCreationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration User Creation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setProfileValidationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Profile Validation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setProfileValidationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Profile Validation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordValidationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password Validation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordValidationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password Validation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setRecaptchaRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Recaptcha')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRecaptchaDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Recaptcha')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Browser
+ public void setCookieAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Cookie')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setCookieDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Cookie')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ public void setFormsAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setFormsRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setFormsDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ // Reset credentials
+ public void setResetPasswordRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setResetPasswordOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setResetPasswordDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Clients
+ public void setClientIdAndSecretRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setClientIdAndSecretAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setClientIdAndSecretDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[4]//input[@type='radio']")).click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java
new file mode 100644
index 00000000000..34e8a098d98
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java
@@ -0,0 +1,85 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * Created by mhajas on 8/21/15.
+ */
+public class OTPPolicy extends Authentication {
+
+ @FindBy(linkText = "Save")
+ private WebElement saveButton;
+
+ public void clickSave() {
+ saveButton.click();
+ }
+
+ @FindBy(linkText = "Cancel")
+ private WebElement cancelButton;
+
+ public void clickCancel() {
+ cancelButton.click();
+ }
+
+ @FindBy(id = "lookAhead")
+ private WebElement lookAheadInput;
+
+ public void setLookAheadInputValue(String value) {
+ Form.setInputValue(lookAheadInput, value);
+ }
+
+ @FindBy(id = "counter")
+ private WebElement initialCounterInput;
+
+ public void setInitialcounterInputValue(String value) {
+ Form.setInputValue(initialCounterInput, value);
+ }
+
+ public enum OTPTypeSelectValues {
+
+ TIME_BASED("time Based"), COUNTER_BASED("Counter Based");
+
+ private String name;
+
+ private OTPTypeSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum OTPHashAlgorithmSelectValues {
+
+ SHA1("SHA1"), SHA256("SHA256"), SHA512("SHA512");
+
+ private String name;
+
+ private OTPHashAlgorithmSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum NumberOfDigitsSelectValues {
+
+ NUMBER6("6"), NUMBER8("8");
+
+ private String name;
+
+ private NumberOfDigitsSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
new file mode 100644
index 00000000000..b2b08b54936
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
@@ -0,0 +1,100 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import java.util.List;
+
+import org.jboss.arquillian.graphene.findby.ByJQuery;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author Petr Mensik
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class PasswordPolicy extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/password-policy";
+ }
+
+ @FindBy(tagName = "select")
+ private Select addPolicySelect;
+
+ @FindBy(tagName = "select")
+ private WebElement addPolicySelectElement;
+
+ @FindBy(css = "tr.ng-scope")
+ private List allRows;
+
+ public void addPolicy(PasswordPolicy.Type policy, String value) {
+ waitGuiForElement(addPolicySelectElement);
+ addPolicySelect.selectByVisibleText(policy.getName());
+ setPolicyValue(policy, value);
+ primaryButton.click();
+ }
+
+
+ public void addPolicy(PasswordPolicy.Type policy, int value) {
+ addPolicy(policy, String.valueOf(value));
+ }
+
+ public void addPolicy(PasswordPolicy.Type policy) {
+ addPolicySelect.selectByVisibleText(policy.getName());
+ primaryButton.click();
+ }
+
+ public void removePolicy(PasswordPolicy.Type policy) {
+ int policyInputLocation = findPolicy(policy);
+ allRows.get(policyInputLocation).findElements(By.tagName("button")).get(0).click();
+ primaryButton.click();
+ }
+
+ public void editPolicy(PasswordPolicy.Type policy, int value) {
+ editPolicy(policy, String.valueOf(value));
+ }
+
+ public void editPolicy(PasswordPolicy.Type policy, String value) {
+ setPolicyValue(policy, value);
+ primaryButton.click();
+ }
+
+ private void setPolicyValue(PasswordPolicy.Type policy, String value) {
+ int policyInputLocation = findPolicy(policy);
+ WebElement input = allRows.get(policyInputLocation).findElement(By.tagName("input"));
+ input.clear();
+ input.sendKeys(value);
+ }
+
+ private int findPolicy(PasswordPolicy.Type policy) {
+ for (int i = 0; i < allRows.size(); i++) {
+ String policyName = allRows.get(i).findElement(ByJQuery.selector("td:eq(0)")).getText();
+ if (policyName.equals(policy.getName())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ public enum Type {
+
+ HASH_ITERATIONS("Hash Iterations"), LENGTH("Length"), DIGITS("Digits"), LOWER_CASE("Lower Case"),
+ UPPER_CASE("Upper Case"), SPECIAL_CHARS("Special Chars"), NOT_USERNAME("Not Username"),
+ REGEX_PATTERN("Regex Pattern"), PASSWORD_HISTORY("Password History"),
+ FORCE_EXPIRED_PASSWORD_CHANGE("Force Expired Password Change");
+
+ private String name;
+
+ private Type(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java
new file mode 100644
index 00000000000..5da1cdaaedb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java
@@ -0,0 +1,55 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.By;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class RequiredActions extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/required-actions";
+ }
+
+ public void clickTermsAndConditionEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Terms and Conditions']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickTermsAndConditionDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Terms and Conditions']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickVerifyEmailEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Verify Email']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickVerifyEmailDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Verify Email']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickUpdatePasswordEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Password']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickUpdatePasswordDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Password']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickConfigureTotpEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Configure Totp']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickConfigureTotpDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Configure Totp']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickUpdateProfileEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Profile']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickUpdateProfileDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Profile']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
new file mode 100644
index 00000000000..783cd0bc06f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
@@ -0,0 +1,107 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.testsuite.console.page.fragment.Breadcrumb;
+import static org.keycloak.testsuite.console.page.fragment.Breadcrumb.BREADCRUMB_XPATH;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Client extends Clients {
+
+ public static final String ID = "id"; // TODO client.id vs client.clientId
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ID + "}";
+ }
+
+ public final void setId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getId() {
+ return getUriParameter(ID).toString();
+ }
+
+ @FindBy(xpath = BREADCRUMB_XPATH)
+ private Breadcrumb breadcrumb;
+
+ public Breadcrumb breadcrumb() {
+ return breadcrumb;
+ }
+
+ public void backToClientsViaBreadcrumb() {
+ breadcrumb.clickItemOneLevelUp();
+ }
+
+ @FindBy(id = "removeClient")
+ private WebElement deleteIcon;
+
+ public void delete() {
+ deleteIcon.click();
+ modalDialog.confirmDeletion();
+ }
+
+ @FindBy(xpath = "//div[@data-ng-controller='ClientTabCtrl']/ul")
+ protected ClientTabs clientTabs;
+
+ public ClientTabs tabs() {
+ return clientTabs;
+ }
+
+ public class ClientTabs {
+
+ @FindBy(linkText = "Settings")
+ private WebElement settingsLink;
+ @FindBy(linkText = "Roles")
+ private WebElement rolesLink;
+ @FindBy(linkText = "Mappers")
+ private WebElement mappersLink;
+ @FindBy(linkText = "Scope")
+ private WebElement scopeLink;
+ @FindBy(linkText = "Revocation")
+ private WebElement revocationLink;
+ @FindBy(linkText = "Sessions")
+ private WebElement sessionsLink;
+ @FindBy(linkText = "Installation")
+ private WebElement installationLink;
+
+ public void settings() {
+ settingsLink.click();
+ }
+
+ public void roles() {
+ rolesLink.click();
+ }
+
+ public void mappers() {
+ mappersLink.click();
+ }
+
+ public void scope() {
+ scopeLink.click();
+ }
+
+ public void revocation() {
+ revocationLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ public void installation() {
+ installationLink.click();
+ }
+
+ }
+
+ public ClientResource clientResource() {
+ return clientsResource().get(getId());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java
new file mode 100644
index 00000000000..cdc8d6b0c47
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientClustering extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clustering";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java
new file mode 100644
index 00000000000..08a3523159f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientCredentials extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/credentials";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java
new file mode 100644
index 00000000000..65c1c6c7588
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientInstallation extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/installation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java
new file mode 100644
index 00000000000..d56dca5482c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientMappers extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/mappers";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java
new file mode 100644
index 00000000000..fb161a89a49
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRevocation extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/revocation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java
new file mode 100644
index 00000000000..d0a0b0d5e8c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java
@@ -0,0 +1,36 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.testsuite.console.page.roles.*;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRole extends ClientRoles {
+
+ public static final String ROLE_ID = "roleId";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ROLE_ID + "}";
+ }
+
+ public void setRoleId(String id) {
+ setUriParameter(ROLE_ID, id);
+ }
+
+ public String getRoleId() {
+ return getUriParameter(ROLE_ID).toString();
+ }
+
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+ public void backToClientRolesViaBreadcrumb() {
+ breadcrumb().clickItemOneLevelUp();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java
new file mode 100644
index 00000000000..a8ee969bbfb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.testsuite.console.page.roles.RolesTable;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRoles extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/roles";
+ }
+
+ @FindBy(css = "table[class*='table']")
+ private RolesTable table;
+
+ public RolesTable roles() {
+ return table;
+ }
+
+ public RolesResource rolesResource() {
+ return clientResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java
new file mode 100644
index 00000000000..32107fd2296
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientScopeMappings extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/scope-mappings";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java
new file mode 100644
index 00000000000..eae50132009
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSessions extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java
new file mode 100644
index 00000000000..8fe1c2c3deb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java
@@ -0,0 +1,18 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSettings extends Client {
+
+ @Page
+ private ClientSettingsForm form;
+
+ public ClientSettingsForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
new file mode 100644
index 00000000000..41cdf71d21e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
@@ -0,0 +1,91 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSettingsForm extends CreateClientForm {
+
+ @FindBy(id = "baseUrl")
+ private WebElement baseUrlInput;
+ @FindBy(id = "adminUrl")
+ private WebElement adminUrlInput;
+
+ @FindBy(id = "newWebOrigin")
+ private WebElement newWebOriginInput;
+ @FindBy(xpath = ".//input[ng-model='client.webOrigins[i]']")
+ private List webOriginInputs;
+ @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteWebOrigin')]")
+ private List deleteWebOriginIcons;
+
+ public void setBaseUrl(String baseUrl) {
+ setInputValue(baseUrlInput, baseUrl);
+ }
+
+ public String getBaseUrl() {
+ return getInputValue(baseUrlInput);
+ }
+
+ public void setAdminUrl(String adminUrl) {
+ setInputValue(adminUrlInput, adminUrl);
+ }
+
+ public String getAdminUrl() {
+ return getInputValue(adminUrlInput);
+ }
+
+ public void addWebOrigin(String redirectUri) {
+ newWebOriginInput.sendKeys(redirectUri);
+ }
+
+ public List getWebOrigins() {
+ List values = new ArrayList<>();
+ for (WebElement input : webOriginInputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+ public void setWebOrigins(List webOrigins) {
+ while (!deleteWebOriginIcons.isEmpty()) {
+ deleteWebOriginIcons.get(0).click();
+ pause(100);
+ }
+ if (webOrigins != null) {
+ for (String redirectUri : webOrigins) {
+ addWebOrigin(redirectUri);
+ pause(100);
+ }
+ }
+ }
+
+ @Override
+ public void setValues(ClientRepresentation client) {
+ super.setValues(client);
+ setBaseUrl(client.getBaseUrl());
+ if (OIDC.equals(client.getProtocol())) {
+ setAdminUrl(client.getAdminUrl());
+ setWebOrigins(client.getWebOrigins());
+ }
+ }
+
+ @Override
+ public ClientRepresentation getValues() {
+ ClientRepresentation values = super.getValues();
+ values.setBaseUrl(getBaseUrl());
+ if (OIDC.equals(values.getProtocol())) {
+ values.setAdminUrl(getAdminUrl());
+ values.setWebOrigins(getWebOrigins());
+ }
+ return values;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
new file mode 100644
index 00000000000..6f9cccd64a7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
@@ -0,0 +1,138 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.clients;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+
+import static org.openqa.selenium.By.linkText;
+import static org.openqa.selenium.By.tagName;
+
+/**
+ *
+ * @author Filip Kisss
+ */
+public class Clients extends AdminConsoleRealm {
+
+ public static final String CREATE = "Create";
+ public static final String IMPORT = "Import";
+
+ public static final String EDIT = "Edit";
+ public static final String DELETE = "Delete";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clients";
+ }
+
+ @FindBy(tagName = "table")
+ private ClientsTable clientsTable;
+
+ public ClientsTable table() {
+ return clientsTable;
+ }
+
+ public class ClientsTable extends DataTable {
+
+ public List searchClients(String searchPattern) {
+ search(searchPattern);
+ return getClientsFromRows();
+ }
+
+ public void createClient() {
+ waitAjaxForBody();
+ clickHeaderLink(CREATE);
+ }
+
+ public void importClient() {
+ waitAjaxForBody();
+ clickHeaderLink(IMPORT);
+ }
+
+ public void clickClient(ClientRepresentation client) {
+ waitAjaxForBody();
+ clickClient(client.getClientId());
+ }
+
+ public void clickClient(String clientId) {
+ waitAjaxForBody();
+ body().findElement(linkText(clientId)).click();
+ }
+
+ public void editClient(String clientId) {
+ waitAjaxForBody();
+ clickRowActionButton(getRowByLinkText(clientId), EDIT);
+ }
+
+ public void deleteClient(String clientId) {
+ waitAjaxForBody();
+ clickRowActionButton(getRowByLinkText(clientId), DELETE);
+ }
+
+ public ClientRepresentation findClient(String clientId) {
+ List clients = searchClients(clientId);
+ if (clients.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == clients.size();
+ return clients.get(0);
+ }
+ }
+
+ public List getClientsFromRows() {
+ List rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ ClientRepresentation client = getClientFromRow(row);
+ if (client != null) {
+ rows.add(client);
+ }
+ }
+ return rows;
+ }
+
+ public ClientRepresentation getClientFromRow(WebElement row) {
+ ClientRepresentation client = null;
+ if (row.isDisplayed()) {
+ client = new ClientRepresentation();
+ List tds = row.findElements(tagName("td"));
+ client.setClientId(tds.get(0).getText());
+ List redirectUris = new ArrayList<>();
+ redirectUris.add(tds.get(2).getText()); // FIXME there can be more than 1 redirect uri
+ client.setRedirectUris(redirectUris);
+ }
+ return client;
+ }
+ }
+
+ public void deleteClient(String clientId) {
+ clientsTable.searchClients(clientId);
+ clientsTable.deleteClient(clientId);
+ }
+
+ public ClientsResource clientsResource() {
+ return realmResource().clients();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java
new file mode 100644
index 00000000000..87568c9ab99
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClient extends AdminConsoleCreate {
+
+ public CreateClient() {
+ setEntity("client");
+ }
+
+ @Page
+ private CreateClientForm form;
+
+ public CreateClientForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
new file mode 100644
index 00000000000..87eadb5dfc6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
@@ -0,0 +1,230 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.auth.page.login.OIDCLogin.OIDC;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.page.Form.getInputValue;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.keycloak.testsuite.util.Timer;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClientForm extends Form {
+
+ @FindBy(id = "clientId")
+ private WebElement clientIdInput;
+
+ @FindBy(id = "name")
+ private WebElement nameInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='enabled']]")
+ private OnOffSwitch enabledSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='consentRequired']]")
+ private OnOffSwitch consentRequiredSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='directGrantsOnly']]")
+ private OnOffSwitch directGrantsOnlySwitch;
+
+ @FindBy(id = "protocol")
+ private Select protocolSelect;
+ @FindBy(id = "protocol")
+ private WebElement protocolSelectElement;
+
+ @FindBy
+ private SAMLClientSettingsForm samlForm;
+
+ public SAMLClientSettingsForm samlForm() {
+ return samlForm;
+ }
+
+ @FindBy(id = "accessType")
+ private Select accessTypeSelect;
+ @FindBy(id = "accessType")
+ private WebElement accessTypeSelectElement;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='serviceAccountsEnabled']]")
+ private OnOffSwitch serviceAccountsEnabledSwitch;
+
+ @FindBy(id = "newRedirectUri")
+ private WebElement newRedirectUriInput;
+ @FindBy(xpath = ".//input[@ng-model='client.redirectUris[i]']")
+ private List redirectUriInputs;
+ @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteRedirectUri')]")
+ private List deleteRedirectUriIcons;
+
+ public void setValues(ClientRepresentation client) {
+ waitAjaxForElement(clientIdInput);
+
+ setClientId(client.getClientId());
+ setName(client.getName());
+ setEnabled(client.isEnabled());
+ setConsentRequired(client.isConsentRequired());
+ setDirectGrantsOnly(client.isDirectGrantsOnly());
+ setProtocol(client.getProtocol());
+ if (OIDC.equals(client.getProtocol())) {
+ setAccessType(client);
+ if (!client.isBearerOnly()) {
+ if (!client.isPublicClient()) {
+ setServiceAccountsEnabled(client.isServiceAccountsEnabled());
+ }
+ setRedirectUris(client.getRedirectUris());
+ }
+ }
+ }
+
+ public ClientRepresentation getValues() {
+ ClientRepresentation values = new ClientRepresentation();
+ values.setClientId(getClientId());
+ values.setName(getName());
+ values.setEnabled(isEnabled());
+ values.setConsentRequired(isConsentRequired());
+ values.setDirectGrantsOnly(isDirectGrantsOnly());
+ values.setProtocol(getProtocol());
+ if (OIDC.equals(values.getProtocol())) {
+ values.setBearerOnly(isBearerOnly());
+ if (!values.isBearerOnly()) {
+ values.setPublicClient(isPublicClient());
+ if (!values.isPublicClient()) {
+ values.setServiceAccountsEnabled(isServiceAccountsEnabled());
+ }
+ values.setRedirectUris(getRedirectUris());
+ }
+ }
+ return values;
+ }
+
+ public String getClientId() {
+ return getInputValue(clientIdInput);
+ }
+
+ public void setClientId(String clientId) {
+ setInputValue(clientIdInput, clientId);
+ }
+
+ public String getName() {
+ return getInputValue(nameInput);
+ }
+
+ public void setName(String name) {
+ setInputValue(nameInput, name);
+ }
+
+ public boolean isEnabled() {
+ return enabledSwitch.isOn();
+ }
+
+ public void setEnabled(boolean enabled) {
+ enabledSwitch.setOn(enabled);
+ }
+
+ public static final String BEARER_ONLY = "bearer-only";
+ public static final String PUBLIC = "public";
+ public static final String CONFIDENTIAL = "confidential";
+
+ public boolean isBearerOnly() {
+ return BEARER_ONLY.equals(
+ accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
+ }
+
+ public boolean isPublicClient() {
+ return PUBLIC.equals(
+ accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
+ }
+
+ public void setBearerOnly(boolean bearerOnly) {
+ accessTypeSelectElement.sendKeys(BEARER_ONLY);
+// accessTypeSelect.selectByVisibleText(BEARER_ONLY);
+ }
+
+ public void setPublicClient(boolean publicClient) {
+ accessTypeSelectElement.sendKeys(PUBLIC);
+// accessTypeSelect.selectByVisibleText(PUBLIC);
+ }
+
+ public void setAccessType(ClientRepresentation client) { // TODO verify
+ setBearerOnly(client.isBearerOnly());
+ setPublicClient(client.isPublicClient());
+ if (!client.isBearerOnly() && !client.isPublicClient()) {
+ accessTypeSelect.selectByVisibleText(CONFIDENTIAL);
+ }
+ }
+
+ public void addRedirectUri(String redirectUri) {
+ newRedirectUriInput.sendKeys(redirectUri);
+ }
+
+ public List getRedirectUris() {
+ List values = new ArrayList<>();
+ for (WebElement input : redirectUriInputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+ public void setRedirectUris(List redirectUris) {
+ Timer.time();
+ while (!deleteRedirectUriIcons.isEmpty()) {
+ deleteRedirectUriIcons.get(0).click();
+ pause(100);
+ }
+ Timer.time("deleteRedirectUris");
+ if (redirectUris != null) {
+ for (String redirectUri : redirectUris) {
+ addRedirectUri(redirectUri);
+ pause(100);
+ }
+ }
+ Timer.time("addRedirectUris");
+ }
+
+ public boolean isConsentRequired() {
+ return consentRequiredSwitch.isOn();
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ consentRequiredSwitch.setOn(consentRequired);
+ }
+
+ public boolean isDirectGrantsOnly() {
+ return directGrantsOnlySwitch.isOn();
+ }
+
+ public void setDirectGrantsOnly(boolean directGrantsOnly) {
+ directGrantsOnlySwitch.setOn(directGrantsOnly);
+ }
+
+ public String getProtocol() {
+ waitAjaxForElement(protocolSelect.getFirstSelectedOption());
+ return protocolSelect.getFirstSelectedOption().getText();
+ }
+
+ public void setProtocol(String protocol) {
+ Timer.time();
+ protocolSelectElement.sendKeys(protocol);
+ Timer.time("clientSettings.setProtocol()");
+ }
+
+ public boolean isServiceAccountsEnabled() {
+ return serviceAccountsEnabledSwitch.isOn();
+ }
+
+ public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+ serviceAccountsEnabledSwitch.setOn(serviceAccountsEnabled);
+ }
+
+ public class SAMLClientSettingsForm extends Form {
+
+ // TODO add SAML client attributes
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java
new file mode 100644
index 00000000000..8a3317ae001
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import static org.keycloak.testsuite.console.page.clients.Client.ID;
+import org.keycloak.testsuite.console.page.roles.CreateRole;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClientRole extends CreateRole {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clients/{" + ID + "}";
+ }
+
+ public void setClientId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getClientId() {
+ return getUriParameter(ID).toString();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java
new file mode 100644
index 00000000000..08e0e0006f0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java
@@ -0,0 +1,104 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+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 mhajas
+ */
+public class AdminEvents extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/admin-events";
+ }
+
+ @FindBy(tagName = "table")
+ private AdminEventsTable table;
+
+ public AdminEventsTable table() {
+ return table;
+ }
+
+ public class AdminEventsTable extends DataTable {
+
+ public void update() {
+ waitAjaxForBody();
+ clickHeaderButton("Update");
+ }
+
+ public void reset() {
+ waitAjaxForBody();
+ clickHeaderButton("Reset");
+ }
+
+ @FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
+ private WebElement filterButton;
+
+ public void filter() {
+ waitAjaxForBody();
+ filterButton.click();
+ }
+
+ @FindBy(tagName = "form")
+ private AdminEventsTableFilterForm filterForm;
+
+ public AdminEventsTableFilterForm filterForm() {
+ return filterForm;
+ }
+
+ public class AdminEventsTableFilterForm extends Form {
+
+ public void addOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_adminEnabledEventOperations']/ul")).click();
+ driver.findElement(By.xpath("//div[@id='select2-drop']//div[text()[contains(.,'" + type + "')]]/..")).click();
+ }
+
+ public void removeOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_adminEnabledEventOperations']//div[text()='" + type + "']/../a")).click();
+ }
+
+ @FindBy(id = "resource")
+ private WebElement resourcePathInput;
+
+ public void setResourcePathInput(String value) {
+ setInputValue(resourcePathInput, value);
+ }
+
+ @FindBy(id = "realm")
+ private WebElement realmInput;
+
+ public void setRealmInput(String value) {
+ setInputValue(realmInput, value);
+ }
+
+ @FindBy(id = "client")
+ private WebElement clientInput;
+
+ public void setClientInput(String value) {
+ setInputValue(clientInput, value);
+ }
+
+ @FindBy(id = "user")
+ private WebElement userInput;
+
+ public void setUserInput(String value) {
+ setInputValue(userInput, value);
+ }
+
+ @FindBy(id = "ipAddress")
+ private WebElement ipAddressInput;
+
+ public void setIpAddressInput(String value) {
+ setInputValue(ipAddressInput, value);
+ }
+ }
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java
new file mode 100644
index 00000000000..686e902b192
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java
@@ -0,0 +1,99 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Config extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/events-settings";
+ }
+
+ @FindBy(xpath = "//form")
+ private ConfigForm form;
+
+ public ConfigForm form() {
+ return form;
+ }
+
+ public class ConfigForm extends Form {
+ @FindBy(id = "s2id_autogen1")
+ private WebElement eventListenersInput;
+
+ @FindBy(xpath = "//div[@id='s2id_autogen1']/..//select")
+ private Select eventListenersSelect;
+
+ public void addEventListener(String listener) {
+ eventListenersInput.click();
+ eventListenersSelect.selectByVisibleText(listener);
+ }
+
+ public void removeEventListener(String listener) {
+ eventListenersInput.findElement(By.xpath("//div[text()='" + listener + "']/../a")).click();
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='enabled']]")
+ private OnOffSwitch SaveEvents;
+
+ public void setSaveEvents(boolean value) {
+ SaveEvents.setOn(value);
+ }
+
+ @FindBy(xpath = "//div[@id='s2id_enabledEventTypes']//input")
+ private WebElement savedTypesInput;
+
+ @FindBy(xpath = "//div[@id='select2-drop']/ul")
+ private WebElement savedTypesOptions;
+
+ public void addSaveType(String type) {
+ savedTypesInput.click();
+ savedTypesOptions.findElement(By.xpath("//div[text()='" + type + "']")).click();
+ }
+
+ public void removeSaveType(String type) {
+ savedTypesInput.findElement(By.xpath("//div[text()='" + type + "']/../a")).click();
+ }
+
+ public void clearLoginEvents() {
+ driver.findElement(By.xpath("//button[@data-ng-click='clearEvents()']")).click();
+ }
+
+ @FindBy(id = "expiration")
+ private WebElement expirationInput;
+
+ @FindBy(name = "expirationUnit")
+ private Select expirationUnitSelect;
+
+ public void setExpiration(String value, String unit) {
+ expirationUnitSelect.selectByVisibleText(unit);
+ Form.setInputValue(expirationInput, value);
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsEnabled']]")
+ private OnOffSwitch saveAdminEvents;
+
+ public void setSaveAdminEvents(boolean value) {
+ saveAdminEvents.setOn(value);
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsDetailsEnabled']]")
+ private OnOffSwitch includeRepresentation;
+
+ public void setIncludeRepresentation(boolean value) {
+ includeRepresentation.setOn(value);
+ }
+
+ public void clearAdminEvents() {
+ driver.findElement(By.xpath("//button[@data-ng-click='clearAdminEvents()']")).click();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java
new file mode 100644
index 00000000000..53c8345ba71
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java
@@ -0,0 +1,35 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Events extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment();
+ }
+
+ @FindBy(linkText = "Login Events")
+ private WebElement loginEventsTab;
+ @FindBy(linkText = "Admin Events")
+ private WebElement adminEventsTab;
+ @FindBy(linkText = "Config")
+ private WebElement configTab;
+
+ public void loginEvents() {
+ loginEventsTab.click();
+ }
+ public void adminEvents() {
+ adminEventsTab.click();
+ }
+ public void config() {
+ configTab.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java
new file mode 100644
index 00000000000..67529511399
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java
@@ -0,0 +1,80 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+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 mhajas
+ */
+public class LoginEvents extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/events";
+ }
+
+ @FindBy(tagName = "table")
+ private LoginEventsTable table;
+
+ public LoginEventsTable table() {
+ return table;
+ }
+
+ public class LoginEventsTable extends DataTable {
+
+ public void update() {
+ waitAjaxForBody();
+ clickHeaderButton("Update");
+ }
+
+ public void reset() {
+ waitAjaxForBody();
+ clickHeaderButton("Reset");
+ }
+
+ @FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
+ private WebElement filterButton;
+
+ public void filter() {
+ waitAjaxForBody();
+ filterButton.click();
+ }
+
+ @FindBy(tagName = "form")
+ private LoginEventsTableFilterForm filterForm;
+
+ public LoginEventsTableFilterForm filterForm() {
+ return filterForm;
+ }
+
+ public class LoginEventsTableFilterForm extends Form {
+
+ public void addEventType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_eventTypes']/ul")).click();
+ driver.findElement(By.xpath("//div[@id='select2-drop']//div[text()='" + type + "']/..")).click();
+ }
+
+ public void removeOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_eventTypes']//div[text()='" + type + "']/../a")).click();
+ }
+
+ @FindBy(id = "client")
+ private WebElement clientInput;
+
+ public void setClientInput(String value) {
+ setInputValue(clientInput, value);
+ }
+
+ @FindBy(id = "user")
+ private WebElement userInput;
+
+ public void setUserInput(String value) {
+ setInputValue(userInput, value);
+ }
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java
new file mode 100644
index 00000000000..4dc47f99da4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java
@@ -0,0 +1,20 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateLdapUserProvider extends AdminConsoleCreate {
+
+ public CreateLdapUserProvider() {
+ setEntity("user-federation");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/providers/ldap";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
new file mode 100644
index 00000000000..a9b88829e41
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
@@ -0,0 +1,130 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+
+/**
+ * Created by fkiss.
+ */
+public class LdapUserProviderForm extends Form {
+
+ @FindBy(id = "consoleDisplayName")
+ private WebElement consoleDisplayNameInput;
+
+ @FindBy(id = "priority")
+ private WebElement priorityInput;
+
+ @FindBy(id = "usernameLDAPAttribute")
+ private WebElement usernameLDAPAttributeInput;
+
+ @FindBy(id = "userObjectClasses")
+ private WebElement userObjectClassesInput;
+
+ @FindBy(id = "ldapConnectionUrl")
+ private WebElement ldapConnectionUrlInput;
+
+ @FindBy(id = "ldapBaseDn")
+ private WebElement ldapBaseDnInput;
+
+ @FindBy(id = "ldapUsersDn")
+ private WebElement ldapUserDnInput;
+
+ @FindBy(id = "ldapBindDn")
+ private WebElement ldapBindDnInput;
+
+ @FindBy(id = "ldapBindCredential")
+ private WebElement ldapBindCredentialInput;
+
+ @FindBy(id = "kerberosRealm")
+ private WebElement kerberosRealmInput;
+
+ @FindBy(id = "serverPrincipal")
+ private WebElement serverPrincipalInput;
+
+ @FindBy(id = "keyTab")
+ private WebElement keyTabInput;
+
+ @FindBy(id = "batchSizeForSync")
+ private WebElement batchSizeForSyncInput;
+
+ @FindBy(id = "fullSyncPeriod")
+ private WebElement fullSyncPeriodInput;
+
+ @FindBy(id = "changedSyncPeriod")
+ private WebElement changedSyncPeriodInput;
+
+ @FindBy(id = "editMode")
+ private Select editModeSelect;
+
+ @FindBy(id = "vendor")
+ private Select vendorSelect;
+
+ @FindByJQuery("a:contains('Test connection')")
+ private WebElement testConnectionButton;
+
+ @FindByJQuery("a:contains('Test authentication')")
+ private WebElement testAuthenticationButton;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch syncRegistrations;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch connectionPooling;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(2)")
+ private OnOffSwitch pagination;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(3)")
+ private OnOffSwitch allowKerberosAuth;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(4)")
+ private OnOffSwitch debug;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(5)")
+ private OnOffSwitch useKerberosForPwdAuth;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(6)")
+ private OnOffSwitch periodicFullSync;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(7)")
+ private OnOffSwitch periodicChangedUsersSync;
+
+ @FindByJQuery("button:contains('Save')")
+ private WebElement saveButton;
+
+ public void selectEditMode(String mode){
+ waitGuiForElement(By.id("editMode"));
+ editModeSelect.selectByVisibleText(mode);
+ }
+
+ public void selectVendor(String vendor){
+ waitGuiForElement(By.id("editMode"));
+ vendorSelect.selectByVisibleText(vendor);
+ }
+
+ public void configureLdap(String displayName, String editMode, String vendor, String connectionUrl, String userDN, String ldapBindDn, String ldapBindCredential){
+ consoleDisplayNameInput.sendKeys(displayName);
+ editModeSelect.selectByVisibleText(editMode);
+ selectVendor(vendor);
+ ldapConnectionUrlInput.sendKeys(connectionUrl);
+ ldapUserDnInput.sendKeys(userDN);
+ ldapBindDnInput.sendKeys(ldapBindDn);
+ ldapBindCredentialInput.sendKeys(ldapBindCredential);
+ saveButton.click();
+ }
+
+ public void testConnection(){
+ testConnectionButton.click();
+ }
+
+ public void testAuthentication(){
+ testAuthenticationButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
new file mode 100644
index 00000000000..b209b818e5a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.By;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+
+/**
+ * Created by fkiss.
+ */
+public class UserFederation extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/user-federation";
+ }
+
+ @FindByJQuery("select[ng-model*='selectedProvider']")
+ private Select addProviderSelect;
+
+ public void addProvider(String provider) {
+ waitGuiForElement(By.cssSelector("select[ng-model*='selectedProvider']"));
+ addProviderSelect.selectByVisibleText(provider);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java
new file mode 100644
index 00000000000..d6d517b6a28
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java
@@ -0,0 +1,34 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.List;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Breadcrumb {
+
+ public static final String BREADCRUMB_XPATH = "//ol[@class='breadcrumb']";
+
+ @FindBy(xpath = "./li[not(contains(@class,'ng-hide'))]/a")
+ private List items;
+
+ public int size() {
+ return items.size();
+ }
+
+ public WebElement getItem(int index) {
+ return items.get(index);
+ }
+
+ public WebElement getItemFromEnd(int index) {
+ return items.get(size() - index - 1);
+ }
+
+ public void clickItemOneLevelUp() {
+ getItemFromEnd(0).click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java
new file mode 100644
index 00000000000..d049d2eb06d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java
@@ -0,0 +1,76 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.List;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.By;
+import static org.openqa.selenium.By.xpath;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class DataTable {
+
+ @FindBy(css = "input[class*='search']")
+ private WebElement searchInput;
+ @FindBy(css = "div[class='input-group-addon'] i")
+ private WebElement searchButton;
+
+ @FindBy(tagName = "thead")
+ private WebElement header;
+ @FindBy(css = "tbody")
+ private WebElement body;
+ @FindBy(css = "tbody tr.ng-scope")
+ private List rows;
+
+ @FindBy
+ private WebElement infoRow;
+
+ public void search(String pattern) {
+ waitAjaxForBody();
+ searchInput.sendKeys(pattern);
+ searchButton.click();
+ }
+
+ public void clickHeaderButton(String buttonText) {
+ waitAjaxForBody();
+ header.findElement(By.xpath(".//button[text()='" + buttonText + "']")).click();
+ }
+
+ public void clickHeaderLink(String linkText) {
+ waitAjaxForBody();
+ header.findElement(By.linkText(linkText)).click();
+ }
+
+ public WebElement body() {
+ return body;
+ }
+
+ public void waitAjaxForBody() {
+ waitAjaxForElement(body);
+ }
+
+ public List rows() {
+ waitAjaxForBody();
+ pause(250);
+ return rows;
+ }
+
+ public WebElement getRowByLinkText(String text) {
+ WebElement row = body.findElement(By.xpath(".//tr[./td/a[text()='" + text + "']]"));
+ waitAjaxForElement(row);
+ return row;
+ }
+
+ public void clickRowByLinkText(String text) {
+ body.findElement(By.xpath(".//tr/td/a[text()='" + text + "']")).click();
+ }
+
+ public void clickRowActionButton(WebElement row, String buttonText) {
+ row.findElement(xpath(".//button[text()='" + buttonText + "']")).click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/FlashMessage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/FlashMessage.java
similarity index 59%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/FlashMessage.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/FlashMessage.java
index e6eadadd176..b117b5c3c9f 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/FlashMessage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/FlashMessage.java
@@ -15,13 +15,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package org.keycloak.testsuite.admin.fragment;
+package org.keycloak.testsuite.console.page.fragment;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import org.jboss.arquillian.graphene.fragment.Root;
-import static org.keycloak.testsuite.admin.util.SeleniumUtils.waitGuiForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
import org.openqa.selenium.WebElement;
/**
@@ -34,34 +33,34 @@ public class FlashMessage {
private WebElement root;
public boolean isSuccess() {
- waitGui().until("Flash message should be success")
- .element(root)
- .attribute("class")
- .contains("success");
- return root.getAttribute("class").contains("success");
+ waitGui().until("Flash message should be success")
+ .element(root)
+ .attribute("class")
+ .contains("success");
+ return root.getAttribute("class").contains("success");
}
public boolean isError() {
- waitGui().until("Flash message should be error")
- .element(root)
- .attribute("class")
- .contains("error");
+ waitGui().until("Flash message should be error")
+ .element(root)
+ .attribute("class")
+ .contains("error");
return root.getAttribute("class").contains("error");
}
public boolean isDanger() {
- waitGui().until("Flash message should be danger")
- .element(root)
- .attribute("class")
- .contains("danger");
- return root.getAttribute("class").contains("danger");
- }
+ waitGui().until("Flash message should be danger")
+ .element(root)
+ .attribute("class")
+ .contains("danger");
+ return root.getAttribute("class").contains("danger");
+ }
public String getText() {
return root.getText();
}
public void waitUntilPresent() {
- waitGuiForElement(root, "Flash message should be visible.");
+ waitGuiForElementPresent(root, "Flash message should be visible.");
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java
new file mode 100644
index 00000000000..902287ef3d3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.ArrayList;
+import java.util.List;
+import static org.keycloak.testsuite.page.Form.getInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class InputList {
+
+ @FindBy(xpath=".//input[@ng-model='client.redirectUris[i]']")
+ private List inputs;
+
+ public List getValues() {
+ List values = new ArrayList<>();
+ for (WebElement input: inputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/MenuPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Menu.java
similarity index 52%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/MenuPage.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Menu.java
index fda78b745f6..577259caf7e 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/MenuPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Menu.java
@@ -15,11 +15,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package org.keycloak.testsuite.admin.fragment;
+package org.keycloak.testsuite.console.page.fragment;
import java.util.List;
-import static org.keycloak.testsuite.admin.util.SeleniumUtils.waitGuiForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
@@ -29,42 +28,49 @@ import org.openqa.selenium.support.FindBy;
*
* @author Petr Mensik
*/
-public class MenuPage {
+public class Menu {
+
+ private static final String MENU_LOCATOR = "ul[class='dropdown-menu']";
- private static final String MENU_LOCATOR = "ul[class='dropdown-menu']";
-
@FindBy(css = MENU_LOCATOR)
- private List menuList;
-
+ private List menuList;
+
@FindBy(css = ".dropdown-toggle")
private List toggle;
public void logOut() {
- clickOnMenuElement(Menu.USER, "Sign Out");
- }
-
- public void goToAccountManagement() {
- clickOnMenuElement(Menu.USER, "Manage Account");
- }
-
- public void switchRealm(String realmName) {
- clickOnMenuElement(Menu.REALM, realmName);
- }
-
- public String getCurrentRealm() {
- waitGuiForElement(By.cssSelector(MENU_LOCATOR));
- return toggle.get(1).getText();
- }
+ clickOnMenuElement(MenuType.USER, "Sign Out");
+ }
- private void clickOnMenuElement(Menu menuType, String linkText) {
- int menuOrder = 0;
- switch(menuType) {
- case REALM: menuOrder = 1; break;
- case USER: menuOrder = 0; break;
- }
- waitGuiForElement(By.cssSelector(MENU_LOCATOR));
- if (!menuList.get(menuOrder).isDisplayed())
- toggle.get(menuOrder).click();
+ public void goToAccountManagement() {
+ clickOnMenuElement(MenuType.USER, "Manage Account");
+ }
+
+ public void switchRealm(String realmName) {
+ if (!realmName.equals(getCurrentRealm())) {
+ clickOnMenuElement(MenuType.REALM, realmName);
+ }
+ }
+
+ public String getCurrentRealm() {
+ waitGuiForElement(By.cssSelector(MENU_LOCATOR));
+ return toggle.get(1).getText();
+ }
+
+ private void clickOnMenuElement(MenuType menuType, String linkText) {
+ int menuOrder = 0;
+ switch (menuType) {
+ case REALM:
+ menuOrder = 1;
+ break;
+ case USER:
+ menuOrder = 0;
+ break;
+ }
+ waitGuiForElement(By.cssSelector(MENU_LOCATOR));
+ if (!menuList.get(menuOrder).isDisplayed()) {
+ toggle.get(menuOrder).click();
+ }
for (WebElement item : menuList.get(menuOrder).findElements(By.cssSelector(MENU_LOCATOR + " a"))) {
if (item.getText().contains(linkText)) {
item.click();
@@ -73,9 +79,10 @@ public class MenuPage {
}
throw new RuntimeException("Could not find menu item containing \"" + linkText + "\"");
}
-
- private enum Menu {
- USER, REALM
- }
+
+ private enum MenuType {
+
+ USER, REALM
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
new file mode 100644
index 00000000000..9bb95dd24a4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
@@ -0,0 +1,36 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ModalDialog {
+
+ @FindBy(xpath = ".//button[text()='Cancel']")
+ private WebElement cancelButton;
+ @FindBy(xpath = ".//button[text()='Delete']")
+ private WebElement deleteButton;
+
+ @FindBy(xpath = ".//button[@ng-click='ok()']")
+ private WebElement okButton;
+
+ public void ok() {
+ waitAjaxForElement(okButton);
+ okButton.click();
+ }
+
+ public void confirmDeletion() {
+ waitAjaxForElement(deleteButton);
+ deleteButton.click();
+ }
+
+ public void cancel() {
+ waitAjaxForElement(cancelButton);
+ cancelButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/OnOffSwitch.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
similarity index 50%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/OnOffSwitch.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
index e07be813804..6319c4c8207 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/OnOffSwitch.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
@@ -15,11 +15,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package org.keycloak.testsuite.admin.fragment;
+package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.jboss.arquillian.test.api.ArquillianResource;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
@@ -28,37 +28,46 @@ import org.openqa.selenium.interactions.Actions;
*
* @author Petr Mensik
*/
-
public class OnOffSwitch {
- @Root
- private WebElement root;
-
- @ArquillianResource
- private Actions actions;
-
- public boolean isEnabled() {
- return root.findElement(By.tagName("input")).isSelected();
- }
-
- private void click() {
- actions.moveToElement(root.findElements(By.tagName("span")).get(0))
- .click().build().perform();
- }
-
- public void toggle() {
- click();
- }
-
- public void enable() {
- if(!isEnabled()) {
- click();
- }
- }
-
- public void disable() {
- if(isEnabled()) {
- click();
- }
- }
+ @Root
+ private WebElement root;
+
+ @ArquillianResource
+ private Actions actions;
+
+ public boolean isOn() {
+ waitAjaxForElement(root);
+ return root.findElement(By.tagName("input")).isSelected();
+ }
+
+ private void click() {
+ waitAjaxForElement(root);
+ actions.moveToElement(root.findElements(By.tagName("span")).get(0))
+ .click().build().perform();
+ }
+
+ public void toggle() {
+ click();
+ }
+
+ public void on() {
+ if (!isOn()) {
+ click();
+ }
+ }
+
+ public void off() {
+ if (isOn()) {
+ click();
+ }
+ }
+
+ public void setOn(boolean on) {
+ if ((on && !isOn())
+ || (!on && isOn())) {
+ click(); // click if requested value differs from the actual value
+ }
+ }
+
}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/PickList.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/PickList.java
similarity index 96%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/PickList.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/PickList.java
index d3152938123..e8d433a605a 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/PickList.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/PickList.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.fragment;
+package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java
new file mode 100644
index 00000000000..5e2ea52ffa6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java
@@ -0,0 +1,11 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RealmSelector {
+
+ // TODO
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/idp/IdentityProviderSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/idp/IdentityProviderSettings.java
new file mode 100644
index 00000000000..275ddf493b7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/idp/IdentityProviderSettings.java
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.idp;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.admin.client.resource.IdentityProvidersResource;
+import org.keycloak.testsuite.model.Provider;
+import org.keycloak.testsuite.model.SocialProvider;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import static org.openqa.selenium.By.tagName;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class IdentityProviderSettings extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "identity-provider-settings";
+ }
+
+ @FindBy(tagName = "select")
+ private Select newProviderSelect;
+
+ @FindByJQuery("input[class*='form-control']:eq(1)")
+ private WebElement providerKey;
+
+ @FindByJQuery("input[class*='form-control']:eq(2)")
+ private WebElement providerSecret;
+
+ @FindBy(tagName = "tbody")
+ private WebElement providersTable;
+
+ public void addNewProvider(Provider provider) {
+ newProviderSelect.selectByVisibleText(provider.providerName.getName());
+ providerKey.sendKeys(provider.key);
+ providerSecret.sendKeys(provider.secret);
+ primaryButton.click();
+ }
+
+ public void editProvider(SocialProvider oldProvider, Provider newProvider) {
+ Provider p = find(oldProvider);
+ if (p == null) {
+ throw new AssertionError("Provider should have been found");
+ }
+ log.info(p.providerName);
+ }
+
+ public Provider find(SocialProvider provider) {
+ List list = getAllRows();
+ for (Provider p : list) {
+ if (p.providerName == provider) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ private List getAllRows() {
+ List rows = new ArrayList<>();
+ for (WebElement rowElement : providersTable.findElements(tagName("tr"))) {
+ Provider provider = new Provider();
+ List tds = rowElement.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ provider.providerName = SocialProvider.valueOf(tds.get(0).getText());
+ provider.key = tds.get(1).getText();
+ provider.secret = tds.get(2).getText();
+ rows.add(provider);
+ }
+ }
+ return rows;
+ }
+
+ public IdentityProvidersResource identityProviders() {
+ return realmResource().identityProviders();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java
new file mode 100644
index 00000000000..e903f0145fc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class CacheSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/cache-settings";
+ }
+
+ @Page
+ private CacheSettingsForm form;
+
+ public CacheSettingsForm form() {
+ return form;
+ }
+
+ public class CacheSettingsForm extends Form {
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch realmCacheEnabled;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch userCacheEnabled;
+
+ public void setRealmCacheEnabled(boolean value) {
+ realmCacheEnabled.setOn(value);
+ }
+
+ public void setUserCacheEnabled(boolean value) {
+ userCacheEnabled.setOn(value);
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/CreateRealm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CreateRealm.java
similarity index 96%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/CreateRealm.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CreateRealm.java
index f8dda90a231..63d3e159718 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/CreateRealm.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CreateRealm.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.fragment;
+package org.keycloak.testsuite.console.page.realm;
import org.jboss.arquillian.drone.api.annotation.Drone;
import static org.openqa.selenium.By.id;
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java
new file mode 100644
index 00000000000..f75117b54a6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java
@@ -0,0 +1,70 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * Created by mhajas on 8/25/15.
+ */
+public class EmailSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/smtp-settings";
+ }
+
+ @Page
+ private EmailSettingsForm form;
+
+ public EmailSettingsForm form() {
+ return form;
+ }
+
+ public class EmailSettingsForm extends Form {
+ @FindBy(id = "smtpHost")
+ private WebElement hostInput;
+
+ @FindBy(id = "smtpPort")
+ private WebElement portInput;
+
+ @FindBy(id = "smtpFrom")
+ private WebElement fromInput;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch enableSSL;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch enableStartTLS;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(2)")
+ private OnOffSwitch enableAuthentication;
+
+ public void setEnableSSL(boolean sslEnabled) {
+ enableSSL.setOn(sslEnabled);
+ }
+
+ public void setEnableStartTLS(boolean startTLS) {
+ enableSSL.setOn(startTLS);
+ }
+
+ public void setEnableAuthentication(boolean authentication) {
+ enableSSL.setOn(authentication);
+ }
+
+ public void setHostInput(String value) {
+ setInputValue(hostInput, value);
+ }
+
+ public void setPortInput(String value) {
+ setInputValue(portInput, value);
+ }
+
+ public void setFromInput(String value) {
+ setInputValue(fromInput, value);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/GeneralSettingsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/GeneralSettings.java
similarity index 90%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/GeneralSettingsPage.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/GeneralSettings.java
index 5a4d9bb507f..a2a32de2a61 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/GeneralSettingsPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/GeneralSettings.java
@@ -16,10 +16,9 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.page.settings;
+package org.keycloak.testsuite.console.page.realm;
-import org.keycloak.testsuite.admin.page.AbstractPage;
-import org.keycloak.testsuite.admin.model.Theme;
+import org.keycloak.testsuite.model.Theme;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
@@ -29,7 +28,7 @@ import org.openqa.selenium.support.ui.Select;
* @author Petr Mensik
*/
-public class GeneralSettingsPage extends AbstractPage {
+public class GeneralSettings extends RealmSettings {
@FindBy(id = "name")
private WebElement realmName;
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java
new file mode 100644
index 00000000000..902acf07e21
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.realm;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class KeysSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/cache-settings";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java
new file mode 100644
index 00000000000..87c93f0c424
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class LoginSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/login-settings";
+ }
+
+ @Page
+ private LoginSettingsForm form;
+
+ public LoginSettingsForm form() {
+ return form;
+ }
+
+ public class LoginSettingsForm extends Form {
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch registrationAllowed;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='registrationEmailAsUsername']]")
+ private OnOffSwitch emailAsUsernameOnOffSwitch;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='resetPasswordAllowed']]")
+ private OnOffSwitch resetPasswordAllowed;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(2)")
+ private OnOffSwitch rememberMeEnabled;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='verifyEmail']]")
+ private OnOffSwitch verifyEmailEnabled;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(4)")
+ private OnOffSwitch directGrantApiEnabled;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(5)")
+ private OnOffSwitch requireSsl;
+
+ public boolean isRegistrationAllowed() {
+ return registrationAllowed.isOn();
+ }
+
+ public void setRegistrationAllowed(boolean allowed) {
+ registrationAllowed.setOn(allowed);
+ }
+
+ public void setEmailAsUsername(boolean emailAsUsername) {
+ emailAsUsernameOnOffSwitch.setOn(emailAsUsername);
+ }
+
+ public void setResetPasswordAllowed(boolean allowed) {
+ resetPasswordAllowed.setOn(allowed);
+ }
+
+ public void setVerifyEmailAllowed(boolean allowed) {
+ verifyEmailEnabled.setOn(allowed);
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java
new file mode 100644
index 00000000000..83b1a67526d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java
@@ -0,0 +1,72 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RealmSettings extends AdminConsoleRealm {
+
+ @FindBy(xpath = "//div[@data-ng-controller='RealmTabCtrl']/ul")
+ private RealmTabs realmTabs;
+
+ public RealmTabs tabs() {
+ return realmTabs;
+ }
+
+ public class RealmTabs {
+
+ @FindBy(linkText = "General")
+ private WebElement generalSettingsTab;
+ @FindBy(linkText = "Login")
+ private WebElement loginSettingsTab;
+ @FindBy(linkText = "Keys")
+ private WebElement keysSettingsTab;
+ @FindBy(linkText = "Email")
+ private WebElement emailSettingsTab;
+ @FindBy(linkText = "Themes")
+ private WebElement themeSettingsTab;
+ @FindBy(linkText = "Cache")
+ private WebElement cacheSettingsTab;
+ @FindBy(linkText = "Tokens")
+ private WebElement tokenSettingsTab;
+ @FindBy(linkText = "Security Defenses")
+ private WebElement defenseTab;
+
+ public void general() {
+ generalSettingsTab.click();
+ }
+
+ public void login() {
+ loginSettingsTab.click();
+ }
+
+ public void keys() {
+ keysSettingsTab.click();
+ }
+
+ public void email() {
+ emailSettingsTab.click();
+ }
+
+ public void themes() {
+ themeSettingsTab.click();
+ }
+
+ public void cache() {
+ cacheSettingsTab.click();
+ }
+
+ public void tokens() {
+ tokenSettingsTab.click();
+ }
+
+ public void securityDefenses() {
+ defenseTab.click();
+ }
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java
new file mode 100644
index 00000000000..00bd0b84146
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java
@@ -0,0 +1,197 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.page.Form.setInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author Filip Kiss
+ * @author mhajas
+ */
+public class SecurityDefenses extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/defense"; // NOTE: page doesn't exist, only subpages
+ }
+
+ public class Headers extends SecurityDefenses {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/headers";
+ }
+
+ @Page
+ private HeadersForm form;
+
+ public HeadersForm form() {
+ return form;
+ }
+
+ public class HeadersForm extends Form {
+
+ @FindBy(id = "xFrameOptions")
+ private WebElement xFrameOptions;
+
+ public void setXFrameOptions(String value) {
+ setInputValue(xFrameOptions, value);
+ }
+
+ @FindBy(id = "contentSecurityPolicy")
+ private WebElement contentSecurityPolicy;
+
+ public void setContentSecurityPolicy(String value) {
+ setInputValue(contentSecurityPolicy, value);
+ }
+ }
+ }
+
+ public enum TimeSelectValues {
+
+ SECONDS("Seconds"), MINUTES("Minutes"), HOURS("Hours"), DAYS("Days");
+
+ private String name;
+
+ private TimeSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public class BruteForceDetection extends SecurityDefenses {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/brute-force";
+ }
+
+ @Page
+ private BruteForceDetectionForm form;
+
+ public BruteForceDetectionForm form() {
+ return form;
+ }
+
+ public class BruteForceDetectionForm extends Form {
+
+ @FindByJQuery("div[class='onoffswitch']")
+ private OnOffSwitch protectionEnabled;
+
+ public void setProtectionEnabled(boolean protectionEnabled) {
+ this.protectionEnabled.setOn(protectionEnabled);
+ }
+
+ @FindBy(id = "failureFactor")
+ private WebElement maxLoginFailures;
+
+ public void setMaxLoginFailures(String value) {
+ setInputValue(maxLoginFailures, value);
+ }
+
+ @FindBy(id = "waitIncrement")
+ private WebElement waitIncrementInput;
+
+ @FindBy(name = "waitIncrementUnit")
+ private Select waitIncrementSelect;
+
+ public void setWaitIncrementInput(String value) {
+ setInputValue(waitIncrementInput, value);
+ }
+
+ public void setWaitIncrementSelect(TimeSelectValues value) {
+ waitIncrementSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "quickLoginCheckMilliSeconds")
+ private WebElement quickLoginCheckInput;
+
+ public void setQuickLoginCheckInput(String value) {
+ setInputValue(quickLoginCheckInput, value);
+ }
+
+ @FindBy(id = "minimumQuickLoginWait")
+ private WebElement minQuickLoginWaitInput;
+
+ @FindBy(name = "minimumQuickLoginWaitUnit")
+ private Select minQuickLoginWaitSelect;
+
+ public void setMinQuickLoginWaitInput(String value) {
+ setInputValue(minQuickLoginWaitInput, value);
+ }
+
+ public void setMinQuickLoginWaitSelect(TimeSelectValues value) {
+ minQuickLoginWaitSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "maxFailureWait")
+ private WebElement maxWaitInput;
+
+ @FindBy(name = "maxFailureWaitUnit")
+ private Select maxWaitSelect;
+
+ public void setMaxWaitInput(String value) {
+ setInputValue(maxWaitInput, value);
+ }
+
+ public void setMaxWaitSelect(TimeSelectValues value) {
+ maxWaitSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "maxDeltaTime")
+ private WebElement failureResetTimeInput;
+
+ @FindBy(name = "maxDeltaTimeUnit")
+ private Select failureResetTimeSelect;
+
+ public void setFailureResetTimeInput(String value) {
+ setInputValue(failureResetTimeInput, value);
+ }
+
+ public void setFailureResetTimeSelect(TimeSelectValues value) {
+ failureResetTimeSelect.selectByVisibleText(value.getName());
+ }
+
+ }
+
+ }
+
+ @FindByJQuery("a:contains('Brute Force Detection')")
+ private WebElement bruteForceDetectionTab;
+
+ public void goToBruteForceDetection() {
+ bruteForceDetectionTab.click();
+ }
+
+ @FindByJQuery("a:contains('Headers')")
+ private WebElement headersTab;
+
+ public void goToHeaders() {
+ headersTab.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/ThemesSettingsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/ThemeSettings.java
similarity index 61%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/ThemesSettingsPage.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/ThemeSettings.java
index eca517bc8f0..3a4acde5b80 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/ThemesSettingsPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/ThemeSettings.java
@@ -15,23 +15,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.console.page.realm;
-package org.keycloak.testsuite.admin.page.settings;
-
-import org.keycloak.testsuite.admin.page.AbstractPage;
-import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
-import static org.keycloak.testsuite.admin.util.SeleniumUtils.waitGuiForElement;
-import static org.keycloak.testsuite.admin.util.SeleniumUtils.waitGuiForElementNotPresent;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
import org.openqa.selenium.By;
/**
*
* @author Filip Kiss
*/
-public class ThemesSettingsPage extends AbstractPage {
+public class ThemeSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/theme-settings";
+ }
@FindBy(css = "#loginTheme")
private Select loginThemeSelect;
@@ -45,34 +46,23 @@ public class ThemesSettingsPage extends AbstractPage {
@FindBy(css = "#emailTheme")
private Select emailThemeSelect;
- @FindBy(css = "link[href*='login/keycloak/css/login.css']")
- private WebElement keycloakTheme;
-
- public void changeLoginTheme(String themeName){
- waitGuiForElement(By.id("loginTheme"));
+ public void changeLoginTheme(String themeName) {
+ waitGuiForElement(By.id("loginTheme"));
loginThemeSelect.selectByVisibleText(themeName);
}
- public void changeAccountTheme(String themeName){
+ public void changeAccountTheme(String themeName) {
accountThemeSelect.selectByVisibleText(themeName);
}
- public void changeAdminConsoleTheme(String themeName){
+ public void changeAdminConsoleTheme(String themeName) {
adminConsoleThemeSelect.selectByVisibleText(themeName);
}
- public void changeEmailTheme(String themeName){
+ public void changeEmailTheme(String themeName) {
emailThemeSelect.selectByVisibleText(themeName);
}
- public void verifyBaseTheme(){
- waitGuiForElementNotPresent(keycloakTheme);
- }
-
- public void verifyKeycloakTheme(){
- waitGuiForElement(keycloakTheme);
- }
-
public void saveTheme() {
primaryButton.click();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/TokenSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/TokenSettings.java
new file mode 100644
index 00000000000..fc9ad0a4f93
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/TokenSettings.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.realm;
+
+import java.util.concurrent.TimeUnit;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static java.lang.String.valueOf;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import static org.apache.commons.lang3.text.WordUtils.capitalize;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.page.Form;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class TokenSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/token-settings";
+ }
+
+ @Page
+ private TokenSettingsForm form;
+
+ public TokenSettingsForm form() {
+ return form;
+ }
+
+ public class TokenSettingsForm extends Form {
+
+ @FindBy(id = "ssoSessionIdleTimeout")
+ private WebElement sessionTimeout;
+
+ @FindBy(name = "ssoSessionIdleTimeoutUnit")
+ private Select sessionTimeoutUnit;
+
+ @FindBy(id = "ssoSessionMaxLifespan")
+ private WebElement sessionLifespanTimeout;
+
+ @FindBy(name = "ssoSessionMaxLifespanUnit")
+ private Select sessionLifespanTimeoutUnit;
+
+ public void setSessionTimeout(int timeout, TimeUnit unit) {
+ setTimeout(sessionTimeoutUnit, sessionTimeout, timeout, unit);
+ }
+
+ public void setSessionTimeoutLifespan(int time, TimeUnit unit) {
+ setTimeout(sessionLifespanTimeoutUnit, sessionLifespanTimeout, time, unit);
+ }
+
+ private void setTimeout(Select timeoutElement, WebElement unitElement,
+ int timeout, TimeUnit unit) {
+ waitGuiForElement(sessionTimeout);
+ timeoutElement.selectByValue(capitalize(unit.name().toLowerCase()));
+ unitElement.clear();
+ unitElement.sendKeys(valueOf(timeout));
+ }
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java
new file mode 100644
index 00000000000..d1f218b8d28
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateRole extends AdminConsoleCreate {
+
+ public CreateRole() {
+ setEntity("role");
+ }
+
+ @Page
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/PasswordPolicy.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/DefaultRoles.java
similarity index 65%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/PasswordPolicy.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/DefaultRoles.java
index ef3d3de0a84..1611129f3a5 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/PasswordPolicy.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/DefaultRoles.java
@@ -15,25 +15,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.console.page.roles;
-package org.keycloak.testsuite.admin.model;
+import org.jboss.arquillian.graphene.page.Page;
/**
*
* @author Petr Mensik
*/
-public enum PasswordPolicy {
+public class DefaultRoles extends Roles {
- HASH_ITERATIONS("Hash Iterations"), LENGTH("Length"), DIGITS("Digits"), LOWER_CASE("Lower Case"),
- UPPER_CASE("Upper Case"), SPECIAL_CHARS("Special Chars");
-
- private String name;
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/default-roles";
+ }
- private PasswordPolicy(String name) {
- this.name = name;
- }
+ @Page
+ private RoleCompositeRoles form;
+
+ public RoleCompositeRoles form() {
+ return form;
+ }
- public String getName() {
- return name;
- }
}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/URL.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RealmRoles.java
similarity index 64%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/URL.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RealmRoles.java
index 2824401fa5c..b198a4adbd6 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/URL.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RealmRoles.java
@@ -15,19 +15,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.console.page.roles;
-package org.keycloak.testsuite.admin.util;
+import org.openqa.selenium.support.FindBy;
/**
*
* @author Petr Mensik
+ * @author tkyjovsk
*/
-public class URL {
-
- public static final String BASE_URL = "http://localhost:8080/auth";
- public static final String ADMIN_URL = BASE_URL + "/admin";
- public static final String ACCOUNT_URL = BASE_URL + "/realms/master/account";
+public class RealmRoles extends Roles {
- public static final String SETTINGS_SOCIAL = ADMIN_URL + "/#/realms/%s/social-settings";
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/roles";
+ }
+
+ @FindBy(css = "table[class*='table']")
+ private RolesTable table;
+
+ public RolesTable table() {
+ return table;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java
new file mode 100644
index 00000000000..2bde155e9d0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java
@@ -0,0 +1,33 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Role extends RealmRoles {
+
+ public static final String ROLE_ID = "roleId";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ROLE_ID + "}";
+ }
+
+ public void setRoleId(String id) {
+ setUriParameter(ROLE_ID, id);
+ }
+
+ public String getRoleId() {
+ return getUriParameter(ROLE_ID).toString();
+ }
+
+ @Page
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
new file mode 100644
index 00000000000..876b45b441b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
@@ -0,0 +1,182 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.keycloak.representations.idm.RoleRepresentation.Composites;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author fkiss
+ * @author tkyjovsk
+ */
+public class RoleCompositeRoles extends Form {
+
+ @FindBy(id = "available")
+ protected Select availableRealmRolesSelect;
+ @FindBy(id = "assigned")
+ protected Select assignedRealmRolesSelect;
+
+ @FindBy(id = "clients")
+ protected Select clientSelect;
+ @FindBy(id = "available-client")
+ protected Select availableClientRolesSelect;
+ @FindBy(id = "assigned-client")
+ protected Select assignedClientRolesSelect;
+
+ @FindBy(css = "button[ng-click*='addRealm']")
+ protected WebElement addSelectedRealmRolesButton;
+ @FindBy(css = "button[ng-click*='addClient']")
+ protected WebElement addSelectedClientRolesButton;
+ @FindBy(css = "button[ng-click*='deleteRealm']")
+ protected WebElement removeSelectedRealmRolesButton;
+ @FindBy(css = "button[ng-click*='deleteClient']")
+ protected WebElement removeSelectedClientRolesButton;
+
+ public Composites getComposites() {
+ Composites composites = new Composites();
+ // realm roles
+ composites.setRealm(getSelectValues(assignedRealmRolesSelect));
+ // client roles
+ Map> clientRoles = new HashMap<>();
+ for (String client : getSelectValues(clientSelect)) {
+ clientSelect.selectByVisibleText(client);
+ clientRoles.put(client, new ArrayList(getSelectValues(assignedClientRolesSelect)));
+ }
+ composites.setClient(clientRoles);
+ return composites;
+ }
+
+ public void setComposites(Composites composites) {
+ if (composites != null) {
+ setRealmRoles(composites.getRealm());
+ for (String client : composites.getClient().keySet()) {
+ clientSelect.selectByVisibleText(client);
+ setClientRoles(composites.getClient().get(client));
+ }
+ }
+ }
+
+ private void setRealmRoles(Collection roles) {
+ removeRedundantRoles(assignedRealmRolesSelect, removeSelectedRealmRolesButton, roles);
+ addMissingRoles(availableRealmRolesSelect, addSelectedRealmRolesButton, roles);
+ }
+
+ private void setClientRoles(Collection roles) {
+ removeRedundantRoles(assignedClientRolesSelect, removeSelectedClientRolesButton, roles);
+ addMissingRoles(availableClientRolesSelect, addSelectedClientRolesButton, roles);
+ }
+
+ private void removeRedundantRoles(Select select, WebElement button, Collection roles) {
+ select.deselectAll();
+ for (String role : getSelectValues(select)) {
+ if (roles == null // if roles not provided, remove all
+ || !roles.contains(role)) { // if roles provided, remove only the redundant
+ select.selectByVisibleText(role);
+ }
+ }
+ button.click();
+ }
+
+ protected void addMissingRoles(Select select, WebElement button, Collection roles) {
+ select.deselectAll();
+ if (roles != null) { // if roles not provided, don't add any
+ for (String role : getSelectValues(select)) {
+ if (roles.contains(role)) { // if roles provided, add only the missing
+ select.selectByVisibleText(role);
+ }
+ }
+ button.click();
+ }
+ }
+
+ public static Set getSelectValues(Select select) {
+ Set roles = new HashSet<>();
+ for (WebElement option : select.getOptions()) {
+ roles.add(option.getText());
+ }
+ return roles;
+ }
+
+ // ***
+ public Set getAvailableRealmRoles() {
+ return getSelectValues(availableRealmRolesSelect);
+ }
+
+ public Set getAvailableClientRoles(String client) {
+ return getSelectValues(availableClientRolesSelect);
+ }
+
+ public Set getAssignedRealmRoles() {
+ return getSelectValues(assignedRealmRolesSelect);
+ }
+
+ public Set getAssignedClientRoles() {
+ return getSelectValues(assignedClientRolesSelect);
+ }
+
+ // *** original methods ***
+ public void addAvailableRole(String... roles) {
+ waitGuiForElement(By.id("available"));
+ for (String role : roles) {
+ availableRealmRolesSelect.selectByVisibleText(role);
+ addSelectedRealmRolesButton.click();
+ }
+ }
+
+ public void removeAssignedRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ assignedRealmRolesSelect.selectByVisibleText(role);
+ removeSelectedRealmRolesButton.click();
+ }
+
+ public boolean isAssignedRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ try {
+ assignedRealmRolesSelect.selectByVisibleText(role);
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isAssignedClientRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ try {
+ assignedClientRolesSelect.selectByVisibleText(role);
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+
+ public void selectClientRole(String client) {
+ waitGuiForElement(By.id("clients"));
+ clientSelect.selectByVisibleText(client);
+ }
+
+ public void addAvailableClientRole(String... roles) {
+ waitGuiForElement(By.id("available-client"));
+ for (String role : roles) {
+ availableClientRolesSelect.selectByVisibleText(role);
+ addSelectedClientRolesButton.click();
+ }
+ }
+
+ public void removeAssignedClientRole(String client) {
+ waitGuiForElement(By.id("assigned-client"));
+ assignedClientRolesSelect.selectByVisibleText(client);
+ removeSelectedClientRolesButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java
new file mode 100644
index 00000000000..ac1db15fa8f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java
@@ -0,0 +1,101 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RoleForm extends Form {
+
+ @FindBy(id = "name")
+ private WebElement nameInput;
+
+ @FindBy(id = "description")
+ private WebElement descriptionInput;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='compositeSwitch']]")
+ private OnOffSwitch compositeSwitch;
+
+ @FindBy(xpath = ".//fieldset[./legend[contains(text(),'Composite Roles')]]")
+ private RoleCompositeRoles compositeRoles;
+
+ @FindBy(id = "removeRole")
+ private WebElement removeIcon;
+
+ public RoleRepresentation getRole() {
+ RoleRepresentation role = new RoleRepresentation(getName(), getDescription());
+ role.setComposite(isComposite());
+ if (role.isComposite()) {
+ role.setComposites(compositeRoles.getComposites());
+ }
+ return role;
+ }
+
+ public void setRole(RoleRepresentation role) {
+ setBasicAttributes(role);
+ }
+
+ public RoleRepresentation getBasicAttributes() {
+ RoleRepresentation role = new RoleRepresentation();
+ role.setName(getName());
+ role.setDescription(getDescription());
+ role.setComposite(isComposite());
+ log.info(role.getName() + ": " + role.getDescription() + ", comp: " + role.isComposite());
+ return role;
+ }
+
+ public void setBasicAttributes(RoleRepresentation role) {
+ setName(role.getName());
+ setDescription(role.getDescription());
+ if (role.isComposite()) {
+ setCompositeRoles(role);
+ }
+ }
+
+ // TODO KEYCLOAK-1364 enabling/disabling composite role seems unintuitive
+ // it should be possible to remove all composite roles by switching to OFF
+ public void setCompositeRoles(RoleRepresentation role) {
+ if (role.isComposite() && role.getComposites() != null) {
+ setComposite(true);
+ }
+ compositeRoles.setComposites(role.getComposites());
+ }
+
+ public void setName(String name) {
+ setInputValue(nameInput, name);
+ }
+
+ public String getName() {
+ return getInputValue(nameInput);
+ }
+
+ public void setDescription(String description) {
+ setInputValue(descriptionInput, description);
+ }
+
+ public String getDescription() {
+ return getInputValue(descriptionInput);
+ }
+
+ public void setComposite(boolean composite) {
+ compositeSwitch.setOn(composite);
+ }
+
+ public boolean isComposite() {
+ return compositeSwitch.isOn();
+ }
+
+ public RoleCompositeRoles compositeRoles() {
+ return compositeRoles;
+ }
+
+ public void delete() {
+ removeIcon.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java
new file mode 100644
index 00000000000..910e47032c9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Roles extends AdminConsoleRealm {
+
+ @FindBy(css = "ul.nav-tabs")
+ private RoleTabs tabs;
+
+ public RoleTabs tabs() {
+ return tabs;
+ }
+
+ public class RoleTabs {
+
+ @FindBy(linkText = "Realm Roles")
+ private WebElement realmRolesTab;
+ @FindBy(linkText = "Default Roles")
+ private WebElement defaultRolesTab;
+
+ public void realmRoles() {
+ realmRolesTab.click();
+ }
+
+ public void defaultRoles() {
+ defaultRolesTab.click();
+ }
+
+ }
+
+ public RolesResource rolesResource() {
+ return realmResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java
new file mode 100644
index 00000000000..b215844b0b5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java
@@ -0,0 +1,85 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import static org.openqa.selenium.By.tagName;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RolesTable extends DataTable {
+
+ public static final String ADD_ROLE = "Add Role";
+
+ public static final String EDIT = "Edit";
+ public static final String DELETE = "Delete";
+
+ public List searchRoles(String searchPattern) {
+ search(searchPattern);
+ return getRolesFromTableRows();
+ }
+
+ public void addRole() {
+ clickHeaderLink(ADD_ROLE);
+ }
+
+ public void clickRole(String name) {
+ waitAjaxForBody();
+ clickRowByLinkText(name);
+ }
+
+ public void editRole(String name) {
+ clickRowActionButton(getRowByLinkText(name), EDIT);
+ }
+
+ public void deleteRole(String name) {
+ clickRowActionButton(getRowByLinkText(name), DELETE);
+ }
+
+ public RoleRepresentation findRole(String name) {
+ List roles = searchRoles(name);
+ if (roles.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == roles.size();
+ return roles.get(0);
+ }
+ }
+
+ public boolean containsRole(String roleName) {
+ for (RoleRepresentation r : getRolesFromTableRows()) {
+ if (roleName.equals(r.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List getRolesFromTableRows() {
+ List rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ RoleRepresentation role = getRoleFromRow(row);
+ if (role != null) {
+ rows.add(role);
+ }
+ }
+ return rows;
+ }
+
+ public RoleRepresentation getRoleFromRow(WebElement row) {
+ RoleRepresentation role = null;
+ List tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ role = new RoleRepresentation();
+ role.setName(tds.get(0).getText());
+ role.setComposite(Boolean.valueOf(tds.get(1).getText()));
+ role.setDescription(tds.get(2).getText());
+ }
+ return role;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/session/SessionsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/RealmSessions.java
similarity index 68%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/session/SessionsPage.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/RealmSessions.java
index 342a76395e1..147da209e6a 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/session/SessionsPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/RealmSessions.java
@@ -15,10 +15,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.console.page.sessions;
-package org.keycloak.testsuite.admin.page.session;
-
-import org.keycloak.testsuite.admin.page.AbstractPage;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@@ -26,12 +24,17 @@ import org.openqa.selenium.support.FindBy;
*
* @author Petr Mensik
*/
-public class SessionsPage extends AbstractPage {
+public class RealmSessions extends Sessions {
- @FindBy(id = "logoutAllSessions")
- private WebElement logoutAllSessions;
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/realm";
+ }
- public void logoutAllSessions() {
- logoutAllSessions.click();
- }
+ @FindBy(id = "logoutAllSessions")
+ private WebElement logoutAllButton;
+
+ public void logoutAllSessions() {
+ logoutAllButton.click();
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java
new file mode 100644
index 00000000000..01758488168
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.sessions;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Revocation extends Sessions {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/revocation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java
new file mode 100644
index 00000000000..a0a8dbaea91
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java
@@ -0,0 +1,32 @@
+package org.keycloak.testsuite.console.page.sessions;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Sessions extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+ @FindBy(linkText = "Realm Sessions")
+ private WebElement realmSessionsTab;
+
+ @FindBy(linkText = "Revocation")
+ private WebElement revocationTab;
+
+ public void realmSessions() {
+ realmSessionsTab.click();
+ }
+
+ public void revocation() {
+ revocationTab.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java
new file mode 100644
index 00000000000..4cd0df6a6c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateUser extends AdminConsoleCreate {
+
+ public CreateUser() {
+ setEntity("user");
+ }
+
+ @Page
+ private UserAttributesForm form;
+
+ public UserAttributesForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java
new file mode 100644
index 00000000000..fba2adb83ff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.testsuite.console.page.fragment.Breadcrumb;
+import static org.keycloak.testsuite.console.page.fragment.Breadcrumb.BREADCRUMB_XPATH;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class User extends Users {
+
+ public static final String ID = "id";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ID + "}";
+ }
+
+ public void setId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getId() {
+ return (String) getUriParameter(ID);
+ }
+
+ @FindBy(xpath = BREADCRUMB_XPATH)
+ private Breadcrumb breadcrumb;
+
+ public Breadcrumb breadcrumb() {
+ return breadcrumb;
+ }
+
+ @FindBy(xpath = "//div[@data-ng-controller='UserTabCtrl']/ul")
+ protected UserTabs userTabs;
+
+ public UserTabs tabs() {
+ return userTabs;
+ }
+
+ public class UserTabs {
+
+ @FindBy(linkText = "Attributes")
+ private WebElement attributesLink;
+ @FindBy(linkText = "Credentials")
+ private WebElement credentialsLink;
+ @FindBy(linkText = "Role Mappings")
+ private WebElement roleMappingsLink;
+ @FindBy(linkText = "Consents")
+ private WebElement consentsLink;
+ @FindBy(linkText = "Sessions")
+ private WebElement sessionsLink;
+
+ public void attributes() {
+ attributesLink.click();
+ }
+
+ public void credentials() {
+ credentialsLink.click();
+ }
+
+ public void roleMappings() {
+ roleMappingsLink.click();
+ }
+
+ public void consents() {
+ consentsLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ }
+
+ public UserResource userResource() {
+ return usersResource().get(getId());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java
new file mode 100644
index 00000000000..ebe52189a7c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java
@@ -0,0 +1,22 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserAttributes extends User {
+
+ @FindBy(name = "userForm")
+ private UserAttributesForm form;
+
+ public UserAttributesForm form() {
+ return form;
+ }
+
+ public void backToUsersViaBreadcrumb() {
+ breadcrumb().clickItemOneLevelUp();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java
new file mode 100644
index 00000000000..85b32e6b4cf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java
@@ -0,0 +1,139 @@
+package org.keycloak.testsuite.console.page.users;
+
+import java.util.List;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class UserAttributesForm extends Form {
+
+ @FindBy(id = "id")
+ private WebElement idInput;
+
+ @FindBy(id = "username")
+ private WebElement usernameInput;
+
+ @FindBy(id = "email")
+ private WebElement emailInput;
+
+ @FindBy(id = "firstName")
+ private WebElement firstNameInput;
+
+ @FindBy(id = "lastName")
+ private WebElement lastNameInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='userEnabled']]")
+ private OnOffSwitch userEnabledSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='emailVerified']]")
+ private OnOffSwitch emailVerifiedSwitch;
+
+ @FindBy(xpath = ".//div[./label[contains(text(), 'Required User Actions')]]//input")
+ private WebElement requiredUserActionsInput;
+
+ @FindBy(id = "reqActions")
+ private Select requiredUserActionsSelect;
+
+ @FindBy(className = "select2-result-label")
+ private WebElement requiredUserActionsConfirm;
+
+ @FindBy(className = "select2-search-choice-close")
+ private List removeRequiredActionsList;
+
+ @FindBy(xpath = "//button[@data-ng-click='unlockUser()']")
+ private WebElement unlockUserButton;
+
+ public String getId() {
+ return getInputValue(idInput);
+ }
+
+ public String getUsername() {
+ return getInputValue(usernameInput);
+ }
+
+ public void setUsername(String username) {
+ setInputValue(usernameInput, username);
+ }
+
+ public String getEmail() {
+ return getInputValue(emailInput);
+ }
+
+ public void setEmail(String email) {
+ setInputValue(emailInput, email);
+ }
+
+ public String getFirstName() {
+ return getInputValue(firstNameInput);
+ }
+
+ public void setFirstName(String firstName) {
+ setInputValue(firstNameInput, firstName);
+ }
+
+ public String getLastName() {
+ return getInputValue(lastNameInput);
+ }
+
+ public void setLastName(String lastname) {
+ setInputValue(lastNameInput, lastname);
+ }
+
+ public boolean isEnabled() {
+ return userEnabledSwitch.isOn();
+ }
+
+ public void setEnabled(boolean enabled) {
+ userEnabledSwitch.setOn(enabled);
+ }
+
+ public void unlockUser() {
+ unlockUserButton.click();
+ }
+
+ public boolean isEmailVerified() {
+ return emailVerifiedSwitch.isOn();
+ }
+
+ public void setEmailVerified(boolean emailVerified) {
+ emailVerifiedSwitch.setOn(emailVerified);
+ }
+
+ public void addRequiredAction(String requiredAction) {
+ requiredUserActionsInput.click();
+ requiredUserActionsSelect.selectByVisibleText(requiredAction);
+ }
+
+ public void setRequiredActions(List requiredActions) {
+ for (WebElement e : removeRequiredActionsList) {
+ e.click();
+ }
+ if (requiredActions != null && !requiredActions.isEmpty()) {
+ for (String action : requiredActions) {
+ addRequiredAction(action);
+ }
+ }
+ }
+
+ public void setValues(UserRepresentation user) {
+ waitAjaxForElement(usernameInput);
+ setUsername(user.getUsername());
+ setEmail(user.getEmail());
+ setFirstName(user.getFirstName());
+ setLastName(user.getLastName());
+ setEnabled(user.isEnabled());
+ setEmailVerified(user.isEmailVerified());
+ setRequiredActions(user.getRequiredActions());
+ }
+
+ // TODO Contact Information section
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java
new file mode 100644
index 00000000000..65257638da1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.users;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserConsents extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/consents";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java
new file mode 100644
index 00000000000..830d789c9d7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import static org.keycloak.testsuite.page.Form.setInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserCredentials extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/user-credentials";
+ }
+
+ @FindBy(id = "password")
+ private WebElement newPasswordInput;
+
+ @FindBy(id = "confirmPassword")
+ private WebElement confirmPasswordInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='temporaryPassword']]")
+ private OnOffSwitch temporaryOnOffSwitch;
+
+ @FindBy(xpath = ".//button[contains(@data-ng-click, 'resetPassword')]")
+ private WebElement resetPasswordButton;
+
+ public void setNewPassword(String newPassword) {
+ setInputValue(newPasswordInput, newPassword);
+ }
+
+ public void setConfirmPassword(String confirmPassword) {
+ setInputValue(confirmPasswordInput, confirmPassword);
+ }
+
+ public void setTemporary(boolean temporary) {
+ temporaryOnOffSwitch.setOn(temporary);
+ }
+
+ public void clickResetPasswordAndConfirm() {
+ resetPasswordButton.click();
+ modalDialog.ok();
+ }
+
+ public void resetPassword(String newPassword) {
+ resetPassword(newPassword, newPassword);
+ }
+ public void resetPassword(String newPassword, String confirmPassword) {
+ setNewPassword(newPassword);
+ setConfirmPassword(confirmPassword);
+ clickResetPasswordAndConfirm();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java
new file mode 100644
index 00000000000..3a05b68ab8d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.admin.client.resource.RoleMappingResource;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserRoleMappings extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "role-mappings";
+ }
+
+ @Page
+ private UserRoleMappingsForm form;
+
+ public UserRoleMappingsForm form() {
+ return form;
+ }
+
+ public RoleMappingResource roleMappingResource() {
+ return userResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java
new file mode 100644
index 00000000000..26290176a9d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java
@@ -0,0 +1,46 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.roles.RoleCompositeRoles;
+
+/**
+ * Created by fkiss.
+ */
+public class UserRoleMappingsForm extends RoleCompositeRoles {
+
+ @FindBy(id = "realm-composite")
+ private Select effectiveRolesSelect;
+
+ @FindBy(id = "client-composite")
+ private Select effectiveClientRolesSelect;
+
+ public boolean isEffectiveRealmRolesComplete(RoleRepresentation... roles) {
+ return isEffectiveRolesComplete(effectiveRolesSelect, roles);
+ }
+
+ public boolean isEffectiveClientRolesComplete(RoleRepresentation... roles) {
+ return isEffectiveRolesComplete(effectiveClientRolesSelect, roles);
+ }
+
+ private boolean isEffectiveRolesComplete(Select select, RoleRepresentation... roles) {
+ List roleNames = new ArrayList<>();
+ for (RoleRepresentation role : roles) {
+ roleNames.add(role.getName());
+ }
+ for (WebElement role : select.getOptions()) {
+ roleNames.contains(role.getText());
+ roleNames.remove(role.getText());
+ }
+ log.info(Arrays.toString(roles));
+ log.info(roleNames);
+ return roleNames.isEmpty();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java
new file mode 100644
index 00000000000..bfe9ac53196
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.users;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserSessions extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java
new file mode 100644
index 00000000000..3b1d7303995
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java
@@ -0,0 +1,142 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.console.page.users;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import static org.openqa.selenium.By.*;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class Users extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/users";
+ }
+
+ public static final String VIEW_ALL_USERS = "View all users";
+ public static final String UNLOCK_USERS = "Unlock Users";
+ public static final String ADD_USER = "Add User";
+
+ public static final String EDIT = "Edit";
+ public static final String IMPERSONATE = "Impersonate";
+ public static final String DELETE = "Delete";
+
+ @FindBy(xpath = "//div[./h1[text()='Users']]/table")
+ private UsersTable table;
+
+ public UsersTable table() {
+ return table;
+ }
+
+ public class UsersTable extends DataTable {
+
+ public List searchUsers(String searchPattern) {
+ search(searchPattern);
+ return getUsersFromTableRows();
+ }
+
+ public void viewAllUsers() {
+ clickHeaderButton(VIEW_ALL_USERS);
+ }
+
+ public void unlockUsers() {
+ clickHeaderButton(UNLOCK_USERS);
+ }
+
+ public void clickUser(String username) {
+ waitAjaxForElement(body());
+ body().findElement(linkText(username)).click();
+ }
+
+ public void editUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), EDIT);
+ }
+
+ public void impersonateUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), IMPERSONATE);
+ }
+
+ public void deleteUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), DELETE);
+ modalDialog.confirmDeletion();
+ }
+
+ public void addUser() {
+ clickHeaderLink(ADD_USER);
+ }
+
+ public UserRepresentation findUser(String searchPattern) {
+ List users = searchUsers(searchPattern);
+ if (users.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == users.size();
+ return users.get(0);
+ }
+ }
+
+ public UserRepresentation getUserFromTableRow(WebElement row) {
+ UserRepresentation user = null;
+ List tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ user = new UserRepresentation();
+ user.setUsername(tds.get(0).getText());
+ user.setLastName(tds.get(1).getText());
+ user.setFirstName(tds.get(2).getText());
+ user.setEmail(tds.get(3).getText());
+ }
+ return user;
+ }
+
+ public List getUsersFromTableRows() {
+ List users = new ArrayList<>();
+ List rows = rows();
+// if (rows.size() > 1) {
+ for (WebElement rowElement : rows) {
+ if (rowElement.isDisplayed()) {
+ UserRepresentation user = getUserFromTableRow(rowElement);
+ if (user != null) {
+ users.add(user);
+ }
+ }
+ }
+// }
+ return users;
+ }
+
+ }
+
+ public UsersResource usersResource() {
+ return realmResource().users();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Provider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Provider.java
similarity index 95%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Provider.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Provider.java
index 6f7fc767c1f..c47305074a4 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Provider.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Provider.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.model;
+package org.keycloak.testsuite.model;
/**
*
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/RequiredUserAction.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/RequiredUserAction.java
new file mode 100644
index 00000000000..8aa73a7bd4a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/RequiredUserAction.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public enum RequiredUserAction {
+
+ UPDATE_PASSWORD("Update Password"),
+ VERIFY_EMAIL("Verify Email"),
+ UPDATE_PROFILE("Update Profile"),
+ CONFIGURE_TOTP("Configure Totp"),
+ TERMS_AND_CONDITIONS("Terms and Conditions");
+
+ private final String actionName;
+
+ private RequiredUserAction(String actionName) {
+ this.actionName = actionName;
+ }
+
+ public String getActionName() {
+ return actionName;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/SocialProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/SocialProvider.java
similarity index 95%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/SocialProvider.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/SocialProvider.java
index 3ba17ec8daf..7ec785d5981 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/SocialProvider.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/SocialProvider.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.model;
+package org.keycloak.testsuite.model;
/**
*
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Theme.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Theme.java
similarity index 95%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Theme.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Theme.java
index 6765ba2843d..21235aa8197 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Theme.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Theme.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.keycloak.testsuite.admin.model;
+package org.keycloak.testsuite.model;
/**
*
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java
new file mode 100644
index 00000000000..a06666ccc8b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.page;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.openqa.selenium.WebDriver;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractPage {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ private final Map uriParameters = new HashMap<>();
+
+ @Drone
+ protected WebDriver driver;
+
+ private UriBuilder builder;
+
+ public WebDriver getDriver() {
+ return driver;
+ }
+
+ public abstract UriBuilder createUriBuilder();
+
+ public String getUriFragment() {
+ return "";
+ }
+
+ /**
+ *
+ * @return Instance of UriBuilder that can build URIs for a concrete page.
+ */
+ public UriBuilder getUriBuilder() {
+ if (builder == null) {
+ builder = createUriBuilder();
+ String fragment = getUriFragment();
+ if (fragment != null && !fragment.isEmpty()) {
+ builder.fragment(fragment);
+ }
+ }
+ return builder;
+ }
+
+ public AbstractPage setUriParameter(String name, Object value) {
+ uriParameters.put(name, value);
+ return this;
+ }
+
+ public Object getUriParameter(String name) {
+ return uriParameters.get(name);
+ }
+
+ public URI buildUri() {
+ return getUriBuilder().buildFromMap(uriParameters);
+ }
+
+ @Override
+ public String toString() {
+ return buildUri().toASCIIString();
+ }
+
+ public void navigateTo() {
+ String uri = buildUri().toASCIIString();
+ log.debug("current URL: " + driver.getCurrentUrl());
+ log.info("navigating to " + uri);
+ driver.navigate().to(uri);
+ pause(300); // this is needed for FF for some reason
+ log.info("current URL: " + driver.getCurrentUrl());
+ }
+
+ public boolean isCurrent() {
+ return driver.getCurrentUrl().equals(toString());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
new file mode 100644
index 00000000000..b85c75a0c6e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.page;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractPageWithInjectedUrl extends AbstractPage {
+
+ public abstract URL getInjectedUrl();
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ try {
+ return UriBuilder.fromUri(getInjectedUrl().toURI());
+ } catch (URISyntaxException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java
new file mode 100644
index 00000000000..eee8a219b5c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.page;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import static org.jboss.arquillian.graphene.Graphene.guardAjax;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Form {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ @Drone
+ protected WebDriver driver;
+
+ public static final String ACTIVE_DIV_XPATH = ".//div[not(contains(@class,'ng-hide'))]";
+
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Save']")
+ private WebElement save;
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Cancel']")
+ private WebElement cancel;
+
+ public void save() {
+// guardAjax(save).click();
+ save.click();
+ }
+
+ public void cancel() {
+ guardAjax(cancel).click();
+ }
+
+ public static String getInputValue(WebElement input) {
+ waitAjaxForElement(input);
+ return input.getAttribute(VALUE);
+ }
+
+ public static final String VALUE = "value";
+
+ public static void setInputValue(WebElement input, String value) {
+ waitAjaxForElement(input);
+ if (input.isEnabled()) {
+ input.clear();
+ if (value != null) {
+ input.sendKeys(value);
+ }
+ } else {
+ // TODO log warning
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java
new file mode 100644
index 00000000000..2898773f01f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.servlet;
+
+import org.keycloak.services.resources.RealmsResource;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class ApplicationServlet extends HttpServlet {
+
+ private static final String LINK = "%s";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String title;
+ if (req.getRequestURI().endsWith("auth")) {
+ title = "AUTH_RESPONSE";
+ } else if (req.getRequestURI().endsWith("logout")) {
+ title = "LOGOUT_REQUEST";
+ } else {
+ title = "APP_REQUEST";
+ }
+
+ PrintWriter pw = resp.getWriter();
+ pw.printf("%s", title);
+ UriBuilder base = UriBuilder.fromUri("http://localhost:8081/auth");
+ pw.printf(LINK, RealmsResource.accountUrl(base).build("test"), "account", "account");
+
+ pw.print("");
+ pw.flush();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
new file mode 100644
index 00000000000..9ff2bcf61ba
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -0,0 +1,43 @@
+package org.keycloak.testsuite.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class IOUtil {
+
+ public static T loadJson(InputStream is, Class type) {
+ try {
+ return JsonSerialization.readValue(is, type);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to load json.", e);
+ }
+ }
+
+ public static RealmRepresentation loadRealm(String realmConfig) {
+ return loadRealm(IOUtil.class.getResourceAsStream(realmConfig));
+ }
+
+ public static RealmRepresentation loadRealm(File realmFile) {
+ try {
+ return loadRealm(new FileInputStream(realmFile));
+ } catch (FileNotFoundException ex) {
+ throw new IllegalStateException("Test realm file not found: " + realmFile);
+ }
+ }
+
+ public static RealmRepresentation loadRealm(InputStream is) {
+ RealmRepresentation realm = loadJson(is, RealmRepresentation.class);
+ System.out.println("Loaded realm " + realm.getRealm());
+ return realm;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java
new file mode 100644
index 00000000000..9d1d19ef530
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java
@@ -0,0 +1,128 @@
+package org.keycloak.testsuite.util;
+
+import org.jboss.logging.Logger;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.UserFederationProvider;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Marek Posolda
+ */
+public class LDAPTestConfiguration {
+
+ private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class);
+
+ private String connectionPropertiesLocation;
+ private boolean startEmbeddedLdapLerver = true;
+ private Map config;
+
+ protected static final Map PROP_MAPPINGS = new HashMap();
+ protected static final Map DEFAULT_VALUES = new HashMap();
+
+ static {
+ PROP_MAPPINGS.put(LDAPConstants.CONNECTION_URL, "idm.test.ldap.connection.url");
+ PROP_MAPPINGS.put(LDAPConstants.BASE_DN, "idm.test.ldap.base.dn");
+ PROP_MAPPINGS.put(LDAPConstants.USERS_DN, "idm.test.ldap.user.dn.suffix");
+ PROP_MAPPINGS.put(LDAPConstants.BIND_DN, "idm.test.ldap.bind.dn");
+ PROP_MAPPINGS.put(LDAPConstants.BIND_CREDENTIAL, "idm.test.ldap.bind.credential");
+ PROP_MAPPINGS.put(LDAPConstants.VENDOR, "idm.test.ldap.vendor");
+ PROP_MAPPINGS.put(LDAPConstants.CONNECTION_POOLING, "idm.test.ldap.connection.pooling");
+ PROP_MAPPINGS.put(LDAPConstants.PAGINATION, "idm.test.ldap.pagination");
+ PROP_MAPPINGS.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "idm.test.ldap.batch.size.for.sync");
+ PROP_MAPPINGS.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "idm.test.ldap.username.ldap.attribute");
+ PROP_MAPPINGS.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, "idm.test.ldap.rdn.ldap.attribute");
+ PROP_MAPPINGS.put(LDAPConstants.USER_OBJECT_CLASSES, "idm.test.ldap.user.object.classes");
+ PROP_MAPPINGS.put(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE, "idm.test.ldap.user.account.controls.after.password.update");
+ PROP_MAPPINGS.put(LDAPConstants.EDIT_MODE, "idm.test.ldap.edit.mode");
+
+ PROP_MAPPINGS.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "idm.test.kerberos.allow.kerberos.authentication");
+ PROP_MAPPINGS.put(KerberosConstants.KERBEROS_REALM, "idm.test.kerberos.realm");
+ PROP_MAPPINGS.put(KerberosConstants.SERVER_PRINCIPAL, "idm.test.kerberos.server.principal");
+ PROP_MAPPINGS.put(KerberosConstants.KEYTAB, "idm.test.kerberos.keytab");
+ PROP_MAPPINGS.put(KerberosConstants.DEBUG, "idm.test.kerberos.debug");
+ PROP_MAPPINGS.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "idm.test.kerberos.allow.password.authentication");
+ PROP_MAPPINGS.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "idm.test.kerberos.update.profile.first.login");
+ PROP_MAPPINGS.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "idm.test.kerberos.use.kerberos.for.password.authentication");
+
+ DEFAULT_VALUES.put(LDAPConstants.CONNECTION_URL, "ldap://localhost:10389");
+ DEFAULT_VALUES.put(LDAPConstants.BASE_DN, "dc=keycloak,dc=org");
+ DEFAULT_VALUES.put(LDAPConstants.USERS_DN, "ou=People,dc=keycloak,dc=org");
+ DEFAULT_VALUES.put(LDAPConstants.BIND_DN, "uid=admin,ou=system");
+ DEFAULT_VALUES.put(LDAPConstants.BIND_CREDENTIAL, "secret");
+ DEFAULT_VALUES.put(LDAPConstants.VENDOR, LDAPConstants.VENDOR_OTHER);
+ DEFAULT_VALUES.put(LDAPConstants.CONNECTION_POOLING, "true");
+ DEFAULT_VALUES.put(LDAPConstants.PAGINATION, "true");
+ DEFAULT_VALUES.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC));
+ DEFAULT_VALUES.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, null);
+ DEFAULT_VALUES.put(LDAPConstants.USER_OBJECT_CLASSES, null);
+ DEFAULT_VALUES.put(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE, "false");
+ DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString());
+
+ DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
+ DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
+ DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
+ URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab");
+ String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath();
+ DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
+ DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
+ DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true");
+ DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
+ DEFAULT_VALUES.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "false");
+ }
+
+ public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) {
+ LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration();
+ ldapTestConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation);
+ ldapTestConfiguration.loadConnectionProperties();
+ return ldapTestConfiguration;
+ }
+
+ protected void loadConnectionProperties() {
+ Properties p = new Properties();
+ try {
+ log.info("Reading LDAP configuration from: " + connectionPropertiesLocation);
+ InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(connectionPropertiesLocation);
+ p.load(is);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ config = new HashMap();
+ for (Map.Entry property : PROP_MAPPINGS.entrySet()) {
+ String propertyName = property.getKey();
+ String configName = property.getValue();
+
+ String value = (String) p.get(configName);
+ if (value == null) {
+ value = DEFAULT_VALUES.get(propertyName);
+ }
+
+ config.put(propertyName, value);
+ }
+
+ startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true"));
+ log.info("Start embedded server: " + startEmbeddedLdapLerver);
+ log.info("Read config: " + config);
+ }
+
+ public Map getLDAPConfig() {
+ return config;
+ }
+
+ public void setConnectionPropertiesLocation(String connectionPropertiesLocation) {
+ this.connectionPropertiesLocation = connectionPropertiesLocation;
+ }
+
+ public boolean isStartEmbeddedLdapLerver() {
+ return startEmbeddedLdapLerver;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
new file mode 100644
index 00000000000..ba5790dea7d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
@@ -0,0 +1,11 @@
+package org.keycloak.testsuite.util;
+
+/**
+ *
+ * @author vramik
+ */
+public class MailServerConfiguration {
+ public static final String FROM = "server@mail.test";
+ public static final String HOST = "localhost";
+ public static final String PORT = "3025";
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/UserAction.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SecondBrowser.java
similarity index 60%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/UserAction.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SecondBrowser.java
index 8a0dc2c942a..4d03b8aaf5e 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/UserAction.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SecondBrowser.java
@@ -15,26 +15,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.util;
-package org.keycloak.testsuite.admin.model;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.jboss.arquillian.drone.api.annotation.Qualifier;
/**
*
- * @author Petr Mensik
+ * @author Petr Mensik
*/
-public enum UserAction {
-
- UPDATE_PASSWORD("Update Password"), VERIFY_EMAIL("Verify Email"), UPDATE_PROFILE("Update Profile"), CONFIGURE_TOTP("Configure Totp");
-
- private final String actionName;
-
- private UserAction(String actionName) {
- this.actionName = actionName;
- }
-
- public String getActionName() {
- return actionName;
- }
-
-
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Qualifier
+public @interface SecondBrowser {
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
new file mode 100644
index 00000000000..0ce7eda38cc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.util;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Timer {
+
+ private static Long time;
+
+ private static final Map> stats = new HashMap<>();
+
+ public static void time() {
+ time = new Date().getTime();
+ }
+
+ public static void time(String operation) {
+ if (time == null) {
+ System.out.println(MessageFormat.format("Starting timer for operation {0}", operation));
+ time();
+ } else {
+ long timeOrig = time;
+ time();
+ logOperation(operation, time - timeOrig);
+ System.out.println(MessageFormat.format("Operation {0} took {1} ms", operation, time - timeOrig));
+ }
+ }
+
+ private static void logOperation(String operation, long delta) {
+ if (!stats.containsKey(operation)) {
+ stats.put(operation, new ArrayList());
+ }
+ stats.get(operation).add(delta);
+ }
+
+ public static void printStats() {
+ if (!stats.isEmpty()) {
+ System.out.println("OPERATION STATS:");
+ }
+ for (String op : stats.keySet()) {
+ long sum = 0;
+ for (Long t : stats.get(op)) {
+ sum += t;
+ }
+ System.out.println(MessageFormat.format("Operation {0} average time: {1,number,#} ms", op, sum / stats.get(op).size()));
+ }
+ stats.clear();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/SeleniumUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
similarity index 58%
rename from testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/SeleniumUtils.java
rename to testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
index 8cd52991010..ac6ed22e4d0 100644
--- a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/util/SeleniumUtils.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
@@ -15,9 +15,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.keycloak.testsuite.util;
-package org.keycloak.testsuite.admin.util;
-
+import java.util.logging.Level;
+import java.util.logging.Logger;
import static org.jboss.arquillian.graphene.Graphene.waitAjax;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import org.openqa.selenium.By;
@@ -26,53 +27,47 @@ import org.openqa.selenium.WebElement;
/**
*
* @author Petr Mensik
+ * @author tkyjovsk
*/
-public final class SeleniumUtils {
-
- private SeleniumUtils() {
- }
-
- public static void waitAjaxForElement(By element) {
- waitAjax().until()
- .element(element)
- .is()
- .present();
- }
+public final class WaitUtils {
public static void waitAjaxForElement(WebElement element) {
waitAjax().until()
- .element(element)
- .is()
- .present();
+ .element(element).is().present();
+ }
+
+ public static void waitAjaxForElementNotPresent(WebElement element) {
+ waitAjax().until()
+ .element(element).is().not().present();
}
public static void waitGuiForElement(By element, String message) {
- waitGui().until(message)
- .element(element)
- .is()
- .present();
- }
+ waitGui().until(message)
+ .element(element).is().present();
+ }
public static void waitGuiForElement(By element) {
- waitGuiForElement(element, null);
- }
+ waitGuiForElement(element, null);
+ }
public static void waitGuiForElement(WebElement element) {
- waitGuiForElement(element, null);
- }
+ waitGuiForElementPresent(element, null);
+ }
- public static void waitGuiForElement(WebElement element, String message) {
- waitGui().until(message)
- .element(element)
- .is()
- .present();
+ public static void waitGuiForElementPresent(WebElement element, String message) {
+ waitGui().until(message).element(element).is().present();
}
public static void waitGuiForElementNotPresent(WebElement element) {
- waitGui().until()
- .element(element)
- .is()
- .not()
- .present();
+ waitGui().until().element(element).is().not().present();
}
+
+ public static void pause(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ex) {
+ Logger.getLogger(WaitUtils.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
new file mode 100644
index 00000000000..88c25faf26d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1,3 @@
+org.keycloak.testsuite.arquillian.KeycloakArquillianExtension
+!org.jboss.arquillian.container.impl.ContainerExtension
+org.keycloak.testsuite.arquillian.containers.MultipleContainersExtension
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
new file mode 100644
index 00000000000..98aa62ef56d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
@@ -0,0 +1,104 @@
+package org.keycloak.testsuite;
+
+import java.text.MessageFormat;
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.keycloak.admin.client.resource.RealmResource;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.console.page.fragment.FlashMessage;
+import org.openqa.selenium.Cookie;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractAuthTest extends AbstractKeycloakTest {
+
+ @Page
+ protected AuthRealm testRealmPage;
+ @Page
+ protected OIDCLogin testRealmLoginPage;
+
+ protected UserRepresentation testUser;
+
+ @FindByJQuery(".alert")
+ protected FlashMessage flashMessage;
+
+ @Override
+ public void addTestRealms(List testRealms) {
+ RealmRepresentation testRealmRep = new RealmRepresentation();
+ testRealmRep.setRealm(TEST);
+ testRealmRep.setEnabled(true);
+ testRealms.add(testRealmRep);
+ }
+
+ @Before
+ public void beforeAuthTest() {
+ testRealmLoginPage.setAuthRealm(testRealmPage);
+
+ testUser = createUserRepresentation("test", "test@email.test", "test", "user", true);
+ setPasswordFor(testUser, PASSWORD);
+
+ deleteAllCookiesForTestRealm();
+ }
+
+ public void createTestUserWithAdminClient() {
+ log.debug("creating test user");
+ String id = createUserAndResetPasswordWithAdminClient(testRealmResource(), testUser, PASSWORD);
+ testUser.setId(id);
+ }
+
+ public static UserRepresentation createUserRepresentation(String username, String email, String firstName, String lastName, boolean enabled) {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setFirstName(firstName);
+ user.setLastName(lastName);
+ user.setEnabled(enabled);
+ return user;
+ }
+
+ public void deleteAllCookiesForTestRealm() {
+ testRealmPage.navigateTo();
+ log.debug("deleting cookies in test realm");
+ driver.manage().deleteAllCookies();
+ }
+
+ public void listCookies() {
+ log.info("LIST OF COOKIES: ");
+ for (Cookie c : driver.manage().getCookies()) {
+ log.info(MessageFormat.format(" {1} {2} {0}",
+ c.getName(), c.getDomain(), c.getPath(), c.getValue()));
+ }
+ }
+
+ public void assertFlashMessageSuccess() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+ }
+
+ public void assertFlashMessageDanger() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isDanger());
+ }
+
+ public void assertFlashMessageError() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isError());
+ }
+
+ public RealmResource testRealmResource() {
+ return adminClient.realm(testRealmPage.getAuthRealm());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
new file mode 100644
index 00000000000..33991151cee
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -0,0 +1,176 @@
+package org.keycloak.testsuite;
+
+import org.keycloak.testsuite.arquillian.TestContext;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.NotFoundException;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.logging.Logger;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import org.keycloak.testsuite.arquillian.SuiteContext;
+import org.openqa.selenium.WebDriver;
+import org.keycloak.testsuite.auth.page.AuthServer;
+import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
+import static org.keycloak.testsuite.util.URLAssert.*;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.auth.page.login.UpdatePassword;
+import org.keycloak.testsuite.util.Timer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunWith(Arquillian.class)
+@RunAsClient
+public abstract class AbstractKeycloakTest {
+
+ protected Logger log = Logger.getLogger(this.getClass());
+
+ @ArquillianResource
+ protected SuiteContext suiteContext;
+
+ @ArquillianResource
+ protected TestContext testContext;
+
+ @ArquillianResource
+ protected Keycloak adminClient;
+
+ protected List testRealmReps;
+
+ @Drone
+ protected WebDriver driver;
+
+ @Page
+ protected AuthServerContextRoot authServerContextRootPage;
+ @Page
+ protected AuthServer authServerPage;
+
+ @Page
+ protected AuthRealm masterRealmPage;
+
+ @Page
+ protected Account accountPage;
+ @Page
+ protected OIDCLogin loginPage;
+ @Page
+ protected UpdatePassword updatePasswordPage;
+
+ protected UserRepresentation adminUser;
+
+ @Before
+ public void beforeAbstractKeycloakTest() {
+ adminUser = createAdminUserRepresentation();
+
+ setDefaultPageUriParameters();
+
+ driverSettings();
+
+ if (!suiteContext.isAdminPasswordUpdated()) {
+ updateMasterAdminPassword();
+ suiteContext.setAdminPasswordUpdated(true);
+ }
+
+ importTestRealms();
+ }
+
+ @After
+ public void afterAbstractKeycloakTest() {
+// removeTestRealms(); // keeping test realms after test to be able to inspect failures, instead deleting existing realms before import
+// keycloak.close(); // keeping admin connection open
+ Timer.printStats();
+ }
+
+ private void updateMasterAdminPassword() {
+ accountPage.navigateTo();
+ loginPage.form().login(ADMIN, ADMIN);
+ updatePasswordPage.updatePasswords(ADMIN, ADMIN);
+ assertCurrentUrlStartsWith(accountPage);
+ deleteAllCookiesForMasterRealm();
+ }
+
+ public void deleteAllCookiesForMasterRealm() {
+ masterRealmPage.navigateTo();
+ log.debug("deleting cookies in master realm");
+ driver.manage().deleteAllCookies();
+ }
+
+ protected void driverSettings() {
+ driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
+ driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
+ driver.manage().timeouts().setScriptTimeout(3, TimeUnit.SECONDS);
+ driver.manage().window().maximize();
+ }
+
+ public void setDefaultPageUriParameters() {
+ masterRealmPage.setAuthRealm(MASTER);
+ loginPage.setAuthRealm(MASTER);
+ }
+
+ public abstract void addTestRealms(List testRealms);
+
+ private void addTestRealms() {
+ log.debug("loading test realms");
+ if (testRealmReps == null) {
+ testRealmReps = new ArrayList<>();
+ }
+ if (testRealmReps.isEmpty()) {
+ addTestRealms(testRealmReps);
+ }
+ }
+
+ public void importTestRealms() {
+ addTestRealms();
+ log.info("importing test realms");
+ for (RealmRepresentation testRealm : testRealmReps) {
+ importRealm(testRealm);
+ }
+ }
+
+ public void removeTestRealms() {
+ log.info("removing test realms");
+ for (RealmRepresentation testRealm : testRealmReps) {
+ removeRealm(testRealm);
+ }
+ }
+
+ private UserRepresentation createAdminUserRepresentation() {
+ UserRepresentation adminUserRep = new UserRepresentation();
+ adminUserRep.setUsername(ADMIN);
+ setPasswordFor(adminUserRep, ADMIN);
+ return adminUserRep;
+ }
+
+ public void importRealm(RealmRepresentation realm) {
+ log.debug("importing realm: " + realm.getRealm());
+ try { // TODO - figure out a way how to do this without try-catch
+ RealmResource realmResource = adminClient.realms().realm(realm.getRealm());
+ RealmRepresentation rRep = realmResource.toRepresentation();
+ log.debug("realm already exists on server, re-importing");
+ realmResource.remove();
+ } catch (NotFoundException nfe) {
+ // expected when realm does not exist
+ }
+ adminClient.realms().create(realm);
+ }
+
+ public void removeRealm(RealmRepresentation realm) {
+ adminClient.realms().realm(realm.getRealm()).remove();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java
new file mode 100644
index 00000000000..6a7a4c18e9c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java
@@ -0,0 +1,31 @@
+package org.keycloak.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.testsuite.AbstractAuthTest;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.account.AccountManagement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractAccountManagementTest extends AbstractAuthTest {
+
+ @Page
+ protected AccountManagement testRealmAccountManagementPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(TEST);
+ testRealmAccountManagementPage.setAuthRealm(TEST);
+ }
+
+ @Before
+ public void beforeAbstractAccountTest() {
+ // make user test user exists in test realm
+ createTestUserWithAdminClient();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
new file mode 100644
index 00000000000..e509276ecfe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.keycloak.testsuite.auth.page.account.Account;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class AccountTest extends AbstractAccountManagementTest {
+
+ private static final String UPDATED_EMAIL = "new-name@email.test";
+ private static final String NEW_FIRST_NAME = "John";
+ private static final String NEW_LAST_NAME = "Smith";
+
+ @Page
+ private Account testRealmAccountPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmAccountPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeAccountTest() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ }
+
+ @After
+ public void afterAccountTest() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmAccountManagementPage.signOut();
+ }
+
+ @Test
+ public void editAccount() {
+ testRealmAccountManagementPage.account();
+ assertEquals(testRealmAccountPage.getUsername(), testUser.getUsername());
+
+ testRealmAccountPage.setEmail(UPDATED_EMAIL);
+ testRealmAccountPage.setFirstName(NEW_FIRST_NAME);
+ testRealmAccountPage.setLastName(NEW_LAST_NAME);
+ testRealmAccountPage.save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser);
+
+ testRealmAccountManagementPage.account();
+ assertEquals(testRealmAccountPage.getEmail(), UPDATED_EMAIL);
+ assertEquals(testRealmAccountPage.getFirstName(), NEW_FIRST_NAME);
+ assertEquals(testRealmAccountPage.getLastName(), NEW_LAST_NAME);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java
new file mode 100644
index 00000000000..5787903ae05
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java
@@ -0,0 +1,67 @@
+package org.keycloak.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.account.ChangePassword;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ChangePasswordTest extends AbstractAccountManagementTest {
+
+ private static final String NEW_PASSWORD = "newpassword";
+ private static final String WRONG_PASSWORD = "wrongpassword";
+
+ @Page
+ private ChangePassword testRealmChangePasswordPage;
+
+ private String correctPassword;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmChangePasswordPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeChangePasswordTest() {
+ correctPassword = getPasswordOf(testUser);
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ testRealmAccountManagementPage.password();
+ }
+
+ @Test
+ public void invalidChangeAttempts() {
+ testRealmChangePasswordPage.save();
+ assertFlashMessageError();
+
+ testRealmChangePasswordPage.changePasswords(WRONG_PASSWORD, NEW_PASSWORD, NEW_PASSWORD);
+ assertFlashMessageError();
+
+ testRealmChangePasswordPage.changePasswords(correctPassword, NEW_PASSWORD, NEW_PASSWORD + "-mismatch");
+ assertFlashMessageError();
+ }
+
+ @Test
+ public void successfulChangeAttempts() {
+ // change password successfully
+ testRealmChangePasswordPage.changePasswords(correctPassword, NEW_PASSWORD, NEW_PASSWORD);
+ assertFlashMessageSuccess();
+
+ // login using new password
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser.getUsername(), NEW_PASSWORD);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+
+ // change password back
+ testRealmAccountManagementPage.password();
+ testRealmChangePasswordPage.changePasswords(NEW_PASSWORD, correctPassword, correctPassword);
+ assertFlashMessageSuccess();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java
new file mode 100644
index 00000000000..5297038c938
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java
@@ -0,0 +1,132 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.login.Registration;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsername;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class RegistrationTest extends AbstractAccountManagementTest {
+
+ @Page
+ private Registration testRealmRegistrationPage;
+
+ private UserRepresentation newUser;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmRegistrationPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeUserRegistration() {
+ // enable user registration in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setRegistrationAllowed(true);
+ testRealmResource().update(testRealmRep);
+
+ newUser = createUserRepresentation("new_user", "new_user@email.test", "new", "user", true);
+ setPasswordFor(newUser, PASSWORD);
+
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().register();
+ }
+
+ public void assertUserExistsWithAdminClient(UserRepresentation user) {
+ assertNotNull(findUserByUsername(testRealmResource(), user.getUsername()));
+ }
+
+ public void assertUserDoesntExistWithAdminClient(UserRepresentation user) {
+ assertNull(findUserByUsername(testRealmResource(), user.getUsername()));
+ }
+
+ public void assertMessageAttributeMissing(String attributeName) {
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .contains("Please specify " + attributeName + "."));
+ }
+
+ @Test
+ public void successfulRegistration() {
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUser);
+ }
+
+ @Test
+ public void invalidEmail() {
+ newUser.setEmail("invalid.email.value");
+ testRealmRegistrationPage.register(newUser);
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .equals("Invalid email address."));
+ assertUserDoesntExistWithAdminClient(newUser);
+ }
+
+ @Test
+ public void emptyAttributes() {
+ UserRepresentation newUserEmpty = new UserRepresentation(); // empty user attributes
+
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("username");
+
+ newUserEmpty.setUsername(newUser.getUsername());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("first name");
+
+ newUserEmpty.setFirstName(newUser.getFirstName());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("last name");
+
+ newUserEmpty.setLastName(newUser.getLastName());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("email");
+
+ newUserEmpty.setEmail(newUser.getEmail());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("password");
+
+ setPasswordFor(newUserEmpty, getPasswordOf(newUser));
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUserEmpty);
+ }
+
+ @Test
+ public void notMatchingPasswords() {
+ testRealmRegistrationPage.setValues(newUser, "not-matching-password");
+ testRealmRegistrationPage.submit();
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .equals("Password confirmation doesn't match."));
+
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUser);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java
new file mode 100644
index 00000000000..95bff4121dc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java
@@ -0,0 +1,101 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.login.ResetCredentials;
+import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
+import org.keycloak.testsuite.util.MailServer;
+import org.keycloak.testsuite.util.MailServerConfiguration;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class ResetCredentialsTest extends AbstractAccountManagementTest {
+
+ @Page
+ private ResetCredentials testRealmResetCredentialsPage;
+
+ private static boolean init = false;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmResetCredentialsPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeResetCredentials() {
+ // enable reset credentials and configure smpt server in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setSmtpServer(suiteContext.getSmtpServer());
+ testRealmRep.setResetPasswordAllowed(true);
+ testRealmResource().update(testRealmRep);
+
+ if (!init) {
+ init = true;
+ MailServer.start();
+ MailServer.createEmailAccount(testUser.getEmail(), "password");
+ }
+
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().forgotPassword();
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ MailServer.stop();
+ }
+
+ @Test
+ public void resetCredentialsWithEmail() {
+ testRealmResetCredentialsPage.resetCredentials(testUser.getEmail());
+ resetCredentialsAndLoginWithNewPassword();
+ }
+
+ @Test
+ public void resetCredentialsWithUsername() {
+ testRealmResetCredentialsPage.resetCredentials(testUser.getUsername());
+ resetCredentialsAndLoginWithNewPassword();
+ }
+
+ private void resetCredentialsAndLoginWithNewPassword() {
+ assertEquals("You should receive an email shortly with further instructions.",
+ testRealmResetCredentialsPage.getFeedbackText());
+
+ String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, testUser.getEmail(),
+ "Someone just requested to change your Test account's credentials.");
+
+ log.info("navigating to " + url);
+ driver.navigate().to(url);
+ assertCurrentUrlStartsWith(testRealmResetCredentialsPage);
+ testRealmResetCredentialsPage.updatePassword("newPassword");
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login("test", "newPassword");
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java
new file mode 100644
index 00000000000..59e08cc80c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.login.VerifyEmail;
+import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
+import org.keycloak.testsuite.util.MailServer;
+import org.keycloak.testsuite.util.MailServerConfiguration;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class VerifyEmailTest extends AbstractAccountManagementTest {
+
+ @Page
+ private VerifyEmail testRealmVerifyEmailPage;
+
+ private static boolean init = false;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmVerifyEmailPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeVerifyEmail() {
+ // enable verify email and configure smpt server in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setSmtpServer(suiteContext.getSmtpServer());
+ testRealmRep.setVerifyEmail(true);
+ testRealmResource().update(testRealmRep);
+
+ if (!init) {
+ init = true;
+ MailServer.start();
+ MailServer.createEmailAccount(testUser.getEmail(), "password");
+ }
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ MailServer.stop();
+ }
+
+ @Test
+ public void verifyEmail() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+
+ assertEquals("You need to verify your email address to activate your account.",
+ testRealmVerifyEmailPage.getFeedbackText());
+
+ String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, testUser.getEmail(),
+ "Someone has created a Test account with this email address.");
+
+ log.info("navigating to " + url);
+ driver.navigate().to(url);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
new file mode 100644
index 00000000000..73ea7a670e2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
@@ -0,0 +1,139 @@
+package org.keycloak.testsuite.adapter;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.arquillian.ContainersTestEnricher;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer
+public abstract class AbstractAdapterTest extends AbstractAuthTest {
+
+ @Page
+ protected AppServerContextRoot appServerContextRootPage;
+
+ public static final String JBOSS_DEPLOYMENT_STRUCTURE_XML = "jboss-deployment-structure.xml";
+ public static final URL jbossDeploymentStructure = AbstractServletsAdapterTest.class
+ .getResource("/adapter-test/" + JBOSS_DEPLOYMENT_STRUCTURE_XML);
+ public static final String TOMCAT_CONTEXT_XML = "context.xml";
+ public static final URL tomcatContext = AbstractServletsAdapterTest.class
+ .getResource("/adapter-test/" + TOMCAT_CONTEXT_XML);
+
+ @Override
+ public void addTestRealms(List testRealms) {
+ addAdapterTestRealms(testRealms);
+ for (RealmRepresentation tr : testRealms) {
+ log.info("Setting redirect-uris in test realm '" + tr.getRealm() + "' as " + (isRelative() ? "" : "non-") + "relative");
+
+ modifyClientRedirectUris(tr, "http://localhost:8080", "");
+ modifyClientUrls(tr, "http://localhost:8080", "");
+
+ if (isRelative()) {
+ modifyClientRedirectUris(tr, appServerContextRootPage.toString(), "");
+ modifyClientUrls(tr, appServerContextRootPage.toString(), "");
+ modifyClientWebOrigins(tr, "8080", System.getProperty("auth.server.http.port", null));
+ } else {
+ modifyClientRedirectUris(tr, "^(/.*/\\*)", appServerContextRootPage.toString() + "$1");
+ modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
+ }
+ }
+ }
+
+ public abstract void addAdapterTestRealms(List testRealms);
+
+ public boolean isRelative() {
+ return ContainersTestEnricher.isRelative(this.getClass());
+ }
+
+ protected void modifyClientRedirectUris(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ List redirectUris = client.getRedirectUris();
+ if (redirectUris != null) {
+ List newRedirectUris = new ArrayList<>();
+ for (String uri : redirectUris) {
+ newRedirectUris.add(uri.replaceAll(regex, replacement));
+ }
+ client.setRedirectUris(newRedirectUris);
+ }
+ }
+ }
+
+ protected void modifyClientUrls(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ String baseUrl = client.getBaseUrl();
+ if (baseUrl != null) {
+ client.setBaseUrl(baseUrl.replaceAll(regex, replacement));
+ }
+ String adminUrl = client.getAdminUrl();
+ if (adminUrl != null) {
+ client.setAdminUrl(adminUrl.replaceAll(regex, replacement));
+ }
+ }
+ }
+
+ protected void modifyClientWebOrigins(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ List webOrigins = client.getWebOrigins();
+ if (webOrigins != null) {
+ List newWebOrigins = new ArrayList<>();
+ for (String uri : webOrigins) {
+ newWebOrigins.add(uri.replaceAll(regex, replacement));
+ }
+ client.setWebOrigins(newWebOrigins);
+ }
+ }
+ }
+
+ /**
+ * Modifies baseUrl, adminUrl and redirectUris for client based on real
+ * deployment url of the app.
+ *
+ * @param realm
+ * @param clientId
+ * @param deploymentUrl
+ */
+ protected void fixClientUrisUsingDeploymentUrl(RealmRepresentation realm, String clientId, String deploymentUrl) {
+ for (ClientRepresentation client : realm.getClients()) {
+ if (clientId.equals(client.getClientId())) {
+ if (client.getBaseUrl() != null) {
+ client.setBaseUrl(deploymentUrl);
+ }
+ if (client.getAdminUrl() != null) {
+ client.setAdminUrl(deploymentUrl);
+ }
+ List redirectUris = client.getRedirectUris();
+ if (redirectUris != null) {
+ List newRedirectUris = new ArrayList<>();
+ for (String uri : redirectUris) {
+ newRedirectUris.add(deploymentUrl + "/*");
+ }
+ client.setRedirectUris(newRedirectUris);
+ }
+ }
+ }
+ }
+
+ public static void addContextXml(Archive archive, String contextPath) {
+ try {
+ String contextXmlContent = IOUtils.toString(tomcatContext.openStream())
+ .replace("%CONTEXT_PATH%", contextPath);
+ archive.add(new StringAsset(contextXmlContent), "/META-INF/context.xml");
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
new file mode 100644
index 00000000000..defb866822e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
@@ -0,0 +1,62 @@
+package org.keycloak.testsuite.adapter;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.IOUtils;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
+
+ public static final String EXAMPLES_HOME;
+ public static final String EXAMPLES_VERSION_SUFFIX;
+ public static final String EXAMPLES_HOME_DIR;
+ public static final String EXAMPLES_WEB_XML;
+
+ static {
+ EXAMPLES_HOME = System.getProperty("examples.home", null);
+ Assert.assertNotNull("Property ${examples.home} must bet set.", EXAMPLES_HOME);
+ System.out.println(EXAMPLES_HOME);
+
+ EXAMPLES_VERSION_SUFFIX = System.getProperty("examples.version.suffix", null);
+ Assert.assertNotNull("Property ${examples.version.suffix} must bet set.", EXAMPLES_VERSION_SUFFIX);
+ System.out.println(EXAMPLES_VERSION_SUFFIX);
+
+ EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+
+ EXAMPLES_WEB_XML = EXAMPLES_HOME + "/web.xml";
+ }
+
+ protected static WebArchive exampleDeployment(String name) throws IOException {
+ return ShrinkWrap.createFromZipFile(WebArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".war"))
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
+ }
+
+ protected static WebArchive exampleDeployment(String name, String contextPath) throws IOException {
+ URL webXML = Paths.get(EXAMPLES_WEB_XML).toUri().toURL();
+ String webXmlContent = IOUtils.toString(webXML.openStream())
+ .replace("%CONTEXT_PATH%", contextPath);
+ WebArchive webArchive = ShrinkWrap.createFromZipFile(WebArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".war"))
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML)
+ .add(new StringAsset(webXmlContent), "/WEB-INF/web.xml");
+ return webArchive;
+ }
+
+ protected static JavaArchive exampleJarDeployment(String name) {
+ return ShrinkWrap.createFromZipFile(JavaArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".jar"));
+ }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
new file mode 100644
index 00000000000..7dc58c0a3d2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -0,0 +1,45 @@
+package org.keycloak.testsuite.adapter;
+
+import java.net.URL;
+import java.util.List;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.*;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+
+public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
+
+ protected static WebArchive servletDeployment(String name, Class... servletClasses) {
+ return servletDeployment(name, "keycloak.json", servletClasses);
+ }
+
+ protected static WebArchive servletDeployment(String name, String adapterConfig, Class... servletClasses) {
+ String webInfPath = "/adapter-test/" + name + "/WEB-INF/";
+
+ URL keycloakJSON = AbstractServletsAdapterTest.class.getResource(webInfPath + adapterConfig);
+ URL webXML = AbstractServletsAdapterTest.class.getResource(webInfPath + "web.xml");
+
+ WebArchive deployment = ShrinkWrap.create(WebArchive.class, name + ".war")
+ .addClasses(servletClasses)
+ .addAsWebInfResource(webXML, "web.xml")
+ .addAsWebInfResource(keycloakJSON, "keycloak.json")
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
+
+ addContextXml(deployment, name);
+
+ return deployment;
+ }
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ testRealms.add(loadRealm("/adapter-test/demorealm.json"));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java
new file mode 100644
index 00000000000..8a592d9bb4d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java
@@ -0,0 +1,68 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import org.keycloak.testsuite.adapter.page.BasicAuthExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
+
+public abstract class AbstractBasicAuthExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private BasicAuthExample basicAuthExample;
+
+ @Deployment(name = BasicAuthExample.DEPLOYMENT_NAME)
+ private static WebArchive basicAuthExample() throws IOException {
+ return exampleDeployment("examples-basicauth");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ testRealms.add(loadRealm(new File(EXAMPLES_HOME_DIR + "/basic-auth/basicauthrealm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(EXAMPLE);
+ }
+
+ @Test
+ public void testBasicAuthExample() {
+ String value = "hello";
+ Client client = ClientBuilder.newClient();
+
+ Response response = client.target(basicAuthExample
+ .setTemplateValues("admin", "password", value).buildUri()).request().get();
+ assertEquals(200, response.getStatus());
+ assertEquals(value, response.readEntity(String.class));
+ response.close();
+
+ response = client.target(basicAuthExample
+ .setTemplateValues("invalid-user", "password", value).buildUri()).request().get();
+ assertEquals(401, response.getStatus());
+ assertTrue(response.readEntity(String.class).contains("Unauthorized"));
+ response.close();
+
+ response = client.target(basicAuthExample
+ .setTemplateValues("admin", "invalid-password", value).buildUri()).request().get();
+ assertEquals(401, response.getStatus());
+ assertTrue(response.readEntity(String.class).contains("Unauthorized"));
+ response.close();
+
+ client.close();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java
new file mode 100644
index 00000000000..72db4bb1637
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java
@@ -0,0 +1,105 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.adapter.page.AngularCorsProductExample;
+import org.keycloak.testsuite.adapter.page.CorsDatabaseServiceExample;
+import org.keycloak.testsuite.arquillian.jira.Jira;
+import org.keycloak.testsuite.auth.page.account.Account;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ * Created by fkiss.
+ */
+public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ public static final String CORS = "cors";
+
+ @Page
+ private AngularCorsProductExample angularCorsProductExample;
+
+ @Page
+ private Account testRealmAccount;
+
+ @Deployment(name = AngularCorsProductExample.DEPLOYMENT_NAME)
+ private static WebArchive angularCorsProductExample() throws IOException {
+ return exampleDeployment(AngularCorsProductExample.DEPLOYMENT_NAME, "angular-cors-product");
+ }
+
+ @Deployment(name = CorsDatabaseServiceExample.DEPLOYMENT_NAME)
+ private static WebArchive corsDatabaseServiceExample() throws IOException {
+ return exampleDeployment("database-service");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ testRealms.add(
+ loadRealm(new File(EXAMPLES_HOME_DIR + "/cors/cors-realm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(CORS);
+ testRealmLoginPage.setAuthRealm(CORS);
+ testRealmAccount.setAuthRealm(CORS);
+ }
+
+ @Before
+ public void beforeDemoExampleTest() {
+ angularCorsProductExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ }
+
+ @Jira("KEYCLOAK-1546")
+ @Test
+ public void angularCorsProductTest() {
+ angularCorsProductExample.navigateTo();
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(angularCorsProductExample);
+ angularCorsProductExample.reloadData();
+ Assert.assertTrue(driver.getPageSource().contains("Product Listing"));
+ Assert.assertTrue(driver.getPageSource().contains("iphone"));
+ Assert.assertTrue(driver.getPageSource().contains("ipad"));
+ Assert.assertTrue(driver.getPageSource().contains("ipod"));
+
+ angularCorsProductExample.loadRoles();
+ Assert.assertTrue(driver.getPageSource().contains("Role Listing"));
+ Assert.assertTrue(driver.getPageSource().contains("user"));
+
+ angularCorsProductExample.addRole();
+ Assert.assertTrue(driver.getPageSource().contains("stuff"));
+
+ angularCorsProductExample.deleteRole();
+ Assert.assertFalse(driver.getPageSource().contains("stuff"));
+
+ angularCorsProductExample.loadAvailableSocialProviders();
+ Assert.assertTrue(driver.getPageSource().contains("Available social providers"));
+ Assert.assertTrue(driver.getPageSource().contains("twitter"));
+ Assert.assertTrue(driver.getPageSource().contains("google"));
+ Assert.assertTrue(driver.getPageSource().contains("linkedin"));
+ Assert.assertTrue(driver.getPageSource().contains("facebook"));
+ Assert.assertTrue(driver.getPageSource().contains("stackoverflow"));
+ Assert.assertTrue(driver.getPageSource().contains("github"));
+
+ angularCorsProductExample.loadPublicRealmInfo();
+ Assert.assertTrue(driver.getPageSource().contains("Realm name: cors"));
+
+ angularCorsProductExample.loadVersion();
+ Assert.assertTrue(driver.getPageSource().contains("Keycloak version: "));
+
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
new file mode 100644
index 00000000000..d31d390e48f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
@@ -0,0 +1,157 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.*;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account.Account;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.page.CustomerPortalExample;
+import org.keycloak.testsuite.adapter.page.DatabaseServiceExample;
+import org.keycloak.testsuite.adapter.page.ProductPortalExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import org.openqa.selenium.By;
+
+public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private CustomerPortalExample customerPortalExample;
+ @Page
+ private ProductPortalExample productPortalExample;
+ @Page
+ private DatabaseServiceExample databaseServiceExample;
+
+ @Page
+ private Account testRealmAccount;
+
+ @Deployment(name = CustomerPortalExample.DEPLOYMENT_NAME)
+ private static WebArchive customerPortalExample() throws IOException {
+ return exampleDeployment(CustomerPortalExample.DEPLOYMENT_NAME);
+ }
+
+ @Deployment(name = ProductPortalExample.DEPLOYMENT_NAME)
+ private static WebArchive productPortalExample() throws IOException {
+ return exampleDeployment(ProductPortalExample.DEPLOYMENT_NAME);
+ }
+
+ @Deployment(name = DatabaseServiceExample.DEPLOYMENT_NAME)
+ private static WebArchive databaseServiceExample() throws IOException {
+ return exampleDeployment("database-service");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ testRealms.add(
+ loadRealm(new File(EXAMPLES_HOME_DIR + "/preconfigured-demo/testrealm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ testRealmLoginPage.setAuthRealm(DEMO);
+ testRealmAccount.setAuthRealm(DEMO);
+ }
+
+ @Before
+ public void beforeDemoExampleTest() {
+ customerPortalExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ productPortalExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ }
+
+ @Test
+ public void customerPortalListingTest() {
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+ customerPortalExample.waitForCustomerListingHeader();
+
+ Assert.assertTrue(driver.getPageSource().contains("Username: bburke@redhat.com"));
+ Assert.assertTrue(driver.getPageSource().contains("Bill Burke"));
+ Assert.assertTrue(driver.getPageSource().contains("Stian Thorgersen"));
+ }
+
+ @Test
+ public void customerPortalSessionTest() {
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerSession();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+
+ customerPortalExample.waitForCustomerSessionHeader();
+ Assert.assertTrue(driver.getPageSource().contains("You visited this page"));
+ }
+
+ @Test
+ public void productPortalListingTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+
+ Assert.assertTrue(driver.getPageSource().contains("iphone"));
+ Assert.assertTrue(driver.getPageSource().contains("ipad"));
+ Assert.assertTrue(driver.getPageSource().contains("ipod"));
+
+ productPortalExample.goToCustomers();
+ }
+
+ @Test
+ public void goToProductPortalWithOneLoginTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+ productPortalExample.goToCustomers();
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+ customerPortalExample.customerListing();
+ customerPortalExample.goToProducts();
+ assertCurrentUrlStartsWith(productPortalExample);
+ }
+
+ @Test
+ public void logoutFromAllAppsTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+
+ productPortalExample.logOut();
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.productListing();
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerListing();
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ customerPortalExample.logOut();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java
new file mode 100644
index 00000000000..647b15bfade
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java
@@ -0,0 +1,131 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.util.List;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account.Account;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.adapter.AbstractExampleAdapterTest.EXAMPLES_HOME_DIR;
+import org.keycloak.testsuite.adapter.page.fuse.AdminInterface;
+import org.keycloak.testsuite.adapter.page.fuse.CustomerListing;
+import org.keycloak.testsuite.adapter.page.fuse.CustomerPortalFuseExample;
+import org.keycloak.testsuite.adapter.page.fuse.ProductPortalFuseExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractFuseExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ protected CustomerPortalFuseExample customerPortal;
+ @Page
+ protected CustomerListing customerListing;
+ @Page
+ protected AdminInterface adminInterface;
+
+ @Page
+ protected ProductPortalFuseExample productPortal;
+
+ @Page
+ protected Account testRealmAccount;
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ RealmRepresentation fureRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/fuse/testrealm.json"));
+ testRealms.add(fureRealm);
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ testRealmLoginPage.setAuthRealm(DEMO);
+ testRealmAccount.setAuthRealm(DEMO);
+ }
+
+ // no Arquillian deployments - examples already installed by maven
+
+ @Test
+ public void testCustomerListingAndAccountManagement() {
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickCustomerListingLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(customerListing);
+
+ String src = driver.getPageSource();
+ assertTrue(src.contains("Username: bburke@redhat.com")
+ && src.contains("Bill Burke")
+ && src.contains("Stian Thorgersen")
+ );
+
+ // account mgmt
+ customerListing.clickAccountManagement();
+
+ assertCurrentUrlStartsWith(testRealmAccount);
+ assertEquals(testRealmAccount.getUsername(), "bburke@redhat.com");
+
+ driver.navigate().back();
+ customerListing.clickLogOut();
+
+ // assert user not logged in
+ customerPortal.clickCustomerListingLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ }
+
+ @Test
+ public void testAdminInterface() {
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickAdminInterfaceLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("admin", "password");
+ assertCurrentUrlStartsWith(adminInterface);
+ assertTrue(driver.getPageSource().contains("Hello admin!"));
+
+ customerListing.navigateTo();
+ customerListing.clickLogOut();
+ pause(500);
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickAdminInterfaceLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(adminInterface);
+ assertTrue(driver.getPageSource().contains("Status code is 403"));
+ }
+
+ @Test
+ public void testProductPortal() {
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(productPortal);
+
+ assertTrue(productPortal.getProduct1UnsecuredText().contains("401: Unauthorized"));
+ assertTrue(productPortal.getProduct1SecuredText().contains("Product received: id=1"));
+ assertTrue(productPortal.getProduct2SecuredText().contains("Product received: id=2"));
+
+ productPortal.clickLogOutLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
new file mode 100644
index 00000000000..61d8efae506
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -0,0 +1,133 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.page.JSConsoleExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private JSConsoleExample jsConsoleExample;
+
+ public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
+
+ @Deployment(name = JSConsoleExample.DEPLOYMENT_NAME)
+ private static WebArchive jsConsoleExample() throws IOException {
+ return exampleDeployment(JSConsoleExample.CLIENT_ID);
+ }
+
+ @Override
+ public void addAdapterTestRealms(List testRealms) {
+ RealmRepresentation jsConsoleRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/js-console/example-realm.json"));
+
+ fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
+ JSConsoleExample.CLIENT_ID, jsConsoleExample.buildUri().toASCIIString());
+
+ jsConsoleRealm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
+
+ testRealms.add(jsConsoleRealm);
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(EXAMPLE);
+ }
+
+ @Test
+ public void testJSConsoleAuth() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ pause(1000);
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "invalid-password");
+ assertCurrentUrlDoesntStartWith(jsConsoleExample);
+
+ testRealmLoginPage.form().login("invalid-user", "password");
+ assertCurrentUrlDoesntStartWith(jsConsoleExample);
+
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ pause(1000);
+
+ jsConsoleExample.logOut();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Init Success (Not Authenticated)"));
+ }
+
+ @Test
+ public void testRefreshToken() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ }
+
+ @Test
+ public void testRefreshTokenIfUnder30s() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.refreshTokenIfUnder30s();
+ assertTrue(driver.getPageSource().contains("Token not refreshed, valid for"));
+
+ pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
+
+ jsConsoleExample.refreshTokenIfUnder30s();
+ assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ }
+
+ @Test
+ public void testGetProfile() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.getProfile();
+ assertTrue(driver.getPageSource().contains("Failed to load profile"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.getProfile();
+ assertTrue(driver.getPageSource().contains("Failed to load profile"));
+ assertTrue(driver.getPageSource().contains("\"username\": \"user\""));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
new file mode 100644
index 00000000000..38e47b78c84
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -0,0 +1,386 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.Version;
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.page.CustomerDb;
+import org.keycloak.testsuite.adapter.page.CustomerDbErrorPage;
+import org.keycloak.testsuite.adapter.page.CustomerPortal;
+import org.keycloak.testsuite.adapter.page.InputPortal;
+import org.keycloak.testsuite.adapter.page.ProductPortal;
+import org.keycloak.testsuite.adapter.page.SecurePortal;
+import org.keycloak.testsuite.arquillian.jira.Jira;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.Time;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAdapterTest {
+
+ @Page
+ private CustomerPortal customerPortal;
+ @Page
+ private SecurePortal securePortal;
+ @Page
+ private CustomerDb customerDb;
+ @Page
+ private CustomerDbErrorPage customerDbErrorPage;
+ @Page
+ private ProductPortal productPortal;
+ @Page
+ private InputPortal inputPortal;
+
+ @Deployment(name = CustomerPortal.DEPLOYMENT_NAME)
+ protected static WebArchive customerPortal() {
+ return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class);
+ }
+
+ @Deployment(name = SecurePortal.DEPLOYMENT_NAME)
+ protected static WebArchive securePortal() {
+ return servletDeployment(SecurePortal.DEPLOYMENT_NAME, CallAuthenticatedServlet.class);
+ }
+
+ @Deployment(name = CustomerDb.DEPLOYMENT_NAME)
+ protected static WebArchive customerDb() {
+ return servletDeployment(CustomerDb.DEPLOYMENT_NAME, CustomerDatabaseServlet.class);
+ }
+
+ @Deployment(name = CustomerDbErrorPage.DEPLOYMENT_NAME)
+ protected static WebArchive customerDbErrorPage() {
+ return servletDeployment(CustomerDbErrorPage.DEPLOYMENT_NAME, CustomerDatabaseServlet.class, ErrorServlet.class);
+ }
+
+ @Deployment(name = ProductPortal.DEPLOYMENT_NAME)
+ protected static WebArchive productPortal() {
+ return servletDeployment(ProductPortal.DEPLOYMENT_NAME, ProductServlet.class);
+ }
+
+ @Deployment(name = InputPortal.DEPLOYMENT_NAME)
+ protected static WebArchive inputPortal() {
+ return servletDeployment(InputPortal.DEPLOYMENT_NAME, "keycloak.json", InputServlet.class);
+ }
+
+ @Test
+ public void testSavedPostRequest() throws InterruptedException {
+ // test login to customer-portal which does a bearer request to customer-db
+ inputPortal.navigateTo();
+ assertCurrentUrlEquals(inputPortal);
+ inputPortal.execute("hello");
+
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertEquals(driver.getCurrentUrl(), inputPortal + "/secured/post");
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("parameter=hello"));
+
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, customerPortal.toString())
+ .build("demo").toString();
+ driver.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ // test unsecured POST KEYCLOAK-901
+ Client client = ClientBuilder.newClient();
+ Form form = new Form();
+ form.param("parameter", "hello");
+ String text = client.target(inputPortal + "/unsecured").request().post(Entity.form(form), String.class);
+ assertTrue(text.contains("parameter=hello"));
+ client.close();
+ }
+
+ @Test
+ public void testLoginSSOAndLogout() {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ // test SSO
+ productPortal.navigateTo();
+ assertCurrentUrlEquals(productPortal);
+ pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
+
+ // View stats
+ List