keycloak-aplcache

Changes

Details

diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 5d9ecce..44b0e9b 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -62,6 +62,16 @@
             <artifactId>integration-arquillian-event-queue</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.icegreen</groupId>
+            <artifactId>greenmail</artifactId>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
     
     <build>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
index a7238c3..0c02dd8 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
@@ -38,6 +38,7 @@ import org.jboss.arquillian.test.spi.event.suite.BeforeClass;
 import org.jboss.arquillian.test.spi.event.suite.BeforeSuite;
 import org.jboss.logging.Logger;
 import org.keycloak.testsuite.util.LogChecker;
+import org.keycloak.testsuite.util.OAuthClient;
 
 /**
  *
@@ -76,6 +77,10 @@ public class AuthServerTestEnricher {
     @ClassScoped
     private InstanceProducer<TestContext> testContextProducer;
 
+    @Inject
+    @ClassScoped
+    private InstanceProducer<OAuthClient> oAuthClientProducer;
+
     public static String getAuthServerContextRoot() {
         return getAuthServerContextRoot(0);
     }
@@ -196,4 +201,9 @@ public class AuthServerTestEnricher {
         testContextProducer.set(testContext);
     }
 
+    public void initializeOAuthClient(@Observes(precedence = 3) BeforeClass event) {
+        OAuthClient oAuthClient = new OAuthClient();
+        oAuthClientProducer.set(oAuthClient);
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java
index ae07842..3ff7da9 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java
@@ -22,6 +22,7 @@ import org.jboss.arquillian.core.api.annotation.Inject;
 import org.jboss.arquillian.test.api.ArquillianResource;
 import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
 import org.keycloak.testsuite.util.DeleteMeOAuthClient;
+import org.keycloak.testsuite.util.OAuthClient;
 
 import java.lang.annotation.Annotation;
 
@@ -31,11 +32,11 @@ import java.lang.annotation.Annotation;
 public class OAuthClientProvider implements ResourceProvider {
 
     @Inject
-    Instance<DeleteMeOAuthClient> oauthClient;
+    Instance<OAuthClient> oauthClient;
 
     @Override
     public boolean canProvide(Class<?> type) {
-        return DeleteMeOAuthClient.class.isAssignableFrom(type);
+        return OAuthClient.class.isAssignableFrom(type);
     }
 
     @Override
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/LoginPasswordUpdatePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/LoginPasswordUpdatePage.java
new file mode 100644
index 0000000..a7d8706
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/LoginPasswordUpdatePage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.page;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginPasswordUpdatePage {
+
+    @Drone
+    protected WebDriver driver;
+
+    @FindBy(id = "password-new")
+    private WebElement newPasswordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement passwordConfirmInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    public void changePassword(String newPassword, String passwordConfirm) {
+        newPasswordInput.sendKeys(newPassword);
+        passwordConfirmInput.sendKeys(passwordConfirm);
+
+        submitButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Update password");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractAccountPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractAccountPage.java
new file mode 100755
index 0000000..042eec3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractAccountPage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractAccountPage extends AbstractPage {
+
+    @FindBy(linkText = "Sign Out")
+    private WebElement logoutLink;
+
+    @FindBy(id = "kc-current-locale-link")
+    private WebElement languageText;
+
+    @FindBy(id = "kc-locale-dropdown")
+    private WebElement localeDropdown;
+
+    public void logout() {
+        logoutLink.click();
+    }
+
+    public String getLanguageDropdownText() {
+        return languageText.getText();
+    }
+
+    public void openLanguage(String language){
+        localeDropdown.findElement(By.linkText(language)).click();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractPage.java
new file mode 100755
index 0000000..2b1f789
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AbstractPage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.Assert;
+import org.keycloak.common.util.KeycloakUriBuilder;
+import org.keycloak.testsuite.arquillian.SuiteContext;
+import org.openqa.selenium.WebDriver;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractPage {
+
+    @ArquillianResource
+    protected SuiteContext suiteContext;
+
+    @ArquillianResource
+    protected WebDriver driver;
+
+    public void assertCurrent() {
+        String name = getClass().getSimpleName();
+        Assert.assertTrue("Expected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")",
+                isCurrent());
+    }
+
+    protected URI getAuthServerRoot() {
+        try {
+            return KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getContextRoot().toURI()).path("/auth/").build();
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    abstract public boolean isCurrent();
+
+    abstract public void open() throws Exception;
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
new file mode 100644
index 0000000..11aac0b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.Urls;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AccountApplicationsPage extends AbstractAccountPage {
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/applications");
+    }
+
+    @Override
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    private String getPath() {
+        return Urls.accountApplicationsPage(getAuthServerRoot(), "test").toString();
+    }
+
+    public void revokeGrant(String clientId) {
+        driver.findElement(By.id("revoke-" + clientId)).click();
+    }
+
+    public Map<String, AppEntry> getApplications() {
+        Map<String, AppEntry> table = new HashMap<String, AppEntry>();
+        for (WebElement r : driver.findElements(By.tagName("tr"))) {
+            int count = 0;
+            AppEntry currentEntry = null;
+
+            for (WebElement col : r.findElements(By.tagName("td"))) {
+                count++;
+                switch (count) {
+                    case 1:
+                        currentEntry = new AppEntry();
+                        String client = col.getText();
+                        table.put(client, currentEntry);
+                        break;
+                    case 2:
+                        String rolesStr = col.getText();
+                        String[] roles = rolesStr.split(",");
+                        for (String role : roles) {
+                            role = role.trim();
+                            currentEntry.addAvailableRole(role);
+                        }
+                        break;
+                    case 3:
+                        rolesStr = col.getText();
+                        if (rolesStr.isEmpty()) break;
+                        roles = rolesStr.split(",");
+                        for (String role : roles) {
+                            role = role.trim();
+                            currentEntry.addGrantedRole(role);
+                        }
+                        break;
+                    case 4:
+                        String protMappersStr = col.getText();
+                        if (protMappersStr.isEmpty()) break;
+                        String[] protMappers = protMappersStr.split(",");
+                        for (String protMapper : protMappers) {
+                            protMapper = protMapper.trim();
+                            currentEntry.addMapper(protMapper);
+                        }
+                        break;
+                    case 5:
+                        String additionalGrant = col.getText();
+                        if (additionalGrant.isEmpty()) break;
+                        String[] grants = additionalGrant.split(",");
+                        for (String grant : grants) {
+                            grant = grant.trim();
+                            currentEntry.addAdditionalGrant(grant);
+                        }
+                        break;
+                }
+            }
+        }
+        table.remove("Application");
+        return table;
+    }
+
+    public static class AppEntry {
+
+        private final List<String> rolesAvailable = new ArrayList<String>();
+        private final List<String> rolesGranted = new ArrayList<String>();
+        private final List<String> protocolMappersGranted = new ArrayList<String>();
+        private final List<String> additionalGrants = new ArrayList<>();
+
+        private void addAvailableRole(String role) {
+            rolesAvailable.add(role);
+        }
+
+        private void addGrantedRole(String role) {
+            rolesGranted.add(role);
+        }
+
+        private void addMapper(String protocolMapper) {
+            protocolMappersGranted.add(protocolMapper);
+        }
+
+        private void addAdditionalGrant(String grant) {
+            additionalGrants.add(grant);
+        }
+
+        public List<String> getRolesGranted() {
+            return rolesGranted;
+        }
+
+        public List<String> getRolesAvailable() {
+            return rolesAvailable;
+        }
+
+        public List<String> getProtocolMappersGranted() {
+            return protocolMappersGranted;
+        }
+
+        public List<String> getAdditionalGrants() {
+            return additionalGrants;
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
new file mode 100644
index 0000000..ebd6773
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.Urls;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AccountFederatedIdentityPage extends AbstractAccountPage {
+
+    @FindBy(className = "alert-error")
+    private WebElement errorMessage;
+
+    public AccountFederatedIdentityPage() {};
+
+    private String realmName = "test";
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public void realm(String realmName) {
+        this.realmName = realmName;
+    }
+
+    public String getPath() {
+        return Urls.accountFederatedIdentityPage(getAuthServerRoot(), realmName).toString();
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Federated Identities");
+    }
+
+    public void clickAddProvider(String providerId) {
+        driver.findElement(By.id("add-" + providerId)).click();
+    }
+
+    public void clickRemoveProvider(String providerId) {
+        driver.findElement(By.id("remove-" + providerId)).click();
+    }
+
+    public String getError() {
+        return errorMessage.getText();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountLogPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountLogPage.java
new file mode 100755
index 0000000..9775d0c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountLogPage.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.Urls;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AccountLogPage extends AbstractAccountPage {
+
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/log");
+    }
+
+    private String getPath() {
+        return Urls.accountLogPage(getAuthServerRoot(), "test").toString();
+    }
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public List<List<String>> getEvents() {
+        List<List<String>> table = new LinkedList<List<String>>();
+        for (WebElement r : driver.findElements(By.tagName("tr"))) {
+            List<String> row = new LinkedList<String>();
+            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/pages/AccountPasswordPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountPasswordPage.java
new file mode 100755
index 0000000..2c98c55
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountPasswordPage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.resources.AccountService;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AccountPasswordPage extends AbstractAccountPage {
+
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(id = "password-new")
+    private WebElement newPasswordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement passwordConfirmInput;
+
+    @FindBy(className = "btn-primary")
+    private WebElement submitButton;
+
+    private String realmName = "test";
+
+    public void changePassword(String password, String newPassword, String passwordConfirm) {
+        passwordInput.sendKeys(password);
+        newPasswordInput.sendKeys(newPassword);
+        passwordConfirmInput.sendKeys(passwordConfirm);
+
+        submitButton.click();
+    }
+
+    public void changePassword(String newPassword, String passwordConfirm) {
+        newPasswordInput.sendKeys(newPassword);
+        passwordConfirmInput.sendKeys(passwordConfirm);
+
+        submitButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().split("\\?")[0].endsWith("/account/password");
+    }
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public void realm(String realmName) {
+        this.realmName = realmName;
+    }
+
+    public String getPath() {
+        return AccountService.passwordUrl(UriBuilder.fromUri(getAuthServerRoot())).build(this.realmName).toString();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountSessionsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountSessionsPage.java
new file mode 100755
index 0000000..d6c0485
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountSessionsPage.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.Urls;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AccountSessionsPage extends AbstractAccountPage {
+
+    private String realmName = "test";
+
+    @FindBy(id = "logout-all-sessions")
+    private WebElement logoutAllLink;
+
+
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/sessions");
+    }
+
+    public void realm(String realmName) {
+        this.realmName = realmName;
+    }
+
+    public String getPath() {
+        return Urls.accountSessionsPage(getAuthServerRoot(), realmName).toString();
+    }
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public void logoutAll() {
+        logoutAllLink.click();
+    }
+
+    public List<List<String>> getSessions() {
+        List<List<String>> table = new LinkedList<List<String>>();
+        for (WebElement r : driver.findElements(By.tagName("tr"))) {
+            List<String> row = new LinkedList<String>();
+            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/pages/AccountTotpPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountTotpPage.java
new file mode 100755
index 0000000..1029e10
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountTotpPage.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.resources.AccountService;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AccountTotpPage extends AbstractAccountPage {
+
+    @FindBy(id = "totpSecret")
+    private WebElement totpSecret;
+
+    @FindBy(id = "totp")
+    private WebElement totpInput;
+
+    @FindBy(css = "button[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(id = "remove-mobile")
+    private WebElement removeLink;
+
+    private String getPath() {
+        return AccountService.totpUrl(UriBuilder.fromUri(getAuthServerRoot())).build("test").toString();
+    }
+
+    public void configure(String totp) {
+        totpInput.sendKeys(totp);
+        submitButton.click();
+    }
+
+    public String getTotpSecret() {
+        return totpSecret.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().split("\\?")[0].endsWith("/account/totp");
+    }
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public void removeTotp() {
+        removeLink.click();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
new file mode 100755
index 0000000..833acc0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.services.resources.RealmsResource;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AccountUpdateProfilePage extends AbstractAccountPage {
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    @FindBy(id = "firstName")
+    private WebElement firstNameInput;
+
+    @FindBy(id = "lastName")
+    private WebElement lastNameInput;
+
+    @FindBy(id = "email")
+    private WebElement emailInput;
+
+
+    @FindBy(id = "referrer")
+    private WebElement backToApplicationLink;
+
+    @FindBy(css = "button[type=\"submit\"][value=\"Save\"]")
+    private WebElement submitButton;
+
+    @FindBy(css = "button[type=\"submit\"][value=\"Cancel\"]")
+    private WebElement cancelButton;
+
+    @FindBy(className = "alert-success")
+    private WebElement successMessage;
+
+    @FindBy(className = "alert-error")
+    private WebElement errorMessage;
+
+    private String getPath() {
+        return RealmsResource.accountUrl(UriBuilder.fromUri(getAuthServerRoot())).build("test").toString();
+    }
+
+    public void updateProfile(String firstName, String lastName, String email) {
+        firstNameInput.clear();
+        firstNameInput.sendKeys(firstName);
+        lastNameInput.clear();
+        lastNameInput.sendKeys(lastName);
+        emailInput.clear();
+        emailInput.sendKeys(email);
+
+        submitButton.click();
+    }
+
+    public void updateProfile(String username, String firstName, String lastName, String email) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+        firstNameInput.clear();
+        firstNameInput.sendKeys(firstName);
+        lastNameInput.clear();
+        lastNameInput.sendKeys(lastName);
+        emailInput.clear();
+        emailInput.sendKeys(email);
+
+        submitButton.click();
+    }
+
+    public void updateUsername(String username) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+        submitButton.click();
+    }
+
+    public void updateEmail(String email) {
+        emailInput.clear();
+        emailInput.sendKeys(email);
+        submitButton.click();
+    }
+
+    public void clickCancel() {
+        cancelButton.click();
+    }
+
+
+    public String getUsername() {
+        return usernameInput.getAttribute("value");
+    }
+
+    public String getFirstName() {
+        return firstNameInput.getAttribute("value");
+    }
+
+    public String getLastName() {
+        return lastNameInput.getAttribute("value");
+    }
+
+    public String getEmail() {
+        return emailInput.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Edit Account");
+    }
+
+    public void open() {
+        driver.navigate().to(getPath());
+    }
+
+    public void backToApplication() {
+        backToApplicationLink.click();
+    }
+
+    public String getSuccess(){
+        return successMessage.getText();
+    }
+
+    public String getError() {
+        return errorMessage.getText();
+    }
+
+    public boolean isPasswordUpdateSupported() {
+        return driver.getPageSource().contains(getPath() + "/password");
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppPage.java
new file mode 100755
index 0000000..d3122ff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppPage.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.keycloak.OAuth2Constants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AppPage extends AbstractPage {
+
+    public static final String AUTH_SERVER_URL = "http://localhost:8081/auth";
+    public static final String baseUrl = "http://localhost:8081/app";
+
+    @FindBy(id = "account")
+    private WebElement accountLink;
+
+    @Override
+    public void open() {
+        driver.navigate().to(baseUrl);
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getCurrentUrl().startsWith(baseUrl);
+    }
+
+    public RequestType getRequestType() {
+        return RequestType.valueOf(driver.getTitle());
+    }
+
+    public void openAccount() {
+        accountLink.click();
+    }
+
+    public enum RequestType {
+        AUTH_RESPONSE, LOGOUT_REQUEST, APP_REQUEST
+    }
+
+    public void logout() {
+        String logoutUri = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(AUTH_SERVER_URL))
+                .queryParam(OAuth2Constants.REDIRECT_URI,baseUrl).build("test").toString();
+        driver.navigate().to(logoutUri);
+
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/BypassKerberosPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/BypassKerberosPage.java
new file mode 100755
index 0000000..b642262
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/BypassKerberosPage.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class BypassKerberosPage extends AbstractPage {
+
+    @FindBy(name = "continue")
+    private WebElement continueButton;
+
+   public boolean isCurrent() {
+        return driver.getTitle().equals("Log in to test") || driver.getTitle().equals("Anmeldung bei test");
+    }
+
+    public void clickContinue() {
+        continueButton.click();
+    }
+
+    @Override
+    public void open() throws Exception {
+
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ErrorPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ErrorPage.java
new file mode 100644
index 0000000..5b4a116
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ErrorPage.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ErrorPage extends AbstractPage {
+
+    @ArquillianResource
+    protected OAuthClient oauth;
+
+    @FindBy(className = "instruction")
+    private WebElement errorMessage;
+
+    @FindBy(id = "backToApplication")
+    private WebElement backToApplicationLink;
+
+    public String getError() {
+        return errorMessage.getText();
+    }
+
+    public void clickBackToApplication() {
+        backToApplicationLink.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle() != null && driver.getTitle().equals("We're sorry...");
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
new file mode 100644
index 0000000..db64eff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpConfirmLinkPage extends AbstractPage {
+
+    @FindBy(id = "updateProfile")
+    private WebElement updateProfileButton;
+
+    @FindBy(id = "linkAccount")
+    private WebElement linkAccountButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement message;
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Account already exists");
+    }
+
+    public String getMessage() {
+        return message.getText();
+    }
+
+    public void clickReviewProfile() {
+        updateProfileButton.click();
+    }
+
+    public void clickLinkAccount() {
+        linkAccountButton.click();
+    }
+
+    @Override
+    public void open() throws Exception {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
new file mode 100644
index 0000000..8ed8461
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpLinkEmailPage extends AbstractPage {
+
+    @FindBy(id = "instruction1")
+    private WebElement message;
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().startsWith("Link ");
+    }
+
+    @Override
+    public void open() throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getMessage() {
+        return message.getText();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/InfoPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/InfoPage.java
new file mode 100644
index 0000000..df8f1d0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/InfoPage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class InfoPage extends AbstractPage {
+
+    @ArquillianResource
+    protected OAuthClient oauth;
+
+    @FindBy(className = "instruction")
+    private WebElement infoMessage;
+
+    public String getInfo() {
+        return infoMessage.getText();
+    }
+
+    public boolean isCurrent() {
+        return driver.getPageSource().contains("kc-info-message");
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java
new file mode 100755
index 0000000..da289f8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginConfigTotpPage extends AbstractPage {
+
+    @FindBy(id = "totpSecret")
+    private WebElement totpSecret;
+
+    @FindBy(id = "totp")
+    private WebElement totpInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    public void configure(String totp) {
+        totpInput.sendKeys(totp);
+        submitButton.click();
+    }
+
+    public String getTotpSecret() {
+        return totpSecret.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Mobile Authenticator Setup");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java
new file mode 100755
index 0000000..fcbdb2c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginPage extends AbstractPage {
+
+    @ArquillianResource
+    protected OAuthClient oauth;
+    
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(id = "totp")
+    private WebElement totp;
+
+    @FindBy(id = "rememberMe")
+    private WebElement rememberMe;
+
+    @FindBy(name = "login")
+    private WebElement submitButton;
+
+    @FindBy(name = "cancel")
+    private WebElement cancelButton;
+
+    @FindBy(linkText = "Register")
+    private WebElement registerLink;
+
+    @FindBy(linkText = "Forgot Password?")
+    private WebElement resetPasswordLink;
+
+    @FindBy(linkText = "Username")
+    private WebElement recoverUsernameLink;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    @FindBy(className = "alert-warning")
+    private WebElement loginWarningMessage;
+
+    @FindBy(className = "alert-success")
+    private WebElement loginSuccessMessage;
+
+
+    @FindBy(className = "alert-info")
+    private WebElement loginInfoMessage;
+
+
+    @FindBy(id = "kc-current-locale-link")
+    private WebElement languageText;
+
+    @FindBy(id = "kc-locale-dropdown")
+    private WebElement localeDropdown;
+
+    public void login(String username, String password) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+
+        passwordInput.clear();
+        passwordInput.sendKeys(password);
+
+        submitButton.click();
+    }
+
+    public void login(String password) {
+        passwordInput.clear();
+        passwordInput.sendKeys(password);
+
+        submitButton.click();
+    }
+
+    public void missingPassword(String username) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+        passwordInput.clear();
+        submitButton.click();
+
+    }
+    public void missingUsername() {
+        usernameInput.clear();
+        submitButton.click();
+
+    }
+
+    public String getUsername() {
+        return usernameInput.getAttribute("value");
+    }
+
+    public boolean isUsernameInputEnabled() {
+        return usernameInput.isEnabled();
+    }
+
+    public String getPassword() {
+        return passwordInput.getAttribute("value");
+    }
+
+    public void cancel() {
+        cancelButton.click();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+    public String getSuccessMessage() {
+        return loginSuccessMessage != null ? loginSuccessMessage.getText() : null;
+    }
+    public String getInfoMessage() {
+        return loginInfoMessage != null ? loginInfoMessage.getText() : null;
+    }
+
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Log in to test") || driver.getTitle().equals("Anmeldung bei test");
+    }
+
+    public void clickRegister() {
+        registerLink.click();
+    }
+
+    public void clickSocial(String providerId) {
+        WebElement socialButton = findSocialButton(providerId);
+        socialButton.click();
+    }
+
+    public WebElement findSocialButton(String providerId) {
+        String id = "zocial-" + providerId;
+        return this.driver.findElement(By.id(id));
+    }
+
+    public void resetPassword() {
+        resetPasswordLink.click();
+    }
+
+    public void recoverUsername() {
+        recoverUsernameLink.click();
+    }
+
+    public void setRememberMe(boolean enable) {
+        boolean current = rememberMe.isSelected();
+        if (current != enable) {
+            rememberMe.click();
+        }
+    }
+
+    public boolean isRememberMeChecked() {
+        return rememberMe.isSelected();
+    }
+
+    @Override
+    public void open() {
+        oauth.openLoginForm();
+        assertCurrent();
+    }
+
+    public String getLanguageDropdownText() {
+        return languageText.getText();
+    }
+
+    public void openLanguage(String language){
+        localeDropdown.findElement(By.linkText(language)).click();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java
new file mode 100644
index 0000000..7d63b66
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginPasswordResetPage extends AbstractPage {
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-success")
+    private WebElement emailSuccessMessage;
+
+    @FindBy(className = "alert-error")
+    private WebElement emailErrorMessage;
+
+    @FindBy(partialLinkText = "Back to Login")
+    private WebElement backToLogin;
+
+    public void changePassword(String username) {
+        usernameInput.sendKeys(username);
+
+        submitButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Forgot Your Password?");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getSuccessMessage() {
+        return emailSuccessMessage != null ? emailSuccessMessage.getText() : null;
+    }
+
+    public String getErrorMessage() {
+        return emailErrorMessage != null ? emailErrorMessage.getText() : null;
+    }
+
+    public void backToLogin() {
+        backToLogin.click();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
new file mode 100644
index 0000000..93d203d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginPasswordUpdatePage extends AbstractPage {
+
+    @FindBy(id = "password-new")
+    private WebElement newPasswordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement passwordConfirmInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    public void changePassword(String newPassword, String passwordConfirm) {
+        newPasswordInput.sendKeys(newPassword);
+        passwordConfirmInput.sendKeys(passwordConfirm);
+
+        submitButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Update password");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java
new file mode 100644
index 0000000..59b3ebc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginRecoverUsernamePage.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginRecoverUsernamePage extends AbstractPage {
+
+    @FindBy(id = "email")
+    private WebElement emailInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement emailErrorMessage;
+
+    public void recoverUsername(String email) {
+        emailInput.sendKeys(email);
+
+        submitButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Forgot Your Username?");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getMessage() {
+        return emailErrorMessage != null ? emailErrorMessage.getText() : null;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java
new file mode 100755
index 0000000..f58540b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginTotpPage extends AbstractPage {
+
+    @FindBy(id = "totp")
+    private WebElement totpInput;
+
+    @FindBy(id = "password-token")
+    private WebElement passwordToken;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(id = "kc-cancel")
+    private WebElement cancelButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    public void login(String totp) {
+        totpInput.clear();
+        if (totp != null) totpInput.sendKeys(totp);
+
+        submitButton.click();
+    }
+
+    public void cancel() {
+        cancelButton.click();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+    public boolean isCurrent() {
+        if (driver.getTitle().startsWith("Log in to ")) {
+            try {
+                driver.findElement(By.id("totp"));
+                return true;
+            } catch (Throwable t) {
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java
new file mode 100644
index 0000000..6acce4d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class LoginUpdateProfileEditUsernameAllowedPage extends LoginUpdateProfilePage {
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    public void update(String firstName, String lastName, String email, String username) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+        update(firstName, lastName, email);
+    }
+
+    public String getUsername() {
+        return usernameInput.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Update Account Information");
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
new file mode 100644
index 0000000..704060b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginUpdateProfilePage extends AbstractPage {
+
+    @FindBy(id = "firstName")
+    private WebElement firstNameInput;
+
+    @FindBy(id = "lastName")
+    private WebElement lastNameInput;
+
+    @FindBy(id = "email")
+    private WebElement emailInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    public void update(String firstName, String lastName, String email) {
+        firstNameInput.clear();
+        firstNameInput.sendKeys(firstName);
+        lastNameInput.clear();
+        lastNameInput.sendKeys(lastName);
+        emailInput.clear();
+        emailInput.sendKeys(email);
+        submitButton.click();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+    public String getFirstName() {
+        return firstNameInput.getAttribute("value");
+    }
+
+    public String getLastName() {
+        return lastNameInput.getAttribute("value");
+    }
+
+    public String getEmail() {
+        return emailInput.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Update Account Information");
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
new file mode 100755
index 0000000..1a550ec
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthGrantPage extends AbstractPage {
+
+    @FindBy(css = "input[name=\"accept\"]")
+    private WebElement acceptButton;
+    @FindBy(css = "input[name=\"cancel\"]")
+    private WebElement cancelButton;
+
+
+    public void accept(){
+        acceptButton.click();
+    }
+
+    public void cancel(){
+        cancelButton.click();
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Grant Access");
+    }
+
+    @Override
+    public void open() {
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/RegisterPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/RegisterPage.java
new file mode 100644
index 0000000..810ba84
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/RegisterPage.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.junit.Assert;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RegisterPage extends AbstractPage {
+
+    @FindBy(id = "firstName")
+    private WebElement firstNameInput;
+
+    @FindBy(id = "lastName")
+    private WebElement lastNameInput;
+
+    @FindBy(id = "email")
+    private WebElement emailInput;
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement passwordConfirmInput;
+
+    @FindBy(css = "input[type=\"submit\"]")
+    private WebElement submitButton;
+
+    @FindBy(className = "alert-error")
+    private WebElement loginErrorMessage;
+
+    @FindBy(className = "instruction")
+    private WebElement loginInstructionMessage;
+
+
+    public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm) {
+        firstNameInput.clear();
+        if (firstName != null) {
+            firstNameInput.sendKeys(firstName);
+        }
+
+        lastNameInput.clear();
+        if (lastName != null) {
+            lastNameInput.sendKeys(lastName);
+        }
+
+        emailInput.clear();
+        if (email != null) {
+            emailInput.sendKeys(email);
+        }
+
+        usernameInput.clear();
+        if (username != null) {
+            usernameInput.sendKeys(username);
+        }
+
+        passwordInput.clear();
+        if (password != null) {
+            passwordInput.sendKeys(password);
+        }
+
+        passwordConfirmInput.clear();
+        if (passwordConfirm != null) {
+            passwordConfirmInput.sendKeys(passwordConfirm);
+        }
+
+        submitButton.click();
+    }
+
+    public void registerWithEmailAsUsername(String firstName, String lastName, String email, String password, String passwordConfirm) {
+        firstNameInput.clear();
+        if (firstName != null) {
+            firstNameInput.sendKeys(firstName);
+        }
+
+        lastNameInput.clear();
+        if (lastName != null) {
+            lastNameInput.sendKeys(lastName);
+        }
+
+        emailInput.clear();
+        if (email != null) {
+            emailInput.sendKeys(email);
+        }
+
+        try {
+            usernameInput.clear();
+            Assert.fail("Form must be without username field");
+        } catch (NoSuchElementException e) {
+            // OK
+        }
+
+        passwordInput.clear();
+        if (password != null) {
+            passwordInput.sendKeys(password);
+        }
+
+        passwordConfirmInput.clear();
+        if (passwordConfirm != null) {
+            passwordConfirmInput.sendKeys(passwordConfirm);
+        }
+
+        submitButton.click();
+    }
+
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
+    public String getInstruction() {
+        try {
+            return loginInstructionMessage != null ? loginInstructionMessage.getText() : null;
+        } catch (NoSuchElementException e){
+            // OK
+        }
+        return null;
+    }
+
+    public String getFirstName() {
+        return firstNameInput.getAttribute("value");
+    }
+
+    public String getLastName() {
+        return lastNameInput.getAttribute("value");
+    }
+
+    public String getEmail() {
+        return emailInput.getAttribute("value");
+    }
+
+    public String getUsername() {
+        return usernameInput.getAttribute("value");
+    }
+
+    public String getPassword() {
+        return passwordInput.getAttribute("value");
+    }
+
+    public String getPasswordConfirm() {
+        return passwordConfirmInput.getAttribute("value");
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Register with test");
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/TermsAndConditionsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/TermsAndConditionsPage.java
new file mode 100755
index 0000000..e898176
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/TermsAndConditionsPage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TermsAndConditionsPage extends AbstractPage {
+
+    @FindBy(id = "kc-accept")
+    private WebElement submitButton;
+
+    @FindBy(id = "kc-decline")
+    private WebElement cancelButton;
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Terms and Conditions");
+    }
+
+    public void acceptTerms() {
+        submitButton.click();
+    }
+    public void declineTerms() {
+        cancelButton.click();
+    }
+
+    @Override
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java
new file mode 100755
index 0000000..1f0d3af
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/ValidatePassworrdEmailResetPage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ValidatePassworrdEmailResetPage extends AbstractPage {
+
+    @FindBy(id = "key")
+    private WebElement keyInput;
+
+    @FindBy(id="kc-submit")
+    private WebElement submitButton;
+
+    @FindBy(id="kc-cancel")
+    private WebElement cancelButton;
+
+    @FindBy(className = "alert-success")
+    private WebElement emailSuccessMessage;
+
+    @FindBy(className = "alert-error")
+    private WebElement emailErrorMessage;
+
+    public void submitCode(String code) {
+        keyInput.sendKeys(code);
+
+        submitButton.click();
+    }
+
+    public void cancel() {
+        cancelButton.click();
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Forgot Your Password?");
+    }
+
+    public void open() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getSuccessMessage() {
+        return emailSuccessMessage != null ? emailSuccessMessage.getText() : null;
+    }
+
+    public String getErrorMessage() {
+        return emailErrorMessage != null ? emailErrorMessage.getText() : null;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/VerifyEmailPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/VerifyEmailPage.java
new file mode 100644
index 0000000..5a969c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/VerifyEmailPage.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
+ */
+public class VerifyEmailPage extends AbstractPage {
+
+    @ArquillianResource
+    protected OAuthClient oauth;
+
+    @FindBy(linkText = "Click here")
+    private WebElement resendEmailLink;
+
+    @Override
+    public void open() {
+    }
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Email verification");
+    }
+
+    public void clickResendEmail() {
+        resendEmailLink.click();
+    }
+
+    public String getResendEmailLink() {
+        return resendEmailLink.getAttribute("href");
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/GreenMailRule.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/GreenMailRule.java
new file mode 100755
index 0000000..ae7487d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/GreenMailRule.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.util;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+import org.junit.rules.ExternalResource;
+import org.keycloak.models.RealmModel;
+
+import javax.mail.internet.MimeMessage;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.SocketException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class GreenMailRule extends ExternalResource {
+
+    private GreenMail greenMail;
+
+    @Override
+    protected void before() throws Throwable {
+        ServerSetup setup = new ServerSetup(3025, "localhost", "smtp");
+
+        greenMail = new GreenMail(setup);
+        greenMail.start();
+    }
+
+    @Override
+    protected void after() {
+        if (greenMail != null) {
+            // Suppress error from GreenMail on shutdown
+            Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+                @Override
+                public void uncaughtException(Thread t, Throwable e) {
+                    if (!(e.getCause() instanceof SocketException && t.getClass().getName()
+                            .equals("com.icegreen.greenmail.smtp.SmtpHandler"))) {
+                        System.err.print("Exception in thread \"" + t.getName() + "\" ");
+                        e.printStackTrace(System.err);
+                    }
+                }
+            });
+
+            greenMail.stop();
+        }
+    }
+
+    public void configureRealm(RealmModel realm) {
+        Map<String, String> config = new HashMap<>();
+        config.put("from", "auto@keycloak.org");
+        config.put("host", "localhost");
+        config.put("port", "3025");
+        realm.setSmtpConfig(config);
+    }
+
+    public MimeMessage[] getReceivedMessages() {
+        return greenMail.getReceivedMessages();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java
new file mode 100644
index 0000000..69aa142
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.util;
+
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.internet.MimeMessage;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MailUtils {
+
+    private static Pattern mailPattern = Pattern.compile("http[^\\s\"]*");
+
+    public static String getLink(String body) {
+        Matcher matcher = mailPattern.matcher(body);
+        if (matcher.find()) {
+            return matcher.group();
+        }
+        throw new AssertionError("No link found in " + body);
+    }
+
+    public static String getPasswordResetEmailLink(MimeMessage message) throws IOException, MessagingException {
+        Multipart multipart = (Multipart) message.getContent();
+
+        final String textContentType = multipart.getBodyPart(0).getContentType();
+
+        assertEquals("text/plain; charset=UTF-8", textContentType);
+
+        final String textBody = (String) multipart.getBodyPart(0).getContent();
+        final String textChangePwdUrl = getLink(textBody);
+
+        final String htmlContentType = multipart.getBodyPart(1).getContentType();
+
+        assertEquals("text/html; charset=UTF-8", htmlContentType);
+
+        final String htmlBody = (String) multipart.getBodyPart(1).getContent();
+        final String htmlChangePwdUrl = getLink(htmlBody);
+
+        assertEquals(htmlChangePwdUrl, textChangePwdUrl);
+
+        return htmlChangePwdUrl;
+    }
+
+}
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
index 11cda10..84bd64c 100644
--- 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
@@ -19,7 +19,6 @@ package org.keycloak.testsuite;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.commons.io.IOUtils;
-import org.keycloak.broker.provider.util.SimpleHttp;
 import org.keycloak.common.util.KeycloakUriBuilder;
 import org.keycloak.testsuite.arquillian.TestContext;
 
@@ -28,13 +27,10 @@ import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import javax.ws.rs.NotFoundException;
 import org.jboss.arquillian.container.test.api.RunAsClient;
@@ -61,6 +57,7 @@ import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
 import org.keycloak.testsuite.arquillian.SuiteContext;
 import org.keycloak.testsuite.auth.page.WelcomePage;
 import org.keycloak.testsuite.util.DeleteMeOAuthClient;
+import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.WebDriver;
 import org.keycloak.testsuite.auth.page.AuthServer;
@@ -97,7 +94,10 @@ public abstract class AbstractKeycloakTest {
 
     protected Keycloak adminClient;
 
-    protected DeleteMeOAuthClient oauthClient;
+    @ArquillianResource
+    protected OAuthClient oauthClient;
+
+    protected DeleteMeOAuthClient deleteMeOAuthClient;
 
     protected List<RealmRepresentation> testRealmReps;
 
@@ -132,7 +132,7 @@ public abstract class AbstractKeycloakTest {
     public void beforeAbstractKeycloakTest() {
         adminClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
                 MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
-        oauthClient = new DeleteMeOAuthClient(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth");
+        deleteMeOAuthClient = new DeleteMeOAuthClient(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth");
 
         
         adminUser = createAdminUserRepresentation();
@@ -150,6 +150,9 @@ public abstract class AbstractKeycloakTest {
         }
 
         importTestRealms();
+
+        oauthClient.setAdminClient(adminClient);
+        oauthClient.setDriver(driver);
     }
 
     @After
@@ -346,4 +349,12 @@ public abstract class AbstractKeycloakTest {
         return constantsProperties;
     }
 
+    public URI getAuthServerRoot() {
+        try {
+            return KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getContextRoot().toURI()).path("/auth/").build();
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index ac626e0..84ae0c1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -101,12 +101,12 @@ public class ClientTest extends AbstractAdminTest {
 
     @Test
     public void getClientSessions() throws Exception {
-        OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
+        OAuthClient.AccessTokenResponse response = oauthClient.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
         assertEquals(200, response.getStatusCode());
 
-        OAuthClient.AuthorizationCodeResponse codeResponse = oauth.doLogin("test-user@localhost", "password");
+        OAuthClient.AuthorizationCodeResponse codeResponse = oauthClient.doLogin("test-user@localhost", "password");
 
-        OAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(codeResponse.getCode(), "password");
+        OAuthClient.AccessTokenResponse response2 = oauthClient.doAccessTokenRequest(codeResponse.getCode(), "password");
         assertEquals(200, response2.getStatusCode());
 
         ClientResource app = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
index c7085b3..8e4cfc3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
@@ -120,7 +120,7 @@ public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTes
     }
 
     private String getToken(String username, String password) {
-        return oauthClient.getToken(REALM_NAME, Constants.ADMIN_CLI_CLIENT_ID, null, username, password).getToken();
+        return deleteMeOAuthClient.getToken(REALM_NAME, Constants.ADMIN_CLI_CLIENT_ID, null, username, password).getToken();
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
index 0ef5e26..e8ccff9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
@@ -62,7 +62,7 @@ public class ClientRegistrationTest extends AbstractClientRegistrationTest {
     public void registerClientInMasterRealm() throws ClientRegistrationException {
         ClientRegistration masterReg = ClientRegistration.create().url(suiteContext.getAuthServerInfo().getContextRoot() + "/auth", "master").build();
 
-        String token = oauthClient.getToken("master", Constants.ADMIN_CLI_CLIENT_ID, null, "admin", "admin").getToken();
+        String token = deleteMeOAuthClient.getToken("master", Constants.ADMIN_CLI_CLIENT_ID, null, "admin", "admin").getToken();
         masterReg.auth(Auth.token(token));
 
         ClientRepresentation client = new ClientRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/endpoint/group/AbstractGroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/endpoint/group/AbstractGroupTest.java
index 0636872..03dce57 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/endpoint/group/AbstractGroupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/endpoint/group/AbstractGroupTest.java
@@ -50,7 +50,7 @@ public abstract class AbstractGroupTest extends AbstractKeycloakTest {
 
     AccessToken login(String login, String clientId, String clientSecret, String userId) throws Exception {
 
-        AccessTokenResponse tokenResponse = oauthClient.getToken("test", clientId, clientSecret, login, "password");
+        AccessTokenResponse tokenResponse = deleteMeOAuthClient.getToken("test", clientId, clientSecret, login, "password");
 
         String accessToken = tokenResponse.getToken();
         String refreshToken = tokenResponse.getRefreshToken();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
index 54f218e..8ddc7f0 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
@@ -108,10 +108,10 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testConfidentialClientCredentialsBasicAuthentication() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
         ObjectMapper objectMapper = new ObjectMapper();
         JsonNode jsonNode = objectMapper.readTree(tokenResponse);
 
@@ -144,10 +144,10 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testInvalidClientCredentials() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "bad_credential", accessTokenResponse.getAccessToken());
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "bad_credential", accessTokenResponse.getAccessToken());
 
         assertEquals("{\"error_description\":\"Authentication failed.\",\"error\":\"invalid_request\"}", tokenResponse);
 
@@ -156,12 +156,12 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testIntrospectRefreshToken() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
         EventRepresentation loginEvent = events.expectLogin().assertEvent();
         String sessionId = loginEvent.getSessionId();
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
         ObjectMapper objectMapper = new ObjectMapper();
         JsonNode jsonNode = objectMapper.readTree(tokenResponse);
 
@@ -192,11 +192,11 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testPublicClientCredentialsNotAllowed() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
+        oauthClient.doLogin("test-user@localhost", "password");
 
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("public-cli", "it_doesnt_matter", accessTokenResponse.getAccessToken());
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("public-cli", "it_doesnt_matter", accessTokenResponse.getAccessToken());
 
         assertEquals("{\"error_description\":\"Client not allowed.\",\"error\":\"invalid_request\"}", tokenResponse);
 
@@ -205,9 +205,9 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testInactiveAccessToken() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
+        oauthClient.doLogin("test-user@localhost", "password");
         String inactiveAccessToken = "eyJhbGciOiJSUzI1NiJ9.eyJub25jZSI6IjczMGZjNjQ1LTBlMDQtNDE3Yi04MDY0LTkyYWIyY2RjM2QwZSIsImp0aSI6ImU5ZGU1NjU2LWUzMjctNDkxNC1hNjBmLTI1MzJlYjBiNDk4OCIsImV4cCI6MTQ1MjI4MTAwMCwibmJmIjowLCJpYXQiOjE0NTIyODA3MDAsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9leGFtcGxlIiwiYXVkIjoianMtY29uc29sZSIsInN1YiI6IjFkNzQ0MDY5LWYyOTgtNGU3Yy1hNzNiLTU1YzlhZjgzYTY4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImpzLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiNzc2YTA0OTktODNjNC00MDhkLWE5YjctYTZiYzQ5YmQ3MThjIiwiY2xpZW50X3Nlc3Npb24iOiJjN2Y5ODczOC05MDhlLTQxOWYtYTdkNC1kODYxYjRhYTI3NjkiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidXNlciJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiU2FtcGxlIFVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIiwiZ2l2ZW5fbmFtZSI6IlNhbXBsZSIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoic2FtcGxlLXVzZXJAZXhhbXBsZSJ9.YyPV74j9CqOG2Jmq692ZZpqycjNpUgtYVRfQJccS_FU84tGVXoKKsXKYeY2UJ1Y_bPiYG1I1J6JSXC8XqgQijCG7Nh7oK0yN74JbRN58HG75fvg6K9BjR6hgJ8mHT8qPrCux2svFucIMIZ180eoBoRvRstkidOhl_mtjT_i31fU";
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", inactiveAccessToken);
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", inactiveAccessToken);
         ObjectMapper objectMapper = new ObjectMapper();
         JsonNode jsonNode = objectMapper.readTree(tokenResponse);
 
@@ -225,11 +225,11 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testIntrospectAccessToken() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
         EventRepresentation loginEvent = events.expectLogin().assertEvent();
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
         TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
 
         assertTrue(rep.isActive());
@@ -242,12 +242,12 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testIntrospectAccessTokenSessionInvalid() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
-        oauth.doLogout(accessTokenResponse.getRefreshToken(), "password");
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
+        oauthClient.doLogout(accessTokenResponse.getRefreshToken(), "password");
 
-        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
         TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
 
         assertFalse(rep.isActive());
@@ -260,18 +260,18 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
 
     @Test
     public void testIntrospectAccessTokenUserDisabled() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
 
         EventRepresentation loginEvent = events.expectLogin().assertEvent();
 
         UserRepresentation userRep = new UserRepresentation();
         try {
             userRep.setEnabled(false);
-            adminClient.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+            adminClient.realm(oauthClient.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
 
-            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
             TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
 
             assertFalse(rep.isActive());
@@ -282,20 +282,20 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
             events.clear();
         } finally {
             userRep.setEnabled(true);
-            adminClient.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+            adminClient.realm(oauthClient.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
         }
     }
 
     @Test
     public void testIntrospectAccessTokenExpired() throws Exception {
-        oauth.doLogin("test-user@localhost", "password");
-        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        oauthClient.doLogin("test-user@localhost", "password");
+        String code = oauthClient.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauthClient.doAccessTokenRequest(code, "password");
 
         try {
-            Time.setOffset(adminClient.realm(oauth.getRealm()).toRepresentation().getAccessTokenLifespan() + 1);
+            Time.setOffset(adminClient.realm(oauthClient.getRealm()).toRepresentation().getAccessTokenLifespan() + 1);
 
-            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            String tokenResponse = oauthClient.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
             TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
 
             assertFalse(rep.isActive());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
index 00a2cf9..55cf15b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/TestRealmKeycloakTest.java
@@ -17,12 +17,11 @@
 
 package org.keycloak.testsuite;
 
-import org.keycloak.testsuite.util.OAuthClient;
-import java.util.List;
-import org.junit.Before;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 
+import java.util.List;
+
 import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
 
 /**
@@ -33,8 +32,6 @@ import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
  */
 public abstract class TestRealmKeycloakTest extends AbstractKeycloakTest {
 
-    protected OAuthClient oauth;
-
     protected ClientRepresentation findTestApp(RealmRepresentation testRealm) {
         for (ClientRepresentation client : testRealm.getClients()) {
             if (client.getClientId().equals("test-app")) return client;
@@ -47,8 +44,6 @@ public abstract class TestRealmKeycloakTest extends AbstractKeycloakTest {
     public void addTestRealms(List<RealmRepresentation> testRealms) {
         RealmRepresentation testRealm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
 
-        oauth = new OAuthClient(driver, testRealm.getPublicKey());
-
         testRealms.add(testRealm);
 
         configureTestRealm(testRealm);