keycloak-aplcache

Changes

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java 14(+0 -14)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java 14(+0 -14)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientMappersForm.java 187(+0 -187)

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
index 91f909f..e3ae1aa 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
@@ -17,7 +17,7 @@
         <div class="form-group">
             <label class="col-md-2 control-label" for="host">Host</label>
             <div class="col-sm-6">
-                <input ng-disabled="!create" class="form-control" type="text" id="host" name="host" data-ng-model="node.host">
+                <input ng-disabled="!create" class="form-control" type="text" id="host" name="host" data-ng-model="node.host" required>
             </div>
         </div>
         <div class="form-group">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
index 6c48016..cd63f49 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
@@ -76,9 +76,9 @@
                     </div>
                     <div class="row" data-ng-show="targetClient">
                         <div class="col-md-3">
-                            <label class="control-label" for="client-available">{{:: 'available-roles' | translate}}</label>
+                            <label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
                             <kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>
-                            <select id="client-available" class="form-control" multiple size="5"
+                            <select id="available-client" class="form-control" multiple size="5"
                                     ng-multiple="true"
                                     ng-model="selectedClientRoles"
                                     ng-options="r.name for r in clientRoles">
@@ -88,9 +88,9 @@
                             </button>
                         </div>
                         <div class="col-md-3">
-                            <label class="control-label" for="client-assigned">{{:: 'assigned-roles' | translate}}</label>
+                            <label class="control-label" for="assigned-client">{{:: 'assigned-roles' | translate}}</label>
                             <kc-tooltip>{{:: 'client.assigned-roles.tooltip' | translate}}</kc-tooltip>
-                            <select id="client-assigned" class="form-control" multiple size=5
+                            <select id="assigned-client" class="form-control" multiple size=5
                                     ng-multiple="true"
                                     ng-model="selectedClientMappings"
                                     ng-options="r.name for r in clientMappings">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
index bb38c6a..bc552da 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
@@ -65,9 +65,9 @@
                 </div>
                 <div class="row" data-ng-show="targetClient">
                     <div class="col-md-3">
-                        <label class="control-label" for="client-available">{{:: 'available-roles' | translate}}</label>
+                        <label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
                         <kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>
-                        <select id="client-available" class="form-control" multiple size="5"
+                        <select id="available-client" class="form-control" multiple size="5"
                                 ng-multiple="true"
                                 ng-model="selectedClientRoles"
                                 ng-options="r.name for r in clientRoles">
@@ -77,9 +77,9 @@
                         </button>
                     </div>
                     <div class="col-md-3">
-                        <label class="control-label" for="client-assigned">{{:: 'assigned-roles' | translate}}</label>
+                        <label class="control-label" for="assigned-client">{{:: 'assigned-roles' | translate}}</label>
                         <kc-tooltip>{{:: 'client.assigned-roles.tooltip' | translate}}</kc-tooltip>
-                        <select id="client-assigned" class="form-control" multiple size=5
+                        <select id="assigned-client" class="form-control" multiple size=5
                                 ng-multiple="true"
                                 ng-model="selectedClientMappings"
                                 ng-options="r.name for r in clientMappings">
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
index 783cd0b..bb70843 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
@@ -1,5 +1,6 @@
 package org.keycloak.testsuite.console.page.clients;
 
+import org.jboss.arquillian.graphene.fragment.Root;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.testsuite.console.page.fragment.Breadcrumb;
 import static org.keycloak.testsuite.console.page.fragment.Breadcrumb.BREADCRUMB_XPATH;
@@ -55,6 +56,9 @@ public class Client extends Clients {
 
     public class ClientTabs {
 
+        @Root
+        private WebElement tabs;
+        
         @FindBy(linkText = "Settings")
         private WebElement settingsLink;
         @FindBy(linkText = "Roles")
@@ -97,6 +101,10 @@ public class Client extends Clients {
         public void installation() {
             installationLink.click();
         }
+        
+        public WebElement getTabs() {
+            return tabs;
+        }
 
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
index 494b3fe..58fac4d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
@@ -126,11 +126,6 @@ public class Clients extends AdminConsoleRealm {
         }
     }
 
-    public void deleteClient(String clientId) {
-        clientsTable.searchClients(clientId);
-        clientsTable.deleteClient(clientId);
-    }
-
     public ClientsResource clientsResource() {
         return realmResource().clients();
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/clustering/ClientClusteringForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/clustering/ClientClusteringForm.java
new file mode 100644
index 0000000..e8e2748
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/clustering/ClientClusteringForm.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.clustering;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientClusteringForm extends Form {
+
+    @FindBy(id = "nodeReRegistrationTimeout")
+    private WebElement nodeReRegistrationTimeoutInput;
+    
+    @FindBy(xpath = ".//select")
+    private Select nodeReRegistrationTimeoutUnitSelect;
+    
+    @FindBy(linkText = "Register node manually")
+    private WebElement registerNodeManuallyLink;
+    
+    @FindBy(id = "host")
+    private WebElement hostNameInput;
+
+    private void setNodeReRegistrationTimeout(String value) {
+        setInputValue(nodeReRegistrationTimeoutInput, value);
+    }
+    
+    private void setNodeReRegistrationTimeoutUnit(String value) {
+        nodeReRegistrationTimeoutUnitSelect.selectByVisibleText(value);
+    }
+    
+    public void setNodeReRegistrationTimeout(String timeout, String unit) {
+        setNodeReRegistrationTimeoutUnit(unit);
+        setNodeReRegistrationTimeout(timeout);
+    }
+    
+    public void addNode(String hostName) {
+        registerNodeManuallyLink.click();
+//        waitforElement(hostNameInput);
+        setInputValue(hostNameInput, hostName);
+        save();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
index 88e6ed5..8182638 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
@@ -2,13 +2,15 @@ package org.keycloak.testsuite.console.page.clients;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import org.jboss.arquillian.graphene.page.Page;
 import org.keycloak.representations.idm.ClientRepresentation;
 import static org.keycloak.testsuite.auth.page.login.OIDCLogin.OIDC;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.*;
 import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
 import org.keycloak.testsuite.page.Form;
 import static org.keycloak.testsuite.page.Form.getInputValue;
-import static org.keycloak.testsuite.util.WaitUtils.pause;
-import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+import static org.keycloak.testsuite.util.WaitUtils.*;
 import org.keycloak.testsuite.util.Timer;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
@@ -43,10 +45,8 @@ public class CreateClientForm extends Form {
 
     @FindBy(id = "protocol")
     private Select protocolSelect;
-    @FindBy(id = "protocol")
-    private WebElement protocolSelectElement;
-
-    @FindBy
+    
+    @Page
     private SAMLClientSettingsForm samlForm;
 
     public SAMLClientSettingsForm samlForm() {
@@ -55,14 +55,13 @@ public class CreateClientForm extends Form {
 
     @FindBy(id = "accessType")
     private Select accessTypeSelect;
-    @FindBy(id = "accessType")
-    private WebElement accessTypeSelectElement;
-
     @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='serviceAccountsEnabled']]")
     private OnOffSwitch serviceAccountsEnabledSwitch;
 
     @FindBy(id = "newRedirectUri")
     private WebElement newRedirectUriInput;
+    @FindBy(xpath = ".//i[contains(@data-ng-click, 'newRedirectUri')]")
+    private WebElement newRedirectUriSubmit;
     @FindBy(xpath = ".//input[@ng-model='client.redirectUris[i]']")
     private List<WebElement> redirectUriInputs;
     @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteRedirectUri')]")
@@ -75,42 +74,22 @@ public class CreateClientForm extends Form {
         setName(client.getName());
         setEnabled(client.isEnabled());
         setConsentRequired(client.isConsentRequired());
-        setStandardFlowEnabled(client.isStandardFlowEnabled());
-        setImplicitFlowEnabled(client.isImplicitFlowEnabled());
-        setDirectAccessGrantsEnabled(client.isDirectAccessGrantsEnabled());
         setProtocol(client.getProtocol());
         if (OIDC.equals(client.getProtocol())) {
             setAccessType(client);
             if (!client.isBearerOnly()) {
-                if (!client.isPublicClient()) {
+                setStandardFlowEnabled(client.isStandardFlowEnabled());
+                setDirectAccessGrantsEnabled(client.isDirectAccessGrantsEnabled());
+                if (client.isPublicClient()) {
+                    setImplicitFlowEnabled(client.isImplicitFlowEnabled());
+                } else {//confidential
                     setServiceAccountsEnabled(client.isServiceAccountsEnabled());
                 }
-                setRedirectUris(client.getRedirectUris());
-            }
-        }
-    }
-
-    public ClientRepresentation getValues() {
-        ClientRepresentation values = new ClientRepresentation();
-        values.setClientId(getClientId());
-        values.setName(getName());
-        values.setEnabled(isEnabled());
-        values.setConsentRequired(isConsentRequired());
-        values.setStandardFlowEnabled(isStandardFlowEnabled());
-        values.setImplicitFlowEnabled(isImplicitFlowEnabled());
-        values.setDirectAccessGrantsEnabled(isDirectAccessGrantsEnabled());
-        values.setProtocol(getProtocol());
-        if (OIDC.equals(values.getProtocol())) {
-            values.setBearerOnly(isBearerOnly());
-            if (!values.isBearerOnly()) {
-                values.setPublicClient(isPublicClient());
-                if (!values.isPublicClient()) {
-                    values.setServiceAccountsEnabled(isServiceAccountsEnabled());
+                if (client.isStandardFlowEnabled() || client.isImplicitFlowEnabled()) {
+                    setRedirectUris(client.getRedirectUris());
                 }
-                values.setRedirectUris(getRedirectUris());
             }
         }
-        return values;
     }
 
     public String getClientId() {
@@ -137,40 +116,35 @@ public class CreateClientForm extends Form {
         enabledSwitch.setOn(enabled);
     }
 
-    public static final String BEARER_ONLY = "bearer-only";
-    public static final String PUBLIC = "public";
-    public static final String CONFIDENTIAL = "confidential";
+    public enum OidcAccessType {
+        BEARER_ONLY("bearer-only"),
+        PUBLIC("public"),
+        CONFIDENTIAL("confidential");
+        
+        private final String name;
 
-    public boolean isBearerOnly() {
-        return BEARER_ONLY.equals(
-                accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
-    }
-
-    public boolean isPublicClient() {
-        return PUBLIC.equals(
-                accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
-    }
-
-    public void setBearerOnly(boolean bearerOnly) {
-        accessTypeSelectElement.sendKeys(BEARER_ONLY);
-//        accessTypeSelect.selectByVisibleText(BEARER_ONLY);
-    }
+        private OidcAccessType(String name) {
+            this.name = name;
+        }
 
-    public void setPublicClient(boolean publicClient) {
-        accessTypeSelectElement.sendKeys(PUBLIC);
-//        accessTypeSelect.selectByVisibleText(PUBLIC);
+        public String getName() {
+            return name;
+        }
     }
-
-    public void setAccessType(ClientRepresentation client) { // TODO verify
-        setBearerOnly(client.isBearerOnly());
-        setPublicClient(client.isPublicClient());
-        if (!client.isBearerOnly() && !client.isPublicClient()) {
-            accessTypeSelect.selectByVisibleText(CONFIDENTIAL);
+    
+    public void setAccessType(ClientRepresentation client) {
+        if (client.isBearerOnly()) {
+            accessTypeSelect.selectByVisibleText(BEARER_ONLY.getName());
+        } else if (client.isPublicClient()) {
+            accessTypeSelect.selectByVisibleText(PUBLIC.getName());
+        } else {
+            accessTypeSelect.selectByVisibleText(CONFIDENTIAL.getName());
         }
     }
 
     public void addRedirectUri(String redirectUri) {
         newRedirectUriInput.sendKeys(redirectUri);
+        newRedirectUriSubmit.click();
     }
 
     public List<String> getRedirectUris() {
@@ -236,7 +210,7 @@ public class CreateClientForm extends Form {
 
     public void setProtocol(String protocol) {
         Timer.time();
-        protocolSelectElement.sendKeys(protocol);
+        protocolSelect.selectByVisibleText(protocol);
         Timer.time("clientSettings.setProtocol()");
     }
 
@@ -250,7 +224,83 @@ public class CreateClientForm extends Form {
 
     public class SAMLClientSettingsForm extends Form {
 
-        // TODO add SAML client attributes
+        public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
+        public static final String SAML_AUTHNSTATEMENT = "saml.authnstatement";
+	public static final String SAML_CLIENT_SIGNATURE = "saml.client.signature";
+	public static final String SAML_ENCRYPT = "saml.encrypt";
+	public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
+	public static final String SAML_MULTIVALUED_ROLES = "saml.multivalued.roles";
+	public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
+	public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
+	public static final String SAML_ASSERTION_CONSUMER_URL_POST = "saml_assertion_consumer_url_post";
+	public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT = "saml_assertion_consumer_url_redirect";
+	public static final String SAML_FORCE_NAME_ID_FORMAT = "saml_force_name_id_format";
+	public static final String SAML_NAME_ID_FORMAT = "saml_name_id_format";
+	public static final String SAML_SIGNATURE_CANONICALIZATION_METHOD = "saml_signature_canonicalization_method";
+	public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST = "saml_single_logout_service_url_post";
+	public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT = "saml_single_logout_service_url_redirect";
+        
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlAuthnStatement']]")
+        private OnOffSwitch samlAuthnStatement;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlServerSignature']]")
+        private OnOffSwitch samlServerSignature;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlAssertionSignature']]")
+        private OnOffSwitch samlAssertionSignature;
+        @FindBy(id = "signatureAlgorithm")
+        private Select signatureAlgorithm;
+        @FindBy(id = "canonicalization")
+        private Select canonicalization;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlEncrypt']]")
+        private OnOffSwitch samlEncrypt;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlClientSignature']]")
+        private OnOffSwitch samlClientSignature;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlForcePostBinding']]")
+        private OnOffSwitch samlForcePostBinding;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='frontchannelLogout']]")
+        private OnOffSwitch frontchannelLogout;
+        @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlForceNameIdFormat']]")
+        private OnOffSwitch samlForceNameIdFormat;
+        @FindBy(id = "samlNameIdFormat")
+        private Select samlNameIdFormat;
+        
+        @FindBy(xpath = "//fieldset[contains(@data-ng-show, 'saml')]//i")
+        private WebElement fineGrainCollapsor;
+        
+        @FindBy(id = "consumerServicePost")
+        private WebElement consumerServicePostInput;
+        @FindBy(id = "consumerServiceRedirect")
+        private WebElement consumerServiceRedirectInput;
+        @FindBy(id = "logoutPostBinding")
+        private WebElement logoutPostBindingInput;
+        @FindBy(id = "logoutRedirectBinding")
+        private WebElement logoutRedirectBindingInput;
+        
+        public void setValues(ClientRepresentation client) {
+            waitUntilElement(fineGrainCollapsor).is().visible();
+            
+            Map<String, String> attributes = client.getAttributes();
+            samlAuthnStatement.setOn("true".equals(attributes.get(SAML_AUTHNSTATEMENT)));
+            samlServerSignature.setOn("true".equals(attributes.get(SAML_SERVER_SIGNATURE)));
+            samlAssertionSignature.setOn("true".equals(attributes.get(SAML_ASSERTION_SIGNATURE)));
+            if (samlServerSignature.isOn() || samlAssertionSignature.isOn()) {
+                signatureAlgorithm.selectByVisibleText(attributes.get(SAML_SIGNATURE_ALGORITHM));
+                canonicalization.selectByValue("string:" + attributes.get(SAML_SIGNATURE_CANONICALIZATION_METHOD));
+            }
+            samlEncrypt.setOn("true".equals(attributes.get(SAML_ENCRYPT)));
+            samlClientSignature.setOn("true".equals(attributes.get(SAML_CLIENT_SIGNATURE)));
+            samlForcePostBinding.setOn("true".equals(attributes.get(SAML_FORCE_POST_BINDING)));
+            frontchannelLogout.setOn(client.isFrontchannelLogout());
+            samlForceNameIdFormat.setOn("true".equals(attributes.get(SAML_FORCE_NAME_ID_FORMAT)));
+            samlNameIdFormat.selectByVisibleText(attributes.get(SAML_NAME_ID_FORMAT));
+            
+            fineGrainCollapsor.click();
+            waitUntilElement(consumerServicePostInput).is().present();
+            
+            setInputValue(consumerServicePostInput, attributes.get(SAML_ASSERTION_CONSUMER_URL_POST));
+            setInputValue(consumerServiceRedirectInput, attributes.get(SAML_ASSERTION_CONSUMER_URL_REDIRECT));
+            setInputValue(logoutPostBindingInput, attributes.get(SAML_SINGLE_LOGOUT_SERVICE_URL_POST));
+            setInputValue(logoutRedirectBindingInput, attributes.get(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT));
+        }
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsForm.java
new file mode 100644
index 0000000..991b175
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsForm.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.credentials;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientCredentialsForm extends Form {
+
+    @FindBy(id = "clientAuthenticatorType")
+    private Select clientAuthenticatorType;
+    
+    @FindBy(xpath = "//button[text()='Regenerate Secret']")
+    private WebElement regenerateSecretButton;
+
+    @FindBy(xpath = "//button[text()='Generate new keys and certificate']")
+    private WebElement generateNewKeysAndCert;
+    
+    @FindBy(xpath = "//button[text()='Regenerate registration access token']")
+    private WebElement regenerateRegistrationAccessTokenButton;
+    
+    public void selectClientIdAndSecret() {
+        clientAuthenticatorType.selectByVisibleText("Client Id and Secret");
+    }
+    
+    public void selectSignedJwt() {
+        clientAuthenticatorType.selectByVisibleText("Signed Jwt");
+    }
+
+    public void regenerateSecret() {
+        regenerateSecretButton.click();
+    }
+
+    public void regenerateRegistrationAccessToken() {
+        regenerateRegistrationAccessTokenButton.click();
+    }
+
+    public void generateNewKeysAndCert() {
+        generateNewKeysAndCert.click();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeys.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeys.java
new file mode 100644
index 0000000..808ade6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeys.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.credentials;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientCredentialsGeneratePrivateKeys extends ClientCredentials {
+
+    @Override
+    public String getUriFragment() {
+        return super.getUriFragment() + "/client-jwt/Signing/export/jwt.credential";
+    }
+    
+    @Page
+    private ClientCredentialsGeneratePrivateKeysForm form;
+
+    public ClientCredentialsGeneratePrivateKeysForm generateForm() {
+        return form;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeysForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeysForm.java
new file mode 100644
index 0000000..1994c0a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/credentials/ClientCredentialsGeneratePrivateKeysForm.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.credentials;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientCredentialsGeneratePrivateKeysForm extends Form {
+
+    @FindBy(id = "downloadKeyFormat")
+    private Select downloadKeyFormat;
+    
+    @FindBy(id = "keyAlias")
+    private WebElement keyAliasInput;
+
+    @FindBy(id = "keyPassword")
+    private WebElement keyPasswordInput;
+    
+    @FindBy(id = "storePassword")
+    private WebElement storePasswordInput;
+    
+    @FindBy(xpath = "//button[text()='Generate and Download']")
+    private WebElement generateAndDownloadButton;
+    
+    public void selectFormatJKS() {
+        downloadKeyFormat.selectByVisibleText("JKS");
+    }
+
+    public void selectFormatPKCS12() {
+        downloadKeyFormat.selectByVisibleText("PKCS12");
+    }
+    
+    public void setKeyAlias(String value) {
+        setInputValue(keyAliasInput, value);
+    }
+    
+    public void setKeyPassword(String value) {
+        setInputValue(keyPasswordInput, value);
+    }
+    
+    public void setStorePassword(String value) {
+        setInputValue(storePasswordInput, value);
+    }
+    
+    public void clickGenerateAndDownload() {
+        generateAndDownloadButton.click();
+    }    
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/installation/ClientInstallationForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/installation/ClientInstallationForm.java
new file mode 100644
index 0000000..6ed4d9e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/installation/ClientInstallationForm.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.installation;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientInstallationForm extends Form {
+
+    @FindBy(id = "configFormats")
+    private Select configFormatsSelect;
+    
+    @FindBy(tagName = "textarea")
+    private WebElement textarea;
+
+    public void setConfigFormat(String value) {
+        configFormatsSelect.selectByVisibleText(value);
+    }
+    
+    public String getTextareaContent() {
+        return textarea.getText();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/ClientMapper.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/ClientMapper.java
new file mode 100644
index 0000000..35df866
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/ClientMapper.java
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.mappers;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientMapper extends ClientMappers {
+    
+    public static final String MAPPER_ID = "mapperId";
+    
+    @FindBy(xpath = "//i[contains(@class, 'delete')]")
+    private WebElement deleteIcon;
+    
+    @FindBy(tagName = "form")
+    private MapperSettingsForm form;
+    
+    @Override
+    public String getUriFragment() {
+        return super.getUriFragment() + "/{" + MAPPER_ID + "}";
+    }
+    
+    public void setMapperId(String id) {
+        setUriParameter(MAPPER_ID, id);
+    }
+
+    public String getMapperId() {
+        return getUriParameter(MAPPER_ID).toString();
+    }
+    
+    @Override
+    public void delete() {
+        deleteIcon.click();
+        modalDialog.confirmDeletion();
+    }
+    
+    public MapperSettingsForm form() {
+        return form;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java
new file mode 100644
index 0000000..fb4dacf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/CreateClientMappersForm.java
@@ -0,0 +1,311 @@
+package org.keycloak.testsuite.console.page.clients.mappers;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class CreateClientMappersForm extends Form {
+
+    // Mappers types
+    public static final String HARDCODED_ROLE = "Hardcoded Role";
+    public static final String HARDCODED_CLAIM = "Hardcoded claim";
+    public static final String USER_SESSION_NOTE = "User Session Note";
+    public static final String ROLE_NAME_MAPPER = "Role Name Mapper";
+    public static final String USER_ADDRESS = "User Address";
+    public static final String USERS_FULL_NAME = "User's full name";
+    public static final String USER_ATTRIBUTE = "User Attribute";
+    public static final String USER_PROPERTY = "User Property";
+    public static final String GROUP_MEMBERSHIP = "Group Membership";
+    public static final String ROLE_LIST = "Role list";
+    public static final String HARDCODED_ATTRIBUTE = "Hardcoded attribute";
+    public static final String GROUP_LIST = "Group list";
+    public static final String HARDCODED_ROLE_SAML = "Hardcoded role";
+
+    // Role types
+    public static final String REALM_ROLE = "realm";
+    public static final String CLIENT_ROLE = "client";
+    
+    @FindBy(id = "name")
+    private WebElement nameElement;
+
+    @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='consentRequired']]")
+    private OnOffSwitch consentRequiredSwitch;
+
+    @FindBy(id = "consentText")
+    private WebElement consentTextElement;
+
+    @FindBy(id = "mapperTypeCreate")
+    private Select mapperTypeSelect;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Property']//following-sibling::node()//input[@type='text']")
+    private WebElement propertyInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='User Attribute']//following-sibling::node()//input[@type='text']")
+    private WebElement userAttributeInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='User Session Note']//following-sibling::node()//input[@type='text']")
+    private WebElement userSessionNoteInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Multivalued']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch multivaluedInput;
+
+    @FindBy(xpath = ".//button[text() = 'Select Role']/../..//input")
+    private WebElement roleInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='New Role Name']//following-sibling::node()//input[@type='text']")
+    private WebElement newRoleInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Token Claim Name']//following-sibling::node()//input[@type='text']")
+    private WebElement tokenClaimNameInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Claim value']//following-sibling::node()//input[@type='text']")
+    private WebElement tokenClaimValueInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Claim JSON Type']//following-sibling::node()//select")
+    private Select claimJSONTypeInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Add to ID token']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch addToIDTokenInput;
+
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Add to access token']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch addToAccessTokenInput;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Full group path']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch fullGroupPath;
+
+    @FindBy(xpath = ".//button[text() = 'Select Role']")
+    private WebElement selectRoleButton;
+            
+    @FindBy(xpath = "//div[@class='modal-dialog']")
+    private RoleSelectorModalDialog roleSelectorModalDialog;
+
+    public class RoleSelectorModalDialog {
+        @FindBy(id = "available")
+        private Select realmAvailable;
+        @FindBy(xpath = ".//button[@tooltip='Select realm role']")
+        private WebElement selectRealmRoleButton;
+        
+        @FindBy(id = "available-client")
+        private Select clientAvailable;
+        @FindBy(id = "clients")
+        private Select clientSelect;
+        @FindBy(xpath = ".//button[@tooltip='Select client role']")
+        private WebElement selectClientRoleButton;
+        @FindBy(xpath = ".//button[@class='close']")
+        private WebElement closeButton;
+        
+        public void closeRoleSelectorModalDialog() {
+            closeButton.click();
+        }
+        
+        public void selectRealmRole(String roleName) {
+            if (roleName != null) {
+                realmAvailable.selectByVisibleText(roleName);
+            }
+            selectRealmRoleButton.click();
+        }
+        
+        public void selectClientRole(String clientName, String roleName) {
+            if (roleName != null || clientName != null) {
+                clientSelect.selectByVisibleText(clientName);
+                clientAvailable.selectByVisibleText(roleName);
+            }
+            selectClientRoleButton.click();
+        }
+    }
+    
+    public void selectRole(String roleType, String roleName, String clientName) {
+        selectRoleButton.click();
+        switch (roleType) {
+            case REALM_ROLE:
+                roleSelectorModalDialog.selectRealmRole(roleName);
+                break;
+            case CLIENT_ROLE:
+                roleSelectorModalDialog.selectClientRole(clientName, roleName);
+                break;
+            default:
+                throw new IllegalArgumentException("No such role type, use \"" + 
+                        REALM_ROLE + "\" or \"" + CLIENT_ROLE + "\"");
+        }
+    }
+    
+    public void closeRoleSelectorModalDialog() {
+        roleSelectorModalDialog.closeRoleSelectorModalDialog();
+    }
+    
+    public void setName(String value) {
+        setInputValue(nameElement, value);
+    }
+    
+    public boolean isConsentRequired() {
+        return consentRequiredSwitch.isOn();
+    }
+
+    public void setConsentRequired(boolean consentRequired) {
+        consentRequiredSwitch.setOn(consentRequired);
+    }
+
+    public String getConsentText() {
+        return getInputValue(consentTextElement);
+    }
+
+    public void setConsentText(String consentText) {
+        setInputValue(consentTextElement, consentText);
+    }
+
+    public void setMapperType(String type) {
+        mapperTypeSelect.selectByVisibleText(type);
+    }
+    
+    public String getProperty() {
+        return getInputValue(propertyInput);
+    }
+    
+    public void setProperty(String value) {
+        setInputValue(propertyInput, value);
+    }
+
+    public String getUserAttribute() {
+        return getInputValue(userAttributeInput);
+    }
+
+    public void setUserAttribute(String value) {
+        setInputValue(userAttributeInput, value);
+    }
+
+    public String getUserSessionNote() {
+        return getInputValue(userSessionNoteInput);
+    }
+
+    public void setUserSessionNote(String value) {
+        setInputValue(userSessionNoteInput, value);
+    }
+
+    public boolean isMultivalued() {
+        return multivaluedInput.isOn();
+    }
+
+    public void setMultivalued(boolean value) {
+        multivaluedInput.setOn(value);
+    }
+
+    public String getRole() {
+        return getInputValue(roleInput);
+    }
+
+    public void setRole(String value) {
+        setInputValue(roleInput, value);
+    }
+
+    public String getNewRole() {
+        return getInputValue(newRoleInput);
+    }
+
+    public void setNewRole(String value) {
+        setInputValue(newRoleInput, value);
+    }
+
+    public String getTokenClaimName() {
+        return getInputValue(tokenClaimNameInput);
+    }
+
+    public void setTokenClaimName(String value) {
+        setInputValue(tokenClaimNameInput, value);
+    }
+
+    public String getTokenClaimValue() {
+        return getInputValue(tokenClaimValueInput);
+    }
+
+    public void setTokenClaimValue(String value) {
+        setInputValue(tokenClaimValueInput, value);
+    }
+
+    public String getClaimJSONType() {
+        return claimJSONTypeInput.getFirstSelectedOption().getText();
+    }
+
+    public void setClaimJSONType(String value) {
+        claimJSONTypeInput.selectByVisibleText(value);
+    }
+
+    public boolean isAddToIDToken() {
+        return addToIDTokenInput.isOn();
+    }
+
+    public void setAddToIDToken(boolean value) {
+        addToIDTokenInput.setOn(value);
+    }
+
+    public boolean isAddToAccessToken() {
+        return addToAccessTokenInput.isOn();
+    }
+
+    public void setAddToAccessToken(boolean value) {
+        addToAccessTokenInput.setOn(value);
+    }
+    
+    public boolean isFullGroupPath() {
+        return fullGroupPath.isOn();
+    }
+    
+    public void setFullGroupPath(boolean value) {
+        fullGroupPath.setOn(value);
+    }
+    
+    //SAML
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Role attribute name']//following-sibling::node()//input[@type='text']")
+    private WebElement roleAttributeNameInput;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Friendly Name']//following-sibling::node()//input[@type='text']")
+    private WebElement friendlyNameInput;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='SAML Attribute NameFormat']//following-sibling::node()//select")
+    private Select samlAttributeNameFormatSelect;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Single Role Attribute']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch singleRoleAttributeSwitch;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Attribute value']//following-sibling::node()//input[@type='text']")
+    private WebElement attributeValueInput;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Group attribute name']//following-sibling::node()//input[@type='text']")
+    private WebElement groupAttributeNameInput;
+    
+    @FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Single Group Attribute']//following-sibling::node()//div[@class='onoffswitch']")
+    private OnOffSwitch singleGroupAttributeSwitch;
+    
+    public void setRoleAttributeName(String value) {
+        setInputValue(roleAttributeNameInput, value);
+    }
+    
+    public void setFriendlyName(String value) {
+        setInputValue(friendlyNameInput, value);
+    }
+
+    public void setSamlAttributeNameFormat(String value) {
+        samlAttributeNameFormatSelect.selectByVisibleText(value);
+    }
+    
+    public void setSingleRoleAttribute(boolean value) {
+        singleRoleAttributeSwitch.setOn(value);
+    }
+    
+    public void setAttributeValue(String value) {
+        setInputValue(attributeValueInput, value);
+    }
+    
+    public void setGroupAttributeName(String value) {
+        setInputValue(groupAttributeNameInput, value);
+    }
+    
+    public void setSingleGroupAttribute(boolean value) {
+        singleGroupAttributeSwitch.setOn(value);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/MapperSettingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/MapperSettingsForm.java
new file mode 100644
index 0000000..09a9ba9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/mappers/MapperSettingsForm.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.mappers;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class MapperSettingsForm extends CreateClientMappersForm {
+
+    @FindBy(id = "protocol")
+    private WebElement protocolInput;
+    @FindBy(id = "mapperId")
+    private WebElement mapperIdInput;
+    @FindBy(id = "name")
+    private WebElement nameInput;
+    @FindBy(id = "mapperType")
+    private WebElement mapperTypeInput;
+    
+    public String getProtocol() {
+        return getInputValue(protocolInput);
+    }
+    
+    public String getMapperId() {
+        return getInputValue(mapperIdInput);
+    }
+    
+    public String getName() {
+        return getInputValue(nameInput);
+    }
+    
+    public String getMapperType() {
+        return getInputValue(mapperTypeInput);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/scope/ClientScopeForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/scope/ClientScopeForm.java
new file mode 100644
index 0000000..e76ef14
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/scope/ClientScopeForm.java
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.page.clients.scope;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientScopeForm extends Form {
+
+    @FindBy(className = "onoffswitch")
+    private OnOffSwitch fullScopeAllowedSwitch;
+    
+
+    public void setFullScopeAllowed(boolean value) {
+        fullScopeAllowedSwitch.setOn(value);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
index 4db28a0..53dae19 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
@@ -34,11 +34,10 @@ public class ModalDialog {
         waitUntilElement(cancelButton).is().present();
         cancelButton.click();
     }
-    
+
     public void setName(String name) {
         waitUntilElement(nameInput).is().present();
         nameInput.clear();
         nameInput.sendKeys(name);
     }
-
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
index d8c595e..52f0949 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java
@@ -51,7 +51,7 @@ public class OnOffSwitch {
 
     private void click() {
         waitUntilElement(root).is().present();
-        actions.moveToElement(root.findElements(By.tagName("span")).get(0))
+        actions.moveToElement(root.findElement(By.tagName("label")))
                 .click().build().perform();
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
index 9dc25c7..833acdd 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
@@ -1,7 +1,9 @@
 package org.keycloak.testsuite.console.page.roles;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -88,6 +90,14 @@ public class RoleCompositeRoles extends Form {
         button.click();
     }
 
+    public void addRealmRole(String role) {
+        addMissingRoles(availableRealmRolesSelect, addSelectedRealmRolesButton, Arrays.asList(role));
+    }
+    
+    public void addClientRole(String role) {
+        addMissingRoles(availableClientRolesSelect, addSelectedClientRolesButton, Arrays.asList(role));
+    }
+    
     protected void addMissingRoles(Select select, WebElement button, Collection<String> roles) {
         select.deselectAll();
         if (roles != null) { // if roles not provided, don't add any
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractAlert.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractAlert.java
index cad5e26..57d6bb7 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractAlert.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractAlert.java
@@ -1,6 +1,7 @@
 package org.keycloak.testsuite.page;
 
 import com.google.common.base.Predicate;
+import java.util.Arrays;
 import static org.jboss.arquillian.graphene.Graphene.waitModel;
 import org.jboss.arquillian.graphene.fragment.Root;
 import org.jboss.logging.Logger;
@@ -28,7 +29,7 @@ public abstract class AbstractAlert {
         waitModel().until(new Predicate<WebDriver>() {
             @Override
             public boolean apply(WebDriver input) {
-                return !getAttributeClass().endsWith("alert-");
+                return !Arrays.asList(getAttributeClass().split(" ")).contains("alert-");
             }
         });
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
index 0fb0efe..337a504 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
@@ -99,4 +99,4 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
         return adminClient.realm(testRealmPage.getAuthRealm());
     }
 
-}
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
index 4d0eed9..2a48dac 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
@@ -17,7 +17,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.auth.page.account.Account;
 import org.keycloak.testsuite.auth.page.account.Applications;
 import org.keycloak.testsuite.auth.page.login.OAuthGrant;
-import org.keycloak.testsuite.console.page.clients.ClientSettings;
+import org.keycloak.testsuite.console.page.clients.settings.ClientSettings;
 import org.keycloak.testsuite.console.page.clients.Clients;
 import org.keycloak.testsuite.console.page.events.Config;
 import org.keycloak.testsuite.console.page.events.LoginEvents;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
index ab63862..5b7888c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -12,7 +12,7 @@ import org.keycloak.testsuite.adapter.page.JSConsoleExample;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.auth.page.account.Applications;
 import org.keycloak.testsuite.auth.page.login.OAuthGrant;
-import org.keycloak.testsuite.console.page.clients.ClientSettings;
+import org.keycloak.testsuite.console.page.clients.settings.ClientSettings;
 import org.keycloak.testsuite.console.page.clients.Clients;
 import org.keycloak.testsuite.console.page.events.Config;
 import org.keycloak.testsuite.console.page.events.LoginEvents;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java
index 94e9b4b..a3a1463 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java
@@ -2,15 +2,40 @@ package org.keycloak.testsuite.console.clients;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertEquals;
 import org.junit.Before;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import static org.keycloak.testsuite.auth.page.login.OIDCLogin.OIDC;
+import static org.keycloak.testsuite.auth.page.login.OIDCLogin.SAML;
 import org.keycloak.testsuite.console.AbstractConsoleTest;
 import org.keycloak.testsuite.console.page.clients.Client;
 import org.keycloak.testsuite.console.page.clients.Clients;
 import org.keycloak.testsuite.console.page.clients.CreateClient;
+import org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.*;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_ASSERTION_CONSUMER_URL_POST;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_ASSERTION_CONSUMER_URL_REDIRECT;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_ASSERTION_SIGNATURE;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_AUTHNSTATEMENT;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_CLIENT_SIGNATURE;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_ENCRYPT;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_FORCE_NAME_ID_FORMAT;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_FORCE_POST_BINDING;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_MULTIVALUED_ROLES;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_NAME_ID_FORMAT;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_SERVER_SIGNATURE;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_SIGNATURE_ALGORITHM;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_SIGNATURE_CANONICALIZATION_METHOD;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_SINGLE_LOGOUT_SERVICE_URL_POST;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.SAMLClientSettingsForm.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsBooleanAttributes;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsListAttributes;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsStringAttributes;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
 
 /**
@@ -19,6 +44,9 @@ import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
  */
 public abstract class AbstractClientTest extends AbstractConsoleTest {
 
+    public final String TEST_CLIENT_ID = "test-client";
+    public final String TEST_REDIRECT_URIS = "http://example.test/app/*";
+    
     @Page
     protected Clients clientsPage;
     @Page
@@ -36,41 +64,141 @@ public abstract class AbstractClientTest extends AbstractConsoleTest {
         assertCurrentUrlEquals(clientsPage);
         clientsPage.table().createClient();
         createClientPage.form().setValues(client);
+        if (SAML.equals(client.getProtocol())) {
+            createClientPage.form().samlForm().setValues(client);
+        }
         createClientPage.form().save();
     }
 
-    public void deleteClientViaTable(String clientId) {
-        assertCurrentUrlEquals(clientsPage);
-        clientsPage.deleteClient(clientId);
-    }
-
-    public void deleteClientViaPage(String clientId) {
-        assertCurrentUrlEquals(clientsPage);
-        clientsPage.table().search(clientId);
-        clientsPage.table().clickClient(clientId);
-        clientPage.delete();
-    }
-
-    public static ClientRepresentation createClientRepresentation(String clientId, String... redirectUris) {
+    private static ClientRepresentation createClientRep(String clientId) {
         ClientRepresentation client = new ClientRepresentation();
         client.setClientId(clientId);
         client.setEnabled(true);
         client.setConsentRequired(false);
-        client.setStandardFlowEnabled(true);
-        client.setImplicitFlowEnabled(false);
-        client.setDirectAccessGrantsEnabled(true);
-        
+        return client;
+    }
+    
+    public static ClientRepresentation createOidcClientRep(OidcAccessType accessType, String clientId, String... redirectUris) {
+        ClientRepresentation client = createClientRep(clientId);
+       
         client.setProtocol(OIDC);
         
-        client.setBearerOnly(false);
-        client.setPublicClient(false);
-        client.setServiceAccountsEnabled(false);
+        switch (accessType) {
+            case BEARER_ONLY:
+                client.setBearerOnly(true);
+                break;
+            case PUBLIC:
+                client.setBearerOnly(false);
+                client.setPublicClient(true);
+                client.setStandardFlowEnabled(true);
+                client.setImplicitFlowEnabled(false);
+                client.setDirectAccessGrantsEnabled(true);
+                setRedirectUris(client, redirectUris);
+                break;
+            case CONFIDENTIAL:
+                client.setBearerOnly(false);
+                client.setPublicClient(false);
+                client.setStandardFlowEnabled(true);
+                client.setDirectAccessGrantsEnabled(true);
+                client.setServiceAccountsEnabled(true);
+                setRedirectUris(client, redirectUris);
+                break;
+        }
+        return client;
+    }
+    
+    public static ClientRepresentation createSamlClientRep(String clinetId) {
+        ClientRepresentation client = createClientRep(clinetId);
+        
+        client.setProtocol(SAML);
+        
+        client.setFrontchannelLogout(true);
+        client.setAttributes(getSAMLAttributes());
         
+        return client;
+    }
+    
+    private static void setRedirectUris(ClientRepresentation client, String... redirectUris) {
         List<String> redirectUrisList = new ArrayList();
         redirectUrisList.addAll(Arrays.asList(redirectUris));
         client.setRedirectUris(redirectUrisList);
-        
-        return client;
     }
+    
+    protected static void setExpectedWebOrigins(ClientRepresentation client) {
+        List<String> webOrigins = new ArrayList<>();
+        for (String redirectUri : client.getRedirectUris()) {
+            //parse webOrigin from redirectUri: take substring from index 0 to 
+            //first occurence of "/", excluded "http://" by starting search on index 7
+            webOrigins.add(redirectUri.substring(0, redirectUri.indexOf("/", 7)));
+        }
+        client.setWebOrigins(webOrigins);
+    }
+    
+    public ClientRepresentation findClientByClientId(String clientId) {
+        ClientRepresentation found = null;
+        for (ClientRepresentation clientRepresentation : testRealmResource().clients().findAll()) {
+            if (clientRepresentation.getClientId().equals(clientId)) {
+                found = clientRepresentation;
+                break;
+            }
+        }
+        return found;
+    }
+    
+    public void assertClientSettingsEqual(ClientRepresentation c1, ClientRepresentation c2) {
+        assertEqualsStringAttributes(c1.getClientId(), c2.getClientId());
+        assertEqualsStringAttributes(c1.getName(), c2.getName());
+        assertEqualsBooleanAttributes(c1.isEnabled(), c2.isEnabled());
+        assertEqualsBooleanAttributes(c1.isConsentRequired(), c2.isConsentRequired());
+        assertEqualsBooleanAttributes(c1.isDirectAccessGrantsEnabled(), c2.isDirectAccessGrantsEnabled());
+        assertEqualsStringAttributes(c1.getProtocol(), c2.getProtocol());
 
+        assertEqualsBooleanAttributes(c1.isBearerOnly(), c2.isBearerOnly());
+        assertEqualsBooleanAttributes(c1.isPublicClient(), c2.isPublicClient());
+        assertEqualsBooleanAttributes(c1.isSurrogateAuthRequired(), c2.isSurrogateAuthRequired());
+
+        assertEqualsBooleanAttributes(c1.isFrontchannelLogout(), c2.isFrontchannelLogout());
+
+        assertEqualsBooleanAttributes(c1.isServiceAccountsEnabled(), c2.isServiceAccountsEnabled());
+        assertEqualsListAttributes(c1.getRedirectUris(), c2.getRedirectUris());
+        assertEqualsStringAttributes(c1.getBaseUrl(), c2.getBaseUrl());
+        assertEqualsStringAttributes(c1.getAdminUrl(), c2.getAdminUrl());
+        assertEqualsListAttributes(c1.getWebOrigins(), c2.getWebOrigins());
+    }
+    
+    public void assertClientSamlAttributes(Map<String, String> expected, Map<String, String> actual) {
+        for (String key : expected.keySet()) {
+            assertEquals("Expected attribute " + key, expected.get(key), actual.get(key));
+        }
+    }
+    
+    protected static Map<String, String> getSAMLAttributes() {
+        Map<String, String> attributes = new HashMap<>();
+        attributes.put(SAML_ASSERTION_SIGNATURE, "true");
+        attributes.put(SAML_AUTHNSTATEMENT, "false");
+	attributes.put(SAML_CLIENT_SIGNATURE,	"true");
+	attributes.put(SAML_ENCRYPT, "true");
+	attributes.put(SAML_FORCE_POST_BINDING, "true");
+	attributes.put(SAML_MULTIVALUED_ROLES, "false");
+	attributes.put(SAML_SERVER_SIGNATURE,	"true");
+	attributes.put(SAML_SIGNATURE_ALGORITHM, "RSA_SHA512");
+	attributes.put(SAML_ASSERTION_CONSUMER_URL_POST, "http://example0.test");
+	attributes.put(SAML_ASSERTION_CONSUMER_URL_REDIRECT, "http://example1.test");
+	attributes.put(SAML_FORCE_NAME_ID_FORMAT, "true");
+	attributes.put(SAML_NAME_ID_FORMAT, "email");
+	attributes.put(SAML_SIGNATURE_CANONICALIZATION_METHOD, "http://www.w3.org/2001/10/xml-exc-c14n#WithComments");
+	attributes.put(SAML_SINGLE_LOGOUT_SERVICE_URL_POST, "http://example2.test");
+	attributes.put(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT, "http://example3.test");
+        return attributes;
+    }
+    
+    public ProtocolMapperRepresentation findClientMapperByName(String clientId, String mapperName) {
+        ProtocolMapperRepresentation found = null;
+        for (ProtocolMapperRepresentation mapper : testRealmResource().clients().get(clientId).getProtocolMappers().getMappers()) {
+            if (mapperName.equals(mapper.getName())) {
+                found = mapper;
+            }
+        }
+        return found;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientClusteringTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientClusteringTest.java
new file mode 100644
index 0000000..c2f8eec
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientClusteringTest.java
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.console.clients.AbstractClientTest.createOidcClientRep;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.clustering.ClientClustering;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientClusteringTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    private ClientRepresentation found;
+    
+    @Page
+    private ClientClustering clientClusteringPage;
+    
+    @Before
+    public void before() {
+        newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientClusteringPage.setId(found.getId());
+        clientClusteringPage.navigateTo();
+    }
+    
+    @Test
+    public void basicConfigurationTest() {
+        assertTrue(found.getNodeReRegistrationTimeout() == -1);
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("10", "Seconds");
+        clientClusteringPage.form().save();
+        assertAlertSuccess();
+        assertTrue(findClientByClientId(TEST_CLIENT_ID).getNodeReRegistrationTimeout() == 10);
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("10", "Minutes");
+        clientClusteringPage.form().save();
+        assertAlertSuccess();
+        assertTrue(findClientByClientId(TEST_CLIENT_ID).getNodeReRegistrationTimeout() == 600);
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("1", "Hours");
+        clientClusteringPage.form().save();
+        assertAlertSuccess();
+        assertTrue(findClientByClientId(TEST_CLIENT_ID).getNodeReRegistrationTimeout() == 3600);
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("1", "Days");
+        clientClusteringPage.form().save();
+        assertAlertSuccess();
+        assertTrue(findClientByClientId(TEST_CLIENT_ID).getNodeReRegistrationTimeout() == 86400);
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("", "Days");
+        clientClusteringPage.form().save();
+        assertAlertDanger();
+        
+        clientClusteringPage.form().setNodeReRegistrationTimeout("text", "Days");
+        clientClusteringPage.form().save();
+        assertAlertDanger();
+    }
+    
+    @Test
+    public void registerNodeTest() {
+        clientClusteringPage.form().addNode("new node");
+        assertAlertSuccess();
+        assertNotNull(findClientByClientId(TEST_CLIENT_ID).getRegisteredNodes().get("new node"));
+        
+        clientClusteringPage.form().addNode("");
+        assertAlertDanger();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientCredentialsTest.java
new file mode 100644
index 0000000..4030231
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientCredentialsTest.java
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.console.clients.AbstractClientTest.createOidcClientRep;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.credentials.ClientCredentials;
+import org.keycloak.testsuite.console.page.clients.credentials.ClientCredentialsGeneratePrivateKeys;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientCredentialsTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    
+    @Page
+    private ClientCredentials clientCredentialsPage;
+    @Page
+    private ClientCredentialsGeneratePrivateKeys generatePrivateKeysPage;
+    
+    @Before
+    public void before() {
+        newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientCredentialsPage.setId(found.getId());
+        clientCredentialsPage.navigateTo();
+    }
+    
+    @Test
+    public void regenerateSecret() {
+        clientCredentialsPage.form().selectClientIdAndSecret();
+        clientCredentialsPage.form().regenerateSecret();
+        assertAlertSuccess();
+    }
+    
+    @Test
+    public void regenerateRegistrationAccessToken() {
+        clientCredentialsPage.form().regenerateRegistrationAccessToken();
+        assertAlertSuccess();
+    }
+    
+    @Test
+    public void generateNewKeysAndCert() {
+        generatePrivateKeysPage.setId(clientCredentialsPage.getId());
+        clientCredentialsPage.form().selectSignedJwt();
+        clientCredentialsPage.form().generateNewKeysAndCert();
+        assertCurrentUrlEquals(generatePrivateKeysPage);
+        
+        generatePrivateKeysPage.generateForm().clickGenerateAndDownload();
+        assertAlertDanger();
+        
+//        generatePrivateKeysPage.generateForm().setKeyPassword("pass");
+//        generatePrivateKeysPage.generateForm().setStorePassword("pass2");
+//        assertAlertSuccess();//fails with phantomjs
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientInstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientInstallationTest.java
new file mode 100644
index 0000000..3ee6335
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientInstallationTest.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.console.clients.AbstractClientTest.createOidcClientRep;
+import org.keycloak.testsuite.console.page.clients.installation.ClientInstallation;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientInstallationTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    private ClientRepresentation found;
+    
+    @Page
+    private ClientInstallation clientInstallationPage;
+    
+    @Before
+    public void before() {
+        newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientInstallationPage.setId(found.getId());
+        clientInstallationPage.navigateTo();
+    }
+    
+    @Test
+    public void jsonTest() {
+        clientInstallationPage.form().setConfigFormat("Keycloak JSON");
+        assertTrue(clientInstallationPage.form().getTextareaContent().contains("\"realm\": \"test\""));
+    }
+    
+    @Test
+    public void wildflySubsystemTest() {
+        clientInstallationPage.form().setConfigFormat("Wildfly/EAP Subsystem XML");
+        assertTrue(clientInstallationPage.form().getTextareaContent().contains("<realm>test</realm>"));
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersOIDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersOIDCTest.java
new file mode 100644
index 0000000..084f317
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersOIDCTest.java
@@ -0,0 +1,378 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.mappers.ClientMapper;
+import org.keycloak.testsuite.console.page.clients.mappers.ClientMappers;
+import org.keycloak.testsuite.console.page.clients.mappers.CreateClientMappers;
+import static org.keycloak.testsuite.console.page.clients.mappers.CreateClientMappersForm.*;
+
+/**
+ * 
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientMappersOIDCTest extends AbstractClientTest {
+
+    private String id;
+    
+    @Page
+    private ClientMappers clientMappersPage;
+    @Page
+    private ClientMapper clientMapperPage;
+
+    @Page 
+    private CreateClientMappers createClientMappersPage;
+    
+    @Before
+    public void beforeClientMappersTest() {
+        ClientRepresentation newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        id = findClientByClientId(TEST_CLIENT_ID).getId();
+        clientMappersPage.setId(id);
+        clientMappersPage.navigateTo();
+    }
+    
+    private void setInitialValues(String name, boolean consentRequired, String consentText) {
+        createClientMappersPage.form().setName(name);
+        createClientMappersPage.form().setConsentRequired(consentRequired);
+        if (consentRequired) {
+            createClientMappersPage.form().setConsentText(consentText);
+        }
+    }
+    
+    @Test
+    public void testHardcodedRole() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("hardcoded role", true, "Consent Text");
+        createClientMappersPage.form().setMapperType(HARDCODED_ROLE);
+        createClientMappersPage.form().selectRole(REALM_ROLE, "offline_access", null);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "hardcoded role");
+        assertNotNull(found);
+        
+        assertTrue(found.isConsentRequired());
+        assertEquals("Consent Text", found.getConsentText());
+        assertEquals("oidc-hardcoded-role-mapper", found.getProtocolMapper());
+        Map<String, String> config = found.getConfig();
+        
+        assertEquals(1, config.size());
+        assertEquals("offline_access", config.get("role"));
+        
+        //edit
+        createClientMappersPage.form().selectRole(CLIENT_ROLE, "view-profile", "account");
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        config = findClientMapperByName(id, "hardcoded role").getConfig();
+        assertEquals("account.view-profile", config.get("role"));
+        
+        //delete
+        clientMapperPage.setMapperId(found.getId());
+        clientMapperPage.delete();
+        assertAlertSuccess();
+        
+        //check
+        assertNull(findClientMapperByName(id, "hardcoded role"));
+    }
+    
+    @Test
+    public void testHardcodedClaim() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("hardcoded claim", false, null);
+        createClientMappersPage.form().setMapperType(HARDCODED_CLAIM);
+        createClientMappersPage.form().setTokenClaimName("claim name");
+        createClientMappersPage.form().setTokenClaimValue("claim value");
+        createClientMappersPage.form().setClaimJSONType("long");
+        createClientMappersPage.form().setAddToIDToken(true);
+        createClientMappersPage.form().setAddToAccessToken(true);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "hardcoded claim");
+        assertNotNull(found);
+        
+        assertFalse(found.isConsentRequired());
+        assertEquals("oidc-hardcoded-claim-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("true", config.get("id.token.claim"));
+        assertEquals("true", config.get("access.token.claim"));
+        assertEquals("claim name", config.get("claim.name"));
+        assertEquals("claim value", config.get("claim.value"));
+        assertEquals("long", config.get("jsonType.label"));
+    }
+    
+    @Test
+    public void testUserSessionNote() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user session note", false, null);
+        createClientMappersPage.form().setMapperType(USER_SESSION_NOTE);
+        createClientMappersPage.form().setUserSessionNote("session note");
+        createClientMappersPage.form().setTokenClaimName("claim name");
+        createClientMappersPage.form().setClaimJSONType("int");
+        createClientMappersPage.form().setAddToIDToken(false);
+        createClientMappersPage.form().setAddToAccessToken(false);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user session note");
+        assertNotNull(found);
+        
+        assertFalse(found.isConsentRequired());
+        assertEquals("oidc-usersessionmodel-note-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertNull(config.get("id.token.claim"));
+        assertNull(config.get("access.token.claim"));
+        assertEquals("claim name", config.get("claim.name"));
+        assertEquals("session note", config.get("user.session.note"));
+        assertEquals("int", config.get("jsonType.label"));
+    }
+
+    @Test
+    public void testRoleName() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("role name", false, null);
+        createClientMappersPage.form().setMapperType(ROLE_NAME_MAPPER);
+        createClientMappersPage.form().setRole("offline_access");
+        createClientMappersPage.form().setNewRole("new role");
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "role name");
+        assertEquals("oidc-role-name-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("offline_access", config.get("role"));
+        assertEquals("new role", config.get("new.role.name"));
+    }
+
+    @Test
+    public void testUserAddress() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user address", false, null);
+        createClientMappersPage.form().setMapperType(USERS_FULL_NAME);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user address");
+        assertEquals("oidc-full-name-mapper", found.getProtocolMapper());
+    }
+    
+    @Test
+    public void testUserFullName() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user full name", false, null);
+        createClientMappersPage.form().setMapperType(USERS_FULL_NAME);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user full name");
+        assertEquals("oidc-full-name-mapper", found.getProtocolMapper());
+    }
+    
+    @Test
+    public void testUserAttribute() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user attribute", false, null);
+        createClientMappersPage.form().setMapperType(USER_ATTRIBUTE);
+        createClientMappersPage.form().setUserAttribute("user attribute");
+        createClientMappersPage.form().setMultivalued(true);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user attribute");
+        assertEquals("oidc-usermodel-attribute-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("true", config.get("multivalued"));
+        assertEquals("user attribute", config.get("user.attribute"));
+    }
+
+    @Test
+    public void testUserProperty() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user property", false, null);
+        createClientMappersPage.form().setMapperType(USER_PROPERTY);
+        createClientMappersPage.form().setProperty("property");
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user property");
+        assertEquals("oidc-usermodel-property-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("property", config.get("user.attribute"));
+    }
+    
+    @Test
+    public void testGroupMembership() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("group membership", false, null);
+        createClientMappersPage.form().setMapperType(GROUP_MEMBERSHIP);
+        createClientMappersPage.form().setFullGroupPath(true);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "group membership");
+        assertEquals("oidc-group-membership-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("true", config.get("full.path"));
+    }
+    
+    @Test
+    public void testEditMapper() {
+        //prepare data
+        ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation();
+        mapper.setName("mapper name");
+        mapper.setConsentRequired(true);
+        mapper.setConsentText("consent text");
+        mapper.setProtocol("openid-connect");
+        mapper.setProtocolMapper("oidc-usersessionmodel-note-mapper");
+        
+        Map<String, String> config = new HashMap<>();
+        config.put("access.token.claim", "true");
+        config.put("id.token.claim", "true");
+        config.put("claim.name", "claim name");
+        config.put("jsonType.label", "String");
+        config.put("user.session.note", "session note");
+        
+        mapper.setConfig(config);
+        
+        //insert data
+        testRealmResource().clients().get(id).getProtocolMappers().createMapper(mapper).close();
+        
+        //check form
+        clientMapperPage.setId(id);
+        String mapperId = findClientMapperByName(id, "mapper name").getId();
+        clientMapperPage.setMapperId(mapperId);
+        clientMapperPage.navigateTo();
+        
+        assertEquals("openid-connect", clientMapperPage.form().getProtocol());
+        assertEquals(mapperId, clientMapperPage.form().getMapperId());
+        assertEquals("mapper name", clientMapperPage.form().getName());
+        assertTrue(clientMapperPage.form().isConsentRequired());
+        assertEquals("consent text", clientMapperPage.form().getConsentText());
+        assertEquals("User Session Note", clientMapperPage.form().getMapperType());
+        assertEquals("session note", clientMapperPage.form().getUserSessionNote());
+        assertEquals("claim name", clientMapperPage.form().getTokenClaimName());
+        assertEquals("String", clientMapperPage.form().getClaimJSONType());
+        assertTrue(clientMapperPage.form().isAddToIDToken());
+        assertTrue(clientMapperPage.form().isAddToAccessToken());
+        
+        //edit
+        clientMapperPage.form().setConsentRequired(false);
+        clientMapperPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        assertFalse(findClientMapperByName(id, "mapper name").isConsentRequired());
+    }
+    
+    @Test
+    public void testAddBuiltin() {
+        clientMappersPage.mapperTable().addBuiltin();
+        clientMappersPage.mapperTable().checkBuiltinMapper("locale");
+        clientMappersPage.mapperTable().clickAddSelectedBuiltinMapper();
+        assertAlertSuccess();
+        
+        assertTrue("Builtin mapper \"locale\" should be present.", isMapperPresent("locale"));
+        
+        clientMappersPage.mapperTable().deleteMapper("locale");
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
+        
+        assertFalse("Builtin mapper \"locale\" should not be present.", isMapperPresent("locale"));
+    }
+    
+    private boolean isMapperPresent(String name) {
+        List<ProtocolMapperRepresentation> mappers = testRealmResource().clients().get(id).getProtocolMappers().getMappers();
+        boolean found = false;
+        for (ProtocolMapperRepresentation mapper : mappers) {
+            if (name.equals(mapper.getName())) {
+                found = true;
+            }
+        }
+        return found;
+    }
+    
+    @Test
+    public void testCreateMapperInvalidValues() {
+        //empty mapper type
+        clientMappersPage.mapperTable().createMapper();
+        createClientMappersPage.form().save();
+        assertAlertDanger();
+        
+        //empty name
+        createClientMappersPage.form().setMapperType(HARDCODED_ROLE);
+        createClientMappersPage.form().save();
+        assertAlertDanger();
+        
+        createClientMappersPage.form().setName("");
+        createClientMappersPage.form().save();
+        assertAlertDanger();
+        
+        createClientMappersPage.form().setName("name");
+        createClientMappersPage.form().setName("");
+        createClientMappersPage.form().save();
+        assertAlertDanger();
+        
+        //existing name
+        createClientMappersPage.form().setName("email");
+        createClientMappersPage.form().save();
+        assertAlertDanger();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersSAMLTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersSAMLTest.java
new file mode 100644
index 0000000..7905bbd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientMappersSAMLTest.java
@@ -0,0 +1,212 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import java.util.Map;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.testsuite.console.page.clients.mappers.ClientMapper;
+import org.keycloak.testsuite.console.page.clients.mappers.ClientMappers;
+import org.keycloak.testsuite.console.page.clients.mappers.CreateClientMappers;
+import static org.keycloak.testsuite.console.page.clients.mappers.CreateClientMappersForm.*;
+
+/**
+ * 
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientMappersSAMLTest extends AbstractClientTest {
+
+    private String id;
+    
+    @Page
+    private ClientMappers clientMappersPage;
+    @Page
+    private ClientMapper clientMapperPage;
+
+    @Page 
+    private CreateClientMappers createClientMappersPage;
+    
+    @Before
+    public void beforeClientMappersTest() {
+        ClientRepresentation newClient = createSamlClientRep(TEST_CLIENT_ID);
+        testRealmResource().clients().create(newClient).close();
+        
+        id = findClientByClientId(TEST_CLIENT_ID).getId();
+        clientMappersPage.setId(id);
+        clientMappersPage.navigateTo();
+    }
+    
+    private void setInitialValues(String name, boolean consentRequired, String consentText) {
+        createClientMappersPage.form().setName(name);
+        createClientMappersPage.form().setConsentRequired(consentRequired);
+        if (consentRequired) {
+            createClientMappersPage.form().setConsentText(consentText);
+        }
+    }
+    
+    @Test
+    public void testRoleName() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("role name", false, null);
+        createClientMappersPage.form().setMapperType(ROLE_NAME_MAPPER);
+        createClientMappersPage.form().setRole("offline_access");
+        createClientMappersPage.form().setNewRole("new role");
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "role name");
+        assertEquals("saml-role-name-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("offline_access", config.get("role"));
+        assertEquals("new role", config.get("new.role.name"));
+    }
+    
+    @Test
+    public void testRoleList() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("new role list", false, null);
+        createClientMappersPage.form().setMapperType(ROLE_LIST);
+        createClientMappersPage.form().setRoleAttributeName("role attribute name");
+        createClientMappersPage.form().setFriendlyName("friendly name");
+        createClientMappersPage.form().setSamlAttributeNameFormat("URI Reference");
+        createClientMappersPage.form().setSingleRoleAttribute(true);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "new role list");
+        assertNotNull(found);
+        
+        assertFalse(found.isConsentRequired());
+        assertEquals("saml-role-list-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("role attribute name", config.get("attribute.name"));
+        assertEquals("URI Reference", config.get("attribute.nameformat"));
+        assertEquals("friendly name", config.get("friendly.name"));
+        assertEquals("true", config.get("single"));
+    }
+    
+    @Test
+    public void testUserProperty() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user property", false, null);
+        createClientMappersPage.form().setMapperType(USER_PROPERTY);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user property");
+        assertEquals("saml-user-property-mapper", found.getProtocolMapper());
+    }
+    
+    @Test
+    public void testUserSessionNote() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("user session note", false, null);
+        createClientMappersPage.form().setMapperType(USER_SESSION_NOTE);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "user session note");
+        assertNotNull(found);
+        
+        assertFalse(found.isConsentRequired());
+        assertEquals("saml-user-session-note-mapper", found.getProtocolMapper());
+    }
+
+    @Test
+    public void testHardcodedAttribute() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("hardcoded attribute", false, null);
+        createClientMappersPage.form().setMapperType(HARDCODED_ATTRIBUTE);
+        createClientMappersPage.form().setAttributeValue("attribute value");
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "hardcoded attribute");
+        assertNotNull(found);
+        
+        assertFalse(found.isConsentRequired());
+        assertEquals("saml-hardcode-attribute-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("attribute value", config.get("attribute.value"));
+    }
+
+    @Test
+    public void testGroupList() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("group list", false, null);
+        createClientMappersPage.form().setMapperType(GROUP_LIST);
+        createClientMappersPage.form().setGroupAttributeName("group attribute name");
+        createClientMappersPage.form().setSingleGroupAttribute(true);
+        createClientMappersPage.form().setFullGroupPath(true);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "group list");
+        assertEquals("saml-group-membership-mapper", found.getProtocolMapper());
+        
+        Map<String, String> config = found.getConfig();
+        assertEquals("true", config.get("full.path"));
+        assertEquals("true", config.get("single"));
+        assertEquals("group attribute name", config.get("attribute.name"));
+    }
+    
+    @Test
+    public void testHardcodedRole() {
+        //create
+        clientMappersPage.mapperTable().createMapper();
+        setInitialValues("hardcoded role", false, null);
+        createClientMappersPage.form().setMapperType(HARDCODED_ROLE_SAML);
+        createClientMappersPage.form().selectRole(REALM_ROLE, "offline_access", null);
+        createClientMappersPage.form().save();
+        assertAlertSuccess();
+        
+        //check
+        ProtocolMapperRepresentation found = findClientMapperByName(id, "hardcoded role");
+        assertNotNull(found);
+        
+        assertEquals("saml-hardcode-role-mapper", found.getProtocolMapper());
+
+        Map<String, String> config = found.getConfig();
+        assertEquals(1, config.size());
+        assertEquals("offline_access", config.get("role"));
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java
index 0142911..92d144c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java
@@ -1,22 +1,29 @@
 package org.keycloak.testsuite.console.clients;
 
+import java.util.List;
 import org.jboss.arquillian.graphene.page.Page;
 import org.junit.Test;
 import org.keycloak.testsuite.console.page.users.UserRoleMappingsForm;
 
 import static org.junit.Assert.*;
+import org.junit.Before;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.testsuite.console.page.clients.ClientRole;
-import org.keycloak.testsuite.console.page.clients.ClientRoles;
-import org.keycloak.testsuite.console.page.clients.CreateClientRole;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.roles.ClientRole;
+import org.keycloak.testsuite.console.page.clients.roles.ClientRoles;
+import org.keycloak.testsuite.console.page.clients.roles.CreateClientRole;
 import org.keycloak.testsuite.console.page.users.User;
+import org.keycloak.testsuite.util.URLAssert;
 
 /**
  * Created by fkiss.
  */
 public class ClientRolesTest extends AbstractClientTest {
 
+    private String id;
+    private final String TEST_CLIENT_ROLE_NAME = "test-client-role";
+    
     @Page
     private ClientRoles clientRolesPage;
     @Page
@@ -30,10 +37,22 @@ public class ClientRolesTest extends AbstractClientTest {
     @Page
     private UserRoleMappingsForm userRolesPage;
 
-    public void addClientRole(RoleRepresentation roleRep) {
-//        assertCurrentUrl(clientRoles);
+    @Before
+    public void beforeClientRolesTest() {
+        ClientRepresentation newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        id = findClientByClientId(TEST_CLIENT_ID).getId();
+        clientPage.setId(id);
+        clientRolePage.setId(id);
+        clientRolesPage.setId(id);
+        createClientRolePage.setId(id);
+        
+        clientPage.navigateTo();
+    }
+    
+    public void addNewClientRole(RoleRepresentation roleRep) {
         clientRolesPage.roles().addRole();
-//        assertCurrentUrl(createClientRole); // can't do this, need client id to build uri
         createClientRolePage.form().setBasicAttributes(roleRep);
         createClientRolePage.form().save();
         assertAlertSuccess();
@@ -42,25 +61,32 @@ public class ClientRolesTest extends AbstractClientTest {
     }
 
     @Test
-    public void testAddClientRole() {
-        ClientRepresentation newClient = createClientRepresentation("test-client1", "http://example.com/*");
-        RoleRepresentation newRole = new RoleRepresentation("client-role", "", false);
-
-        createClient(newClient);
-        assertAlertSuccess();
-
+    public void testCRUDClientRole() {
+        RoleRepresentation newRole = new RoleRepresentation(TEST_CLIENT_ROLE_NAME, "description", false);
+                
         clientPage.tabs().roles();
-        addClientRole(newRole);
-
-        clientRolePage.backToClientRolesViaBreadcrumb();
-        assertFalse(clientRolesPage.roles().getRolesFromTableRows().isEmpty());
+        addNewClientRole(newRole);
 
-        configure().clients();
-        clientsPage.table().search(newClient.getClientId());
-        clientsPage.table().deleteClient(newClient.getClientId());
-        modalDialog.confirmDeletion();
+        List<RoleRepresentation> clientRoles = testRealmResource().clients().get(id).roles().list();
+        assertEquals("Client roles should contain exactly 1 role.", 1, clientRoles.size());
+        RoleRepresentation role = clientRoles.get(0);
+        assertEquals(TEST_CLIENT_ROLE_NAME, role.getName());
+        assertEquals("description", role.getDescription());
+        assertFalse(role.isScopeParamRequired());
+        assertFalse(role.isComposite());
+        assertNull(role.getComposites());
+        
+        //edit
+        clientRolesPage.navigateTo();
+        clientRolesPage.roles().editRole(TEST_CLIENT_ROLE_NAME);
+        
+        clientRolePage.setRoleId(role.getId());
+        URLAssert.assertCurrentUrlEquals(clientRolePage);
+        
+        //delete
+        clientRolePage.delete();
         assertAlertSuccess();
-        assertNull(clientsPage.table().findClient(newClient.getClientId()));
+        assertTrue("Role should be deleted.", testRealmResource().clients().get(id).roles().list().isEmpty());
     }
 
 //    @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java
new file mode 100644
index 0000000..da68ca3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import java.util.List;
+import java.util.Map;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientMappingsRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.MappingsRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import static org.keycloak.testsuite.console.clients.AbstractClientTest.createOidcClientRep;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.scope.ClientScope;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientScopeTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    private ClientRepresentation found;
+    
+    @Page
+    private ClientScope clientScopePage;
+    
+    @Before
+    public void before() {
+        newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientScopePage.setId(found.getId());
+        clientScopePage.navigateTo();
+    }
+    
+    @Test
+    public void clientScopeTest() {
+        assertTrue(found.isFullScopeAllowed());
+        clientScopePage.scopeForm().setFullScopeAllowed(false);
+        assertAlertSuccess();
+        
+        found = findClientByClientId(TEST_CLIENT_ID);
+        assertFalse(found.isFullScopeAllowed());
+        assertNull(getAllMappingsRepresentation().getRealmMappings());
+        assertNull(getAllMappingsRepresentation().getClientMappings());
+        
+        clientScopePage.roleForm().addRealmRole("offline_access");
+        assertAlertSuccess();
+        
+        clientScopePage.roleForm().selectClientRole("account");
+        clientScopePage.roleForm().addClientRole("view-profile");
+        assertAlertSuccess();
+        
+        found = findClientByClientId(TEST_CLIENT_ID);
+        List<RoleRepresentation> realmMappings = getAllMappingsRepresentation().getRealmMappings();
+        assertEquals(1, realmMappings.size());
+        assertEquals("offline_access", realmMappings.get(0).getName());
+        Map<String, ClientMappingsRepresentation> clientMappings = getAllMappingsRepresentation().getClientMappings();
+        assertEquals(1, clientMappings.size());
+        assertEquals("view-profile", clientMappings.get("account").getMappings().get(0).getName());
+        
+//        clientScopePage.roleForm().removeAssignedRole("offline_access");
+//        assertAlertSuccess();//fails with phantomjs
+//        clientScopePage.roleForm().removeAssignedClientRole("view-profile");
+//        assertAlertSuccess();//fails with phantomjs
+//        
+//        assertNull(getAllMappingsRepresentation().getRealmMappings());
+//        assertNull(getAllMappingsRepresentation().getClientMappings());
+    }
+    
+    private MappingsRepresentation getAllMappingsRepresentation() {
+        return testRealmResource().clients().get(found.getId()).getScopeMappings().getAll();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java
index 9f1fd62..da601e7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java
@@ -17,19 +17,17 @@
  */
 package org.keycloak.testsuite.console.clients;
 
+import java.util.ArrayList;
+import java.util.List;
 import javax.ws.rs.core.Response;
 import org.jboss.arquillian.graphene.page.Page;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 import org.junit.Test;
 
 import org.keycloak.representations.idm.ClientRepresentation;
 import static org.keycloak.testsuite.admin.ApiUtil.getCreatedId;
-import org.keycloak.testsuite.console.page.clients.ClientSettings;
-import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsBooleanAttributes;
-import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsListAttributes;
-import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsStringAttributes;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.*;
+import org.keycloak.testsuite.console.page.clients.settings.ClientSettings;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
 import org.keycloak.testsuite.util.Timer;
 
@@ -45,56 +43,102 @@ public class ClientSettingsTest extends AbstractClientTest {
 
     private ClientRepresentation newClient;
 
+    @Test
     public void crudOIDCConfidential() {
-        newClient = createClientRepresentation("oidc-confidential", "http://example.test/app/*");
+        newClient = createOidcClientRep(CONFIDENTIAL, "oidc-confidential", TEST_REDIRECT_URIS);
         createClient(newClient);
         assertAlertSuccess();
 
-        clientPage.backToClientsViaBreadcrumb();
-        assertCurrentUrlEquals(clientsPage);
-        assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
-
+        setExpectedWebOrigins(newClient);
+        
         // read & verify
-        clientsPage.table().clickClient(newClient);
-        ClientRepresentation found = clientSettingsPage.form().getValues();
+        ClientRepresentation found = findClientByClientId(newClient.getClientId());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
         assertClientSettingsEqual(newClient, found);
-
+        
         // update & verify
-        // TODO change attributes, add redirect uris and weborigins
+        newClient.setClientId("oidc-confidential-updated");
+        newClient.setName("updatedName");
+        
+        List<String> redirectUris = new ArrayList<>();
+        redirectUris.add("http://example2.test/app/*");
+        redirectUris.add("http://example2.test/app2/*");
+        redirectUris.add("http://example3.test/app/*");
+        newClient.setRedirectUris(redirectUris);
+        
+        List<String> webOrigins = new ArrayList<>();
+        webOrigins.clear();
+        webOrigins.add("http://example2.test");
+        webOrigins.add("http://example3.test");
+        newClient.setWebOrigins(webOrigins);
+        
+        clientSettingsPage.form().setClientId("oidc-confidential-updated");
+        clientSettingsPage.form().setName("updatedName");
+        clientSettingsPage.form().setRedirectUris(redirectUris);
+        clientSettingsPage.form().setWebOrigins(webOrigins);
+        clientSettingsPage.form().save();
+        assertAlertSuccess();
+        
+        found = findClientByClientId(newClient.getClientId());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+        assertClientSettingsEqual(newClient, found);
+
         // delete
-        // TODO
-        clientPage.backToClientsViaBreadcrumb();
+        clientPage.delete();
+        assertAlertSuccess();
+        found = findClientByClientId(newClient.getClientId());
+        assertNull("Deleted client " + newClient.getClientId() + " was found.", found);
     }
 
+    @Test
     public void createOIDCPublic() {
-        newClient = createClientRepresentation("oidc-public", "http://example.test/app/*");
-        newClient.setPublicClient(true);
+        newClient = createOidcClientRep(PUBLIC, "oidc-public", TEST_REDIRECT_URIS);
         createClient(newClient);
         assertAlertSuccess();
 
-        clientPage.backToClientsViaBreadcrumb();
-        assertCurrentUrlEquals(clientsPage);
-        assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
+        setExpectedWebOrigins(newClient);
+        
+        ClientRepresentation found = findClientByClientId(newClient.getClientId());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+        assertClientSettingsEqual(newClient, found);
     }
+    
+    @Test
+    public void createOIDCPublicWithoutRedirectURIs() {
+        newClient = createOidcClientRep(PUBLIC, "oidc-public");
+        newClient.setStandardFlowEnabled(false);
+        createClient(newClient);
+        assertAlertSuccess();
 
+        ClientRepresentation found = findClientByClientId(newClient.getClientId());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+        assertClientSettingsEqual(newClient, found);
+    }
+
+    @Test
     public void createOIDCBearerOnly() {
-        newClient = createClientRepresentation("oidc-bearer-only", "http://example.test/app/*");
-        newClient.setBearerOnly(true);
+        newClient = createOidcClientRep(BEARER_ONLY, "oidc-bearer-only");
         createClient(newClient);
         assertAlertSuccess();
 
-        clientPage.backToClientsViaBreadcrumb();
-        assertCurrentUrlEquals(clientsPage);
-        assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
+        ClientRepresentation found = findClientByClientId(newClient.getClientId());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+        assertClientSettingsEqual(newClient, found);
     }
 
     @Test
-    public void successfulCRUD() {
-        crudOIDCConfidential();
-        createOIDCPublic();
-        createOIDCBearerOnly();
-    }
+    public void createSAML() {
+        newClient = createSamlClientRep("saml");
+        createClient(newClient);
+        assertAlertSuccess();
 
+        ClientRepresentation found = findClientByClientId(newClient.getClientId());
+        System.out.println("...." + found.isFrontchannelLogout());
+        assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+        assertClientSettingsEqual(newClient, found);
+        assertClientSamlAttributes(getSAMLAttributes(), found.getAttributes());
+    }
+    
     @Test
     public void invalidSettings() {
         clientsPage.table().createClient();
@@ -106,32 +150,9 @@ public class ClientSettingsTest extends AbstractClientTest {
         assertAlertDanger();
     }
 
-    public void assertClientSettingsEqual(ClientRepresentation c1, ClientRepresentation c2) {
-        assertEqualsStringAttributes(c1.getClientId(), c2.getClientId());
-        assertEqualsStringAttributes(c1.getName(), c2.getName());
-        assertEqualsBooleanAttributes(c1.isEnabled(), c2.isEnabled());
-        assertEqualsBooleanAttributes(c1.isConsentRequired(), c2.isConsentRequired());
-        assertEqualsBooleanAttributes(c1.isStandardFlowEnabled(), c2.isStandardFlowEnabled());
-        assertEqualsBooleanAttributes(c1.isImplicitFlowEnabled(), c2.isImplicitFlowEnabled());
-        assertEqualsBooleanAttributes(c1.isDirectAccessGrantsEnabled(), c2.isDirectAccessGrantsEnabled());
-        assertEqualsStringAttributes(c1.getProtocol(), c2.getProtocol());
-
-        assertEqualsBooleanAttributes(c1.isBearerOnly(), c2.isBearerOnly());
-        assertEqualsBooleanAttributes(c1.isPublicClient(), c2.isPublicClient());
-        assertEqualsBooleanAttributes(c1.isSurrogateAuthRequired(), c2.isSurrogateAuthRequired());
-
-        assertEqualsBooleanAttributes(c1.isFrontchannelLogout(), c2.isFrontchannelLogout());
-
-        assertEqualsBooleanAttributes(c1.isServiceAccountsEnabled(), c2.isServiceAccountsEnabled());
-        assertEqualsListAttributes(c1.getRedirectUris(), c2.getRedirectUris());
-        assertEqualsStringAttributes(c1.getBaseUrl(), c2.getBaseUrl());
-        assertEqualsStringAttributes(c1.getAdminUrl(), c2.getAdminUrl());
-        assertEqualsListAttributes(c1.getWebOrigins(), c2.getWebOrigins());
-    }
-
 //    @Test
     public void createInconsistentClient() {
-        ClientRepresentation c = createClientRepresentation("inconsistent_client");
+        ClientRepresentation c = createOidcClientRep(CONFIDENTIAL, "inconsistent_client");
         c.setPublicClient(true);
         c.setBearerOnly(true);
 
@@ -147,7 +168,7 @@ public class ClientSettingsTest extends AbstractClientTest {
     public void createClients(String clientIdPrefix, int count) {
         for (int i = 0; i < count; i++) {
             String clientId = String.format("%s%02d", clientIdPrefix, i);
-            ClientRepresentation cr = createClientRepresentation(clientId, "http://example.test/*");
+            ClientRepresentation cr = createOidcClientRep(CONFIDENTIAL, clientId, "http://example.test/*");
             Timer.time();
             Response r = testRealmResource().clients().create(cr);
             r.close();
@@ -161,5 +182,4 @@ public class ClientSettingsTest extends AbstractClientTest {
         clientsPage.navigateTo();
         pause(120000);
     }
-
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java
new file mode 100644
index 0000000..d9e64a7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientsTest.java
@@ -0,0 +1,74 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.console.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
+import org.keycloak.testsuite.console.page.clients.settings.ClientSettings;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+
+/**
+ *
+ * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ */
+public class ClientsTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    
+    @Page
+    private ClientSettings clientSettingsPage;
+    
+    @Before
+    public void beforeClientsTest() {
+        newClient = createOidcClientRep(CONFIDENTIAL, TEST_CLIENT_ID, TEST_REDIRECT_URIS);
+        testRealmResource().clients().create(newClient).close();
+        
+        ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientSettingsPage.setId(found.getId());
+    }
+    
+    @Test
+    public void clientsCRUD() {
+        //create
+        clientsPage.table().createClient();
+        assertCurrentUrlEquals(createClientPage);
+        
+        //edit
+        clientsPage.navigateTo();
+        clientsPage.table().editClient(TEST_CLIENT_ID);
+        assertEquals(TEST_CLIENT_ID, clientSettingsPage.form().getClientId());
+        
+        //delete
+        clientsPage.navigateTo();
+        clientsPage.table().deleteClient(TEST_CLIENT_ID);
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
+        
+        ClientRepresentation found = findClientByClientId(TEST_CLIENT_ID);
+        assertNull("Deleted client " + TEST_CLIENT_ID + " was found.", found);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java
index 74418dd..c257564 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java
@@ -18,6 +18,7 @@ import javax.ws.rs.core.Response;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
+import static org.keycloak.testsuite.console.page.clients.CreateClientForm.OidcAccessType.CONFIDENTIAL;
 
 
 /**
@@ -48,7 +49,7 @@ public class AdminEventsTest extends AbstractConsoleTest {
 
     @Test
     public void clientsAdminEventsTest() {
-        newClient = AbstractClientTest.createClientRepresentation("test_client", "http://example.test/test_client/*");
+        newClient = AbstractClientTest.createOidcClientRep(CONFIDENTIAL, "test_client", "http://example.test/test_client/*");
         Response response = clientsPage.clientsResource().create(newClient);
         String id = ApiUtil.getCreatedId(response);
         response.close();