keycloak-aplcache

resolve conflict

3/30/2016 7:23:18 PM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
index 93be310..0621b9b 100644
--- a/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
@@ -105,4 +105,37 @@ public class EventRepresentation {
     public void setDetails(Map<String, String> details) {
         this.details = details;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        EventRepresentation that = (EventRepresentation) o;
+
+        if (time != that.time) return false;
+        if (type != null ? !type.equals(that.type) : that.type != null) return false;
+        if (realmId != null ? !realmId.equals(that.realmId) : that.realmId != null) return false;
+        if (clientId != null ? !clientId.equals(that.clientId) : that.clientId != null) return false;
+        if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false;
+        if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) return false;
+        if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) return false;
+        if (error != null ? !error.equals(that.error) : that.error != null) return false;
+        return !(details != null ? !details.equals(that.details) : that.details != null);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (time ^ (time >>> 32));
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31 * result + (realmId != null ? realmId.hashCode() : 0);
+        result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
+        result = 31 * result + (userId != null ? userId.hashCode() : 0);
+        result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0);
+        result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0);
+        result = 31 * result + (error != null ? error.hashCode() : 0);
+        result = 31 * result + (details != null ? details.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
index e821985..0563112 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
@@ -129,7 +129,7 @@ public class GroupResource {
             child = realm.createGroup(rep.getName());
             updateGroup(rep, child);
             URI uri = uriInfo.getBaseUriBuilder()
-                                           .path(uriInfo.getMatchedURIs().get(1))
+                                           .path(uriInfo.getMatchedURIs().get(2))
                                            .path(child.getId()).build();
             builder.status(201).location(uri);
             adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index de8ba88..1d8ff36 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -35,11 +35,11 @@
     <name>Keycloak Arquillian Integration TestSuite</name>
     
     <properties>
-        
+
         <containers.home>${project.build.directory}/containers</containers.home>
         <auth.server.java.home>${java.home}</auth.server.java.home>
         <app.server.java.home>${java.home}</app.server.java.home>
-        
+
         <!--component versions-->
         <arquillian-core.version>1.1.11.Final</arquillian-core.version>
         <selenium.version>2.52.0</selenium.version>
@@ -77,7 +77,7 @@
                 <groupId>org.wildfly</groupId>
                 <artifactId>wildfly-arquillian-container-managed</artifactId>
                 <version>${arquillian-wildfly-container.version}</version>
-            </dependency>        
+            </dependency>
         </dependencies>
     </dependencyManagement>
     
@@ -102,6 +102,7 @@
     <modules>
         <module>servers</module>
         <module>tests</module>
+        <module>test-apps</module>
     </modules>
-    
+
 </project>
diff --git a/testsuite/integration-arquillian/README.md b/testsuite/integration-arquillian/README.md
index 1adc054..4a0cfa1 100644
--- a/testsuite/integration-arquillian/README.md
+++ b/testsuite/integration-arquillian/README.md
@@ -1,33 +1,36 @@
 # Keycloak Arquillian Integration Testsuite
 
-## Container Lifecycles
+## Overview
+
+For overview see the **Modules Overview** section at the bottom of this README.
 
-### Keycloak Auth Server
 
-There is only one instance of Keycloak server running during a single test run.
-It is automatically started by Arquillian on the `BeforeSuite` event and stopped `AfterSuite`.
+## Container Lifecycles
+
+### Auth Server
 
-The type of container can be determined by property `-Dauth.server.container`. Default value is `auth-server-undertow`, 
-other options are: `auth-server-wildfly` and `auth-server-eap7`. The values correspond to Arquillian *container qualifiers* in `arquillian.xml` config file.
+Keycloak server is automatically started by the testsuite on the `BeforeSuite` event and stopped on `AfterSuite` event.
 
-**Note 1:** For the non-default options it's necessary to build a corresponding server module prior to running any of the test modules.
-This can be done by building the server module directly (from `servers/wildfly`/`servers/eap7`), 
-or by activating `auth-server-wildfly`/`auth-server-eap7` profile when building from the top level module.
+By default the server runs in embedded Undertow.
 
-**Note 2:** Most server-side configurations are done during the build of the server module
-and included in the output artifact - which is then consumed by the test modules( if a corresponding profile is activated).
-To reflect a change in server config in the test (e.g. a datasource) it's necessary to rebuild the server module after each change.
+#### Wildfly/EAP
 
-#### Migration
+Testsuite supports running server on Wildfly/EAP. For this it's necessary to:
+- build the project including the `distribution` module
+ (artifact `keycloak-server-dist`/`-overlay` needs to be available before running the testsuite),
+- activate profile `auth-server-wildfly` or `auth-server-eap7`.
 
-Migration tests can be enabled by setting `-Dmigrated.auth.server.version` property. Supported versions can be found at the bottom of `tests/pom.xml`.
-When enabled, the `AuthServerTestEnricher` class will start and stop the selected migrated instance 
-*before* the current auth server instance is started.
+[More details...](servers/auth-server/README.md)
 
 #### Cluster Setup
 
-Cluster setup can be enabled with profile `auth-server-wildfly-cluster`.
-(It is also necessary to build the server modules with this profile before running the test. See *Notes 1 and 2* above.)
+The cluster setup for server can be enabled by activating profile `auth-server-cluster`.
+
+The cluster setup is not supported for server on Undetow. Profile `auth-server-wildfly` or `auth-server-eap` needs to be activated.
+
+The setup includes:
+- a `mod_cluster` load balancer on Wildfly
+- two clustered nodes of Keycloak server on Wildfly/EAP
 
 Clustering tests require MULTICAST to be enabled on machine's `loopback` network interface.
 This can be done by running the following commands under root privileges:
@@ -36,20 +39,20 @@ route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
 ifconfig lo multicast
 ```
 
-### App Servers
+### App Servers / Adapter Tests
 
 Lifecycle of application server is always tied to a particular TestClass.
 
 Each *adapter* test class is annotated by `@AppServerContainer("app-server-*")` annotation 
 that links it to a particular Arquillian container in `arquillian.xml`.
-The `AppServerTestEnricher` then ensures the server is started before and stopped after all tests methods in the class.
+The `AppServerTestEnricher` then ensures the server is started during `BeforeClass` event and stopped during `AfterClass` event for that particular test class. 
 In case the `@AppServerContainer` annotation has no value it's assumed that the application container 
-is the same as the auth server container (a relative adapter test scenario).
+is the same as the auth server container - a "relative" adapter test scenario.
 
-Adapter tests are separated into submodules because different app containers require different configurations 
-(installation of adapter libs, etc.).
-Container entries of app servers are not present in the main `arquillian.xml` in the `base` module.
-Each adapter submodule adds it's own entry before it runs the tests.
+The app-servers with installed Keycloak adapter are prepared in `servers/app-server` submodules, activated by `-Papp-server-MODULE`.
+[More details.](servers/app-server/README.md)
+
+The corresponding adapter test modules are in `tests/other/adapters` submodules, and are activated by the same profiles.
 
 ## SuiteContext and TestContext
 
@@ -130,32 +133,6 @@ It automatically modifies imported test realms and deployments' adapter configs 
 2. **Bundled** - In the deployed war in `/WEB-INF/keycloak.json`. **Default.**
 
 
-## Maven Modules Structure
-
-```
-integration-arquillian
-│
-├──servers
-│  ├──wildfly   (activated by -Pauth-server-wildfly)
-│  └──eap7      (activated by -Pauth-server-eap7)
-│
-└──tests   (common settings for all test modules)
-   ├──base    (custom ARQ extensions + base functional tests)
-   └──other   (common settings for all modules dependent on base)
-      │
-      ├──adapters         (common settings for all adapter submodules)
-      │  ├──wildfly           (activated by -Papp-server-wildfly)
-      │  ├──wildfly-relative  (activated by -Papp-server-wildfly-relative,auth-server-wildfly)
-      │  ├──...
-      │  ├──tomcat            (activated by -Papp-server-tomcat)
-      │  └──karaf             (activated by -Papp-server-karaf)
-      │
-      ├──console          (activated by -Pconsole-ui-tests)
-      ├──mod_auth_mellon  (activated by -Pmod_auth_mellon)
-      ├──console_no_users (activated by -Pconsole-ui-no-users-tests)
-      └──...
-```
-
 ## Custom Arquillian Extensions
 
 Custom extensions are registered in `META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension`.
@@ -173,6 +150,37 @@ Custom extensions are registered in `META-INF/services/org.jboss.arquillian.core
  * `DeploymentTargetModifier` - Ensures all test app deployments are targeted at app server containers.
  * `URLProvider` - Fixes URLs injected by Arquillian Graphene which contain value `127.0.0.1` instead of required `localhost`.
  
-### CustomKarafContainerExtension
+## Modules Overview
+
+```
+integration-arquillian
+│
+├──servers   (preconfigured test servers)
+│  │
+│  ├──auth-server
+│  │  ├──jboss (wildfly/eap)
+│  │  └──undertow (arq. extension)
+│  │
+│  ├──app-server
+│  │  ├──jboss (wildfly/eap/as)
+│  │  ├──tomcat
+│  │  └──karaf
+│  │
+│  └──wildfly-balancer
+│
+└──tests   (common settings for all test modules)
+   │
+   ├──base    (custom ARQ extensions + base functional tests)
+   │
+   └──other   (common settings for all test modules dependent on base)
+      │
+      ├──adapters         (common settings for all adapter test modules)
+      │  ├──jboss
+      │  ├──tomcat
+      │  └──karaf
+      │
+      ├──console          
+      ├──console_no_users 
+      └──...
+```
 
-Extension for executing karaf commands after container is started. Used for installation of bundles (test apps and adapter libs).
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/app-server/README.md b/testsuite/integration-arquillian/servers/app-server/README.md
index 6905f03..008e4f5 100644
--- a/testsuite/integration-arquillian/servers/app-server/README.md
+++ b/testsuite/integration-arquillian/servers/app-server/README.md
@@ -1,6 +1,9 @@
-# Keycloak Arquillian Integration TestSuite - Test Servers
+# Keycloak Arquillian Integration TestSuite - Test Servers - App Servers
 
-[Up...](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers](../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server](../auth-server/README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers - App Servers
 
 ## App Server - JBoss
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml
new file mode 100644
index 0000000..b34fc7d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project name="inject-provider" basedir="." default="inject-provider">
+
+    <scriptdef name="inject-provider" language="javascript"  manager="bsf">
+        <attribute name="path"/>
+        <![CDATA[
+            importClass(Packages.java.io.File);
+            importClass(Packages.org.keycloak.util.JsonSerialization);
+
+            path = attributes.get("path");
+            file = new File(path);
+            root = JsonSerialization.mapper.readTree(file);
+
+            // inject provider
+            providers = root.withArray("providers");
+            providers.add("module:org.keycloak.testsuite.integration-arquillian-event-queue");
+
+            // save file
+            JsonSerialization.prettyMapper.writeValue(file, root);
+        ]]>
+    </scriptdef>
+
+    <target name="inject-provider">
+        <inject-provider path="${auth.server.home}/standalone/configuration/keycloak-server.json"/>
+    </target>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index 72c2f98..c19cc1b 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -57,6 +57,9 @@
         <unpacked.artifact.version>${auth.server.dist.version}</unpacked.artifact.version>
         
         <auth.server.home>${project.build.directory}/unpacked/${auth.server.dist.unpacked.folder.name}</auth.server.home>
+        
+        <!--used in profile auth-server-cluster. profile jpa sets this to true-->
+        <skip.h2.tcp>false</skip.h2.tcp>
     </properties>
                         
     <profiles>
@@ -97,13 +100,102 @@
                                     </artifactItems>
                                 </configuration>
                             </execution>
+                            <execution>
+                                <id>copy-event-queue-provider</id>
+                                <phase>generate-resources</phase>
+                                <goals>
+                                    <goal>copy</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.keycloak.testsuite</groupId>
+                                            <artifactId>integration-arquillian-event-queue</artifactId>
+                                            <version>${project.version}</version>
+                                            <type>jar</type>
+                                            <overWrite>false</overWrite>
+                                            <outputDirectory>${auth.server.home}/modules/org/keycloak/testsuite/integration-arquillian-event-queue/main</outputDirectory>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>install-event-queue-module</id>
+                                <phase>generate-resources</phase>
+                                <goals>
+                                    <goal>unpack</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.keycloak.testsuite</groupId>
+                                            <artifactId>integration-arquillian-event-queue</artifactId>
+                                            <version>${project.version}</version>
+                                            <type>jar</type>
+                                            <outputDirectory>${auth.server.home}/modules</outputDirectory>
+                                            <includes>**/module.xml</includes>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
                         </executions>
                     </plugin>
                     <plugin>
-                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <version>1.8</version>
+                        <executions>
+                            <execution>
+                                <id>inject-into-keycloak-server-json</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                                <configuration>
+                                    <target>
+                                        <ant antfile="../build.xml" inheritRefs="true">
+                                            <target name="inject-provider"/>
+                                        </ant>
+                                    </target>
+                                </configuration>
+                            </execution>
+                        </executions>
+                        <dependencies>
+                            <dependency>
+                                <groupId>ant-contrib</groupId>
+                                <artifactId>ant-contrib</artifactId>
+                                <version>1.0b3</version>
+                                <exclusions>
+                                    <exclusion>
+                                        <groupId>ant</groupId>
+                                        <artifactId>ant</artifactId>
+                                    </exclusion>
+                                </exclusions>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.apache.ant</groupId>
+                                <artifactId>ant-apache-bsf</artifactId>
+                                <version>1.9.3</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.apache.bsf</groupId>
+                                <artifactId>bsf-api</artifactId>
+                                <version>3.1</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>rhino</groupId>
+                                <artifactId>js</artifactId>
+                                <version>1.7R2</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.keycloak</groupId>
+                                <artifactId>keycloak-core</artifactId>
+                                <version>${project.version}</version>
+                            </dependency>
+                        </dependencies>
                     </plugin>
                     <plugin>
-                        <artifactId>maven-antrun-plugin</artifactId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
                     </plugin>
                     <plugin>
                         <groupId>org.codehaus.mojo</groupId>
@@ -268,6 +360,7 @@
             <id>jpa</id>
             <properties>
                 <jdbc.mvn.driver.deployment.dir>${auth.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+                <skip.h2.tcp>true</skip.h2.tcp>
             </properties>
             <build>
                 <pluginManagement>
@@ -344,7 +437,7 @@
                                             <!-- create module.xml in modules -->
                                             <transformationSet>
                                                 <dir>${auth.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
-                                                <stylesheet>src/main/resources/module.xsl</stylesheet>
+                                                <stylesheet>src/main/resources/xslt/module.xsl</stylesheet>
                                                 <includes>
                                                     <include>module.xml</include>
                                                 </includes>
@@ -366,6 +459,7 @@
                                                 <stylesheet>${common.resources}/datasource.xsl</stylesheet>
                                                 <includes>
                                                     <include>standalone.xml</include>
+                                                    <include>standalone-ha.xml</include>
                                                 </includes>
                                                 <outputDir>${auth.server.home}/standalone/configuration</outputDir>
                                                 <parameters>
@@ -421,14 +515,13 @@
                             <artifactId>xml-maven-plugin</artifactId>
                             <executions>
                                 <execution>
-                                    <id>configure-wildfly-datasource</id>
+                                    <id>jpa-h2-tcp</id>
                                     <phase>process-resources</phase>
                                     <goals>
                                         <goal>transform</goal>
                                     </goals>
                                     <configuration>
                                         <transformationSets>
-                                            <!-- point KeycloakDS datasource to H2 running on TCP port -->
                                             <transformationSet>
                                                 <dir>${auth.server.home}/standalone/configuration</dir>
                                                 <includes>
@@ -447,6 +540,18 @@
                                                     </parameter>
                                                 </parameters>
                                             </transformationSet>
+                                        </transformationSets>
+                                        <skip>${skip.h2.tcp}</skip>
+                                    </configuration>
+                                </execution>
+                                <execution>
+                                    <id>keycloak-ispn-caches</id>
+                                    <phase>process-resources</phase>
+                                    <goals>
+                                        <goal>transform</goal>
+                                    </goals>
+                                    <configuration>
+                                        <transformationSets>
                                             <transformationSet>
                                                 <dir>${auth.server.home}/standalone/configuration</dir>
                                                 <includes>
diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml
index d9f8222..f37234d 100644
--- a/testsuite/integration-arquillian/servers/auth-server/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml
@@ -30,6 +30,7 @@
     <name>Auth Server</name>
 
     <modules>
+        <module>services</module>
         <module>jboss</module>
         <module>undertow</module>
     </modules>
diff --git a/testsuite/integration-arquillian/servers/auth-server/README.md b/testsuite/integration-arquillian/servers/auth-server/README.md
index 87f9a20..1fed446 100644
--- a/testsuite/integration-arquillian/servers/auth-server/README.md
+++ b/testsuite/integration-arquillian/servers/auth-server/README.md
@@ -1,16 +1,21 @@
-# Keycloak Arquillian Integration TestSuite - Test Servers
+# Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server
 
-[Up...](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers](../README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server
+- [Keycloak Arquillian Integration TestSuite - Test Servers - App Servers](../app-server/README.md)
 
-## Auth Server - JBoss `auth-server/jboss`
+## Auth Server - JBoss
 
-### Modules
+Common configurations of Keycloak server on JBoss-based container (Wildfly/EAP).
 
-* __`wildfly` Wildfly 10__
+### Submodules
+
+#### `wildfly` Wildfly 10
  - Builds keycloak server on top of latest Wildfly.
  - Activated by __`-Pauth-server-wildfly`__
 
-* __`eap` EAP 7__
+#### `eap` EAP 7
  - Builds keycloak server on top of latest EAP.
  - Activated by __`-Pauth-server-eap`__
  - Requires access to product repo.
@@ -39,6 +44,6 @@ Configures in `standalone-ha.xml`:
 
 See profile `auth-server-cluster`.
 
-## Auth Server - Undertow `auth-server/undertow`
+## Auth Server - Undertow
 
 Arquillian extension for running Keycloak server in embedded Undertow.
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml
new file mode 100644
index 0000000..47ae3e4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak.testsuite</groupId>
+        <artifactId>integration-arquillian-servers-auth-server-services</artifactId>
+        <version>1.9.2.Final-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>integration-arquillian-event-queue</artifactId>
+    <name>Auth Server Services - Event Queue</name>
+
+    <dependencies>
+
+        <!-- Keycloak deps for tests -->
+
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-dependencies-server-all</artifactId>
+            <type>pom</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-servlet</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <skip>true</skip>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
new file mode 100644
index 0000000..6d0d4ed
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.events;
+
+import org.keycloak.events.Event;
+import org.keycloak.util.JsonSerialization;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class AssertEventsServletFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest req = (HttpServletRequest) servletRequest;
+
+        if ("/event-queue".equals(req.getRequestURI().substring(req.getContextPath().length()))) {
+            BlockingQueue<Event> events = EventsListenerProvider.getInstance();
+            HttpServletResponse resp = (HttpServletResponse) servletResponse;
+            resp.setContentType("application/json");
+
+            Event event = events.poll();
+            if (event != null) {
+                JsonSerialization.writeValueToStream(servletResponse.getOutputStream(), event);
+            } else {
+                resp.setStatus(204);
+            }
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java
new file mode 100644
index 0000000..a4a1a40
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.events;
+
+import org.keycloak.events.Event;
+import org.keycloak.events.EventListenerProvider;
+import org.keycloak.events.admin.AdminEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsListenerProvider implements EventListenerProvider {
+
+    private static final BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
+
+    @Override
+    public void onEvent(Event event) {
+        events.add(event);
+    }
+
+    @Override
+    public void onEvent(AdminEvent event, boolean includeRepresentation) {
+        // TODO: implement if needed
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    public static BlockingQueue<Event> getInstance() {
+        return events;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java
new file mode 100644
index 0000000..aecc01a
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.events;
+
+import org.keycloak.Config;
+import org.keycloak.events.EventListenerProvider;
+import org.keycloak.events.EventListenerProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsListenerProviderFactory implements EventListenerProviderFactory {
+
+    private static final EventsListenerProvider INSTANCE = new EventsListenerProvider();
+
+    private EventsServer server = new EventsServer();
+
+    @Override
+    public EventListenerProvider create(KeycloakSession session) {
+        return INSTANCE;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+        server.start();
+    }
+
+    @Override
+    public void close() {
+        server.stop();
+    }
+
+    @Override
+    public String getId() {
+        return "event-queue";
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
new file mode 100644
index 0000000..0b04ee3
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.events;
+
+import io.undertow.Undertow;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.servlet.Servlets;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+import io.undertow.servlet.api.FilterInfo;
+import io.undertow.servlet.api.ServletContainer;
+import io.undertow.servlet.api.ServletContainer.Factory;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsServer {
+
+    private static final Logger log = Logger.getLogger(EventsServer.class.getName());
+
+    private String rootPath = "/";
+    private int port;
+    private Undertow server;
+
+    public EventsServer() {
+        int eventsPort = Integer.parseInt(System.getProperty("auth.server.events.http.port", "8089"));
+        int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
+        int jbossPortOffset = Integer.parseInt(System.getProperty("jboss.socket.binding.port-offset", "-1"));
+
+        log.fine("Configuration:");
+        log.fine("  auth.server.events.http.port: " + eventsPort);
+        log.fine("  auth.server.port.offset: " + portOffset);
+        log.fine("  jboss.socket.binding.port-offset: " + jbossPortOffset);
+        port = eventsPort + (jbossPortOffset != -1 ? jbossPortOffset : portOffset);
+    }
+
+    public void start() {
+
+        PathHandler root = new PathHandler();
+        this.server = Undertow.builder().addHttpListener(port, "localhost").setHandler(root).build();
+        this.server.start();
+
+        ServletContainer container = Factory.newInstance();
+
+        DeploymentInfo di = new DeploymentInfo();
+        di.setClassLoader(getClass().getClassLoader());
+        di.setContextPath(rootPath);
+        di.setDeploymentName("testing-event-queue");
+
+        FilterInfo filter = Servlets.filter("EventsFilter", AssertEventsServletFilter.class);
+        di.addFilter(filter);
+        di.addFilterUrlMapping("EventsFilter", "/event-queue", DispatcherType.REQUEST);
+
+        DeploymentManager manager = container.addDeployment(di);
+        manager.deploy();
+
+        try {
+            root.addPrefixPath(rootPath, manager.start());
+        } catch (ServletException e) {
+            throw new RuntimeException(e);
+        }
+        log.info("Started EventsServer on port: " + port);
+    }
+
+    public void stop() {
+        this.server.stop();
+    }
+
+    public void setRootPath(String rootPath) {
+        this.rootPath = rootPath;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
new file mode 100644
index 0000000..e995149
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
@@ -0,0 +1,35 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.testsuite.events.EventsListenerProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml
new file mode 100644
index 0000000..19f3d41
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.testsuite.integration-arquillian-event-queue">
+    <resources>
+        <resource-root path="integration-arquillian-event-queue-${project.version}.jar"/>
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="javax.servlet.api"/>
+        <module name="io.undertow.core"/>
+        <module name="io.undertow.servlet"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-server-spi"/>
+    </dependencies>
+</module>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml
new file mode 100644
index 0000000..4684ec0
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <parent>
+        <groupId>org.keycloak.testsuite</groupId>
+        <artifactId>integration-arquillian-servers-auth-server</artifactId>
+        <version>1.9.2.Final-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>integration-arquillian-servers-auth-server-services</artifactId>
+    <packaging>pom</packaging>
+    <name>Auth Server Services</name>
+
+    <modules>
+        <module>event-queue</module>
+    </modules>
+
+</project>
diff --git a/testsuite/integration-arquillian/servers/README.md b/testsuite/integration-arquillian/servers/README.md
index 835e171..d29f470 100644
--- a/testsuite/integration-arquillian/servers/README.md
+++ b/testsuite/integration-arquillian/servers/README.md
@@ -1,6 +1,9 @@
-# Keycloak Arquillian Integration TestSuite
+# Keycloak Arquillian Integration TestSuite - Test Servers
 
-[Keycloak Arquillian Integration TestSuite](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers
+- [Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server](auth-server/README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers - App Servers](app-server/README.md)
 
 ## Test Servers
 
@@ -14,8 +17,6 @@ The artifacts are used by the Arquillian TestSuite.
  - EAP 7
 - Undertow
 
-[Details...](auth-server/README.md)
-
 
 ### App Server
 
@@ -30,7 +31,6 @@ The artifacts are used by the Arquillian TestSuite.
 - Tomcat
  - Tomcat 7, 8
 
-[Details...](app-server/README.md)
 
 ### Load Balancer
 
diff --git a/testsuite/integration-arquillian/test-apps/js-console/example-realm.json b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
new file mode 100755
index 0000000..659e5b4
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
@@ -0,0 +1,66 @@
+{
+    "realm": "example",
+    "enabled": true,
+    "sslRequired": "external",
+    "registrationAllowed": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "users" : [
+        {
+            "username" : "user",
+            "enabled": true,
+            "email" : "sample-user@example",
+            "firstName": "Sample",
+            "lastName": "User",
+            "credentials" : [
+                { "type" : "password",
+                  "value" : "password" }
+            ],
+            "realmRoles": [ "user" ],
+            "clientRoles": {
+                "account": ["view-profile", "manage-account"]
+            }
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "User privileges"
+            },
+            {
+                "name": "admin",
+                "description": "Administrator privileges"
+            }
+        ]
+    },
+    "scopeMappings": [
+        {
+            "client": "js-console",
+            "roles": ["user"]
+        }
+    ],
+    "clients": [
+        {
+            "clientId": "js-console",
+            "enabled": true,
+            "publicClient": true,
+            "baseUrl": "/js-console",
+            "redirectUris": [
+                "/js-console/*"
+            ],
+            "webOrigins": [
+                "http://localhost:8280"
+            ]
+        }
+    ],
+    "clientScopeMappings": {
+        "account": [
+            {
+                "client": "js-console",
+                "roles": ["view-profile"]
+            }
+        ]
+    }
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-console/pom.xml b/testsuite/integration-arquillian/test-apps/js-console/pom.xml
new file mode 100755
index 0000000..2f1396c
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <groupId>org.keycloak.testsuite</groupId>
+        <artifactId>integration-arquillian-test-apps</artifactId>
+        <version>1.9.2.Final-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>integration-arquillian-test-apps-js-console</artifactId>
+    <packaging>war</packaging>
+    <name>JS Console</name>
+    <description/>
+
+    <build>
+        <finalName>js-console</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/js-console/README.md b/testsuite/integration-arquillian/test-apps/js-console/README.md
new file mode 100644
index 0000000..fa9a02d
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/README.md
@@ -0,0 +1,17 @@
+Basic JavaScript Example
+========================
+
+Start and configure Keycloak
+----------------------------
+
+Start Keycloak:
+
+    bin/standalone.sh
+
+Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON file', selct example-realm.json and click Upload.
+
+Deploy the JS Console to Keycloak by running:
+
+    mvn install wildfly:deploy
+
+Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
new file mode 100755
index 0000000..1c41fcf
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
@@ -0,0 +1,156 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<html>
+<head>
+    <script src="http://localhost:8180/auth/js/keycloak.js"></script>
+</head>
+<body>
+
+<div>
+    <button onclick="keycloakInit()">Init</button>
+    <button onclick="keycloak.login()">Login</button>
+    <button onclick="keycloak.logout()">Logout</button>
+    <button onclick="keycloak.register()">Register</button>
+    <button onclick="refreshToken(9999)">Refresh Token</button>
+    <button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
+    <button onclick="loadProfile()">Get Profile</button>
+    <button onclick="loadUserInfo()">Get User Info</button>
+    <button onclick="output(keycloak.tokenParsed)">Show Token</button>
+    <button onclick="output(keycloak.refreshTokenParsed)">Show Refresh Token</button>
+    <button onclick="output(keycloak.idTokenParsed)">Show ID Token</button>
+    <button onclick="showExpires()">Show Expires</button>
+    <button onclick="output(keycloak)">Show Details</button>
+    <button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
+    <button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
+    <button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
+    <select id="flowSelect">
+        <option value="standard">standard</option>
+        <option value="implicit">implicit</option>
+    </select>
+
+    <select id="responseModeSelect">
+        <option value="fragment">fragment</option>
+        <option value="query">query</option>
+    </select>
+</div>
+
+<h2>Result</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="output"></pre>
+
+<h2>Events</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="events"></pre>
+
+
+<script>
+    function loadProfile() {
+        keycloak.loadUserProfile().success(function(profile) {
+            output(profile);
+        }).error(function() {
+            output('Failed to load profile');
+        });
+    }
+
+    function loadUserInfo() {
+        keycloak.loadUserInfo().success(function(userInfo) {
+            output(userInfo);
+        }).error(function() {
+            output('Failed to load user info');
+        });
+    }
+
+    function refreshToken(minValidity) {
+        keycloak.updateToken(minValidity).success(function(refreshed) {
+            if (refreshed) {
+                output(keycloak.tokenParsed);
+            } else {
+                output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
+            }
+        }).error(function() {
+            output('Failed to refresh token');
+        });
+    }
+
+    function showExpires() {
+        if (!keycloak.tokenParsed) {
+            output("Not authenticated");
+            return;
+        }
+
+        var o = 'Token Expires:\t\t' + new Date((keycloak.tokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+        o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds\n';
+
+        if (keycloak.refreshTokenParsed) {
+            o += 'Refresh Token Expires:\t' + new Date((keycloak.refreshTokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+            o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds';
+        }
+
+        output(o);
+    }
+
+    function output(data) {
+        if (typeof data === 'object') {
+            data = JSON.stringify(data, null, '  ');
+        }
+        document.getElementById('output').innerHTML = data;
+    }
+
+    function event(event) {
+        var e = document.getElementById('events').innerHTML;
+        document.getElementById('events').innerHTML = new Date().toLocaleString() + "\t" + event + "\n" + e;
+    }
+
+    var keycloak;
+
+    function keycloakInit() {
+        keycloak = Keycloak();
+
+        keycloak.onAuthSuccess = function () {
+            event('Auth Success');
+        };
+
+        keycloak.onAuthError = function () {
+            event('Auth Error');
+        };
+
+        keycloak.onAuthRefreshSuccess = function () {
+            event('Auth Refresh Success');
+        };
+
+        keycloak.onAuthRefreshError = function () {
+            event('Auth Refresh Error');
+        };
+
+        keycloak.onAuthLogout = function () {
+            event('Auth Logout');
+        };
+
+        keycloak.onTokenExpired = function () {
+            event('Access token expired.');
+        };
+
+        var initOptions = {flow: document.getElementById("flowSelect").value, responseMode:  document.getElementById("responseModeSelect").value}
+        keycloak.init(initOptions).success(function (authenticated) {
+            output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
+        }).error(function () {
+            output('Init Error');
+        });
+    }
+
+</script>
+</body>
+</html>
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json
new file mode 100644
index 0000000..00f6c9e
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm" : "example",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8180/auth",
+  "ssl-required" : "external",
+  "resource" : "js-console",
+  "public-client" : true
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..16e8b23
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>js-console</module-name>
+</web-app>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml
new file mode 100644
index 0000000..e031856
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/pom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>integration-arquillian</artifactId>
+        <groupId>org.keycloak.testsuite</groupId>
+        <version>1.9.2.Final-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>integration-arquillian-test-apps</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Test apps</name>
+
+    <modules>
+        <module>js-console</module>
+        <module>test-apps-dist</module>
+    </modules>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml
new file mode 100644
index 0000000..5fd7363
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml
@@ -0,0 +1,39 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<assembly>
+    <id>test-apps-dist</id>
+
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <includeBaseDirectory>true</includeBaseDirectory>
+
+    <fileSets>
+        <fileSet>
+            <directory>target/test-apps</directory>
+            <outputDirectory></outputDirectory>
+            <excludes>
+                <exclude>**/pom.xml.releaseBackup</exclude>
+                <exclude>**/.svn/**</exclude>
+                <exclude>**/target/**</exclude>
+                <exclude>**/*.iml</exclude>
+            </excludes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
new file mode 100755
index 0000000..e48e088
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
@@ -0,0 +1,31 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project name="test-apps-dist" basedir="." default="all">
+
+    <target name="all">
+        <delete dir="target/test-apps"/>
+        <copy todir="target/test-apps/js-console" overwrite="true">
+            <fileset dir="../js-console">
+                <exclude name="**/target/**"/>
+                <exclude name="**/*.iml"/>
+                <exclude name="**/*.unconfigured"/>
+                <exclude name="**/subsystem-config.xml"/>
+            </fileset>
+        </copy>
+    </target>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml
new file mode 100644
index 0000000..a518ebe
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>integration-arquillian-test-apps</artifactId>
+        <groupId>org.keycloak.testsuite</groupId>
+        <version>1.9.2.Final-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>integration-arquillian-test-apps-dist</artifactId>
+
+    <name>Test apps distribution</name>
+
+    <build>
+        <finalName>${product.name}-${product.version}-test-apps</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.8.2</version>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.8</version>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <id>build-test-apps</id>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <phase>compile</phase>
+                        <configuration>
+                            <target>
+                                <ant antfile="build.xml" inheritRefs="true">
+                                    <target name="all"></target>
+                                </ant>
+                            </target>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>ant-contrib</groupId>
+                        <artifactId>ant-contrib</artifactId>
+                        <version>1.0b3</version>
+                        <exclusions>
+                            <exclusion>
+                                <groupId>ant</groupId>
+                                <artifactId>ant</artifactId>
+                            </exclusion>
+                        </exclusions>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>assembly.xml</descriptor>
+                            </descriptors>
+                            <outputDirectory>target</outputDirectory>
+                            <workDirectory>target/assembly/work</workDirectory>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+                <version>2.5.5</version>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 662a102..bcec08c 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -57,6 +57,11 @@
             <artifactId>commons-configuration</artifactId>
             <version>1.10</version>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak.testsuite</groupId>
+            <artifactId>integration-arquillian-event-queue</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
     
     <build>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
old mode 100644
new mode 100755
index dff81a0..15f0308
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
@@ -22,6 +22,7 @@ import org.jboss.arquillian.test.api.ArquillianResource;
 import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
 
 import java.net.URL;
 
@@ -32,7 +33,7 @@ import java.net.URL;
 public class JSConsoleExample extends AbstractPageWithInjectedUrl {
 
     public static final String DEPLOYMENT_NAME = "js-console-example";
-    public static final String CLIENT_ID = "js-console";
+    public static final String CLIENT_ID = "integration-arquillian-test-apps-js-console";
 
     @ArquillianResource
     @OperateOnDeployment(DEPLOYMENT_NAME)
@@ -45,6 +46,8 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
         return fixedUrl != null ? fixedUrl : url;
     }
 
+    @FindBy(xpath = "//button[text() = 'Init']")
+    private WebElement initButton;
     @FindBy(xpath = "//button[text() = 'Login']")
     private WebElement logInButton;
     @FindBy(xpath = "//button[text() = 'Logout']")
@@ -67,6 +70,17 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
     @FindBy(xpath = "//button[text() = 'Show Details']")
     private WebElement showDetailsButton;
 
+    @FindBy(id = "flowSelect")
+    private Select flowSelect;
+    @FindBy(id = "responseModeSelect")
+    private Select responseModeSelect;
+
+    @FindBy(id = "output")
+    private WebElement outputArea;
+
+    @FindBy(id = "events")
+    private WebElement eventsArea;
+
     public void logIn() {
         logInButton.click();
     }
@@ -87,4 +101,23 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
         getProfileButton.click();
     }
 
+    public void setFlow(String value) {
+        flowSelect.selectByValue(value);
+    }
+
+    public void init() {
+        initButton.click();
+    }
+
+    public void setResponseMode(String value) {
+        responseModeSelect.selectByValue(value);
+    }
+
+    public String getOutputText() {
+        return outputArea.getText();
+    }
+
+    public String getEventsText() {
+        return eventsArea.getText();
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
index 3a5012a..9e7f4f1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
@@ -38,6 +38,7 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
     public static final String EXAMPLES_HOME;
     public static final String EXAMPLES_VERSION_SUFFIX;
     public static final String EXAMPLES_HOME_DIR;
+    public static final String TEST_APPS_HOME_DIR;
     public static final String EXAMPLES_WEB_XML;
 
     static {
@@ -49,7 +50,13 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
         Assert.assertNotNull("Property ${examples.version.suffix} must bet set.", EXAMPLES_VERSION_SUFFIX);
         System.out.println(EXAMPLES_VERSION_SUFFIX);
 
-        EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+        if (!System.getProperty("unpacked.container.folder.name","").isEmpty()) {
+            EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-examples";
+            TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-test-apps";
+        } else {
+            EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+            TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/Keycloak-" + EXAMPLES_VERSION_SUFFIX + "-test-apps";
+        }
 
         EXAMPLES_WEB_XML = EXAMPLES_HOME + "/web.xml";
     }
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
old mode 100644
new mode 100755
index 3251c34..e4c9226
--- 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
@@ -73,7 +73,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
     @Override
     public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
-        RealmRepresentation jsConsoleRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/js-console/example-realm.json"));
+        RealmRepresentation jsConsoleRealm = loadRealm(new File(TEST_APPS_HOME_DIR + "/js-console/example-realm.json"));
 
         fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
                 JSConsoleExample.CLIENT_ID, jsConsoleExamplePage.buildUri().toASCIIString());
@@ -96,6 +96,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         pause(1000);
 
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "invalid-password");
         assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
@@ -105,14 +106,17 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         testRealmLoginPage.form().login("user", "password");
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
-        assertTrue(driver.getPageSource().contains("Auth Success"));
+        jsConsoleExamplePage.init();
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
 
         pause(1000);
 
         jsConsoleExamplePage.logOut();
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        assertTrue(driver.getPageSource().contains("Init Success (Not Authenticated)"));
+        jsConsoleExamplePage.init();
+
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Not Authenticated)"));
     }
 
     @Test
@@ -120,38 +124,41 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         jsConsoleExamplePage.navigateTo();
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
 
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.refreshToken();
-        assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
 
         jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        assertTrue(driver.getPageSource().contains("Auth Success"));
+        jsConsoleExamplePage.init();
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
 
         jsConsoleExamplePage.refreshToken();
-        assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Refresh Success"));
     }
 
     @Test
     public void testRefreshTokenIfUnder30s() {
         jsConsoleExamplePage.navigateTo();
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.refreshToken();
-        assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
 
         jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        assertTrue(driver.getPageSource().contains("Auth Success"));
+        jsConsoleExamplePage.init();
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
 
         jsConsoleExamplePage.refreshTokenIfUnder30s();
-        assertTrue(driver.getPageSource().contains("Token not refreshed, valid for"));
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Token not refreshed, valid for"));
 
         pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
 
         jsConsoleExamplePage.refreshTokenIfUnder30s();
-        assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Refresh Success"));
     }
 
     @Test
@@ -159,17 +166,18 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         jsConsoleExamplePage.navigateTo();
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
 
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.getProfile();
-        assertTrue(driver.getPageSource().contains("Failed to load profile"));
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to load profile"));
 
         jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
         assertCurrentUrlStartsWith(jsConsoleExamplePage);
-        assertTrue(driver.getPageSource().contains("Auth Success"));
+        jsConsoleExamplePage.init();
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
 
         jsConsoleExamplePage.getProfile();
-        assertTrue(driver.getPageSource().contains("Failed to load profile"));
-        assertTrue(driver.getPageSource().contains("\"username\": \"user\""));
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("\"username\": \"user\""));
     }
 
     @Test
@@ -194,6 +202,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         testRealmResource().update(realm);
 
         jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.logIn();
 
         testRealmLoginPage.form().login("user", "password");
@@ -201,12 +210,14 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         assertTrue(oAuthGrantPage.isCurrent());
         oAuthGrantPage.accept();
 
-        assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
+        jsConsoleExamplePage.init();
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
 
         applicationsPage.navigateTo();
         applicationsPage.revokeGrantForApplication("js-console");
 
         jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.init();
         jsConsoleExamplePage.logIn();
 
         assertTrue(oAuthGrantPage.isCurrent());
@@ -223,7 +234,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         resultList.get(0).findElement(By.xpath(".//td[text()='REVOKE_GRANT']"));
         resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='account']"));
-        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']"));
         resultList.get(0).findElement(By.xpath(".//td[text()='revoked_client']/../td[text()='js-console']"));
 
         loginEventsPage.table().reset();
@@ -235,9 +246,87 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']"));
         resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='js-console']"));
-        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']"));
         resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='user']"));
         resultList.get(0).findElement(By.xpath(".//td[text()='consent']/../td[text()='consent_granted']"));
     }
 
+
+    @Test
+    public void implicitFlowTest() {
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.setFlow("implicit");
+        jsConsoleExamplePage.init();
+
+        jsConsoleExamplePage.logIn();
+        assertTrue(driver.getPageSource().contains("Implicit flow is disabled for the client"));
+
+        setImplicitFlowFroClient();
+
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.init();
+        jsConsoleExamplePage.logIn();
+        assertTrue(driver.getPageSource().contains("Standard flow is disabled for the client"));
+
+        logInAndInit("implicit");
+
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
+    }
+
+    @Test
+    public void implicitFlowQueryTest() {
+        setImplicitFlowFroClient();
+
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.setFlow("implicit");
+        jsConsoleExamplePage.setResponseMode("query");
+        jsConsoleExamplePage.init();
+        jsConsoleExamplePage.logIn();
+        assertTrue(driver.getPageSource().contains("Invalid parameter: response_mode"));
+    }
+
+    @Test
+    public void implicitFlowRefreshTokenTest() {
+        setImplicitFlowFroClient();
+
+        logInAndInit("implicit");
+
+        jsConsoleExamplePage.refreshToken();
+
+        assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
+    }
+
+    @Test
+    public void implicitFlowOnTokenExpireTest() {
+        RealmRepresentation realm = testRealmResource().toRepresentation();
+        realm.setAccessTokenLifespanForImplicitFlow(5);
+        testRealmResource().update(realm);
+
+        setImplicitFlowFroClient();
+
+        logInAndInit("implicit");
+
+        pause(5000);
+
+        assertTrue(jsConsoleExamplePage.getEventsText().contains("Access token expired"));
+    }
+
+    private void setImplicitFlowFroClient() {
+        ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "js-console");
+        ClientRepresentation client = clientResource.toRepresentation();
+        client.setImplicitFlowEnabled(true);
+        client.setStandardFlowEnabled(false);
+        clientResource.update(client);
+    }
+
+    private void logInAndInit(String flow) {
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.setFlow(flow);
+        jsConsoleExamplePage.init();
+        jsConsoleExamplePage.logIn();
+        testRealmLoginPage.form().login("user", "password");
+        jsConsoleExamplePage.setFlow(flow);
+        jsConsoleExamplePage.init();
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
index ba50fe7..597556c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
@@ -38,7 +38,7 @@ public abstract class AbstractEventTest extends AbstractAuthTest {
         configRep.setAdminEventsDetailsEnabled(false);
         configRep.setAdminEventsEnabled(false);
         configRep.setEventsEnabled(false);
-        configRep.setEnabledEventTypes(Collections.EMPTY_LIST); // resets to all types
+        configRep.setEnabledEventTypes(Collections.<String>emptyList()); // resets to all types
         saveConfig();
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java
new file mode 100644
index 0000000..264551b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.group;
+
+import org.junit.Before;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.events.Details;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public abstract class AbstractGroupTest extends AbstractKeycloakTest {
+
+    AssertEvents events;
+
+    @Before
+    public void initAssertEvents() throws Exception {
+        events = new AssertEvents(this);
+    }
+
+    AccessToken login(String login, String clientId, String clientSecret, String userId) throws Exception {
+
+        AccessTokenResponse tokenResponse = oauthClient.getToken("test", clientId, clientSecret, login, "password");
+
+        String accessToken = tokenResponse.getToken();
+        String refreshToken = tokenResponse.getRefreshToken();
+
+        AccessToken accessTokenRepresentation = RSATokenVerifier.verifyToken(accessToken, events.getRealmPublicKey(), AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/realms/test");
+
+        JWSInput jws = new JWSInput(refreshToken);
+        if (!RSAProvider.verify(jws, events.getRealmPublicKey())) {
+            throw new RuntimeException("Invalid refresh token");
+        }
+        RefreshToken refreshTokenRepresentation = jws.readJsonContent(RefreshToken.class);
+
+        events.expectLogin()
+                .client(clientId)
+                .user(userId)
+                .session(tokenResponse.getSessionState())
+                .detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
+                .detail(Details.TOKEN_ID, accessTokenRepresentation.getId())
+                .detail(Details.REFRESH_TOKEN_ID, refreshTokenRepresentation.getId())
+                .detail(Details.USERNAME, login)
+                .removeDetail(Details.CODE_ID)
+                .removeDetail(Details.REDIRECT_URI)
+                .removeDetail(Details.CONSENT)
+                .assertEvent();
+
+        return accessTokenRepresentation;
+    }
+
+    RealmRepresentation loadTestRealm(List<RealmRepresentation> testRealms) {
+        RealmRepresentation result = loadRealm("/testrealm.json");
+        testRealms.add(result);
+        return result;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
new file mode 100644
index 0000000..d61a517
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.group;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
+import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
+import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class GroupMappersTest extends AbstractGroupTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation testRealmRep = loadTestRealm(testRealms);
+
+        testRealmRep.setEventsEnabled(true);
+
+        ClientRepresentation client = getClientByAlias(testRealmRep, "test-app");
+        Assert.assertNotNull("test-app client exists", client);
+
+        client.setDirectAccessGrantsEnabled(true);
+
+        List<ProtocolMapperRepresentation> mappers = new LinkedList<>();
+        ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation();
+        mapper.setName("groups");
+        mapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
+        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        mapper.setConsentRequired(false);
+        Map<String, String> config = new HashMap<>();
+        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+        mapper.setConfig(config);
+        mappers.add(mapper);
+
+        mapper = new ProtocolMapperRepresentation();
+        mapper.setName("topAttribute");
+        mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        mapper.setConsentRequired(false);
+        config = new HashMap<>();
+        config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "topAttribute");
+        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "topAttribute");
+        config.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+        mapper.setConfig(config);
+        mappers.add(mapper);
+
+        mapper = new ProtocolMapperRepresentation();
+        mapper.setName("level2Attribute");
+        mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        mapper.setConsentRequired(false);
+        config = new HashMap<>();
+        config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "level2Attribute");
+        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "level2Attribute");
+        config.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+        config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+        mapper.setConfig(config);
+        mappers.add(mapper);
+
+        client.setProtocolMappers(mappers);
+    }
+
+    private ClientRepresentation getClientByAlias(RealmRepresentation testRealmRep, String alias) {
+        for (ClientRepresentation client: testRealmRep.getClients()) {
+            if (alias.equals(client.getClientId())) {
+                return client;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testGroupMappers() throws Exception {
+        RealmResource realm = adminClient.realms().realm("test");
+        {
+            UserRepresentation user = realm.users().search("topGroupUser", -1, -1).get(0);
+
+            AccessToken token = login(user.getUsername(), "test-app", "password", user.getId());
+            Assert.assertTrue(token.getRealmAccess().getRoles().contains("user"));
+            List<String> groups = (List<String>) token.getOtherClaims().get("groups");
+            Assert.assertNotNull(groups);
+            Assert.assertTrue(groups.size() == 1);
+            Assert.assertEquals("topGroup", groups.get(0));
+            Assert.assertEquals("true", token.getOtherClaims().get("topAttribute"));
+        }
+        {
+            UserRepresentation user = realm.users().search("level2GroupUser", -1, -1).get(0);
+
+            AccessToken token = login(user.getUsername(), "test-app", "password", user.getId());
+            Assert.assertTrue(token.getRealmAccess().getRoles().contains("user"));
+            Assert.assertTrue(token.getRealmAccess().getRoles().contains("admin"));
+            Assert.assertTrue(token.getResourceAccess("test-app").getRoles().contains("customer-user"));
+            List<String> groups = (List<String>) token.getOtherClaims().get("groups");
+            Assert.assertNotNull(groups);
+            Assert.assertTrue(groups.size() == 1);
+            Assert.assertEquals("level2group", groups.get(0));
+            Assert.assertEquals("true", token.getOtherClaims().get("topAttribute"));
+            Assert.assertEquals("true", token.getOtherClaims().get("level2Attribute"));
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
new file mode 100644
index 0000000..8e4c92a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
+import org.keycloak.common.util.PemUtils;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.TokenUtil;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AssertEvents {
+
+    static final String DEFAULT_CLIENT_ID = "test-app";
+    static final String DEFAULT_IP_ADDRESS = "127.0.0.1";
+    static final String DEFAULT_REALM = "test";
+    static final String DEFAULT_USERNAME = "test-user@localhost";
+
+    String defaultRedirectUri = "http://localhost:8081/app/auth";
+    String defaultEventsQueueUri = "http://localhost:8092";
+
+    private RealmResource realmResource;
+    private RealmRepresentation realmRep;
+    private AbstractKeycloakTest context;
+    private PublicKey realmPublicKey;
+    private UserRepresentation defaultUser;
+
+    public AssertEvents(AbstractKeycloakTest ctx) throws Exception {
+        context = ctx;
+
+        realmResource = context.adminClient.realms().realm(DEFAULT_REALM);
+        realmRep = realmResource.toRepresentation();
+        String pubKeyString = realmRep.getPublicKey();
+        realmPublicKey = PemUtils.decodePublicKey(pubKeyString);
+
+        defaultUser = getUser(DEFAULT_USERNAME);
+        if (defaultUser == null) {
+            throw new RuntimeException("Default user does not exist: " + DEFAULT_USERNAME + ". Make sure to add it to your test realm.");
+        }
+
+        defaultEventsQueueUri = getAuthServerEventsQueueUri();
+    }
+
+    String getAuthServerEventsQueueUri() {
+        int httpPort = Integer.parseInt(System.getProperty("auth.server.event.http.port", "8089"));
+        int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
+        return "http://localhost:" + (httpPort + portOffset);
+    }
+
+    public EventRepresentation poll() {
+        EventRepresentation event = fetchNextEvent();
+        Assert.assertNotNull("Event expected", event);
+
+        return event;
+    }
+
+    public void clear() {
+        realmResource.clearEvents();
+    }
+
+    public ExpectedEvent expectRequiredAction(EventType event) {
+        return expectLogin().event(event).removeDetail(Details.CONSENT).session(isUUID());
+    }
+
+    public ExpectedEvent expectLogin() {
+        return expect(EventType.LOGIN)
+                .detail(Details.CODE_ID, isCodeId())
+                //.detail(Details.USERNAME, DEFAULT_USERNAME)
+                //.detail(Details.AUTH_METHOD, OIDCLoginProtocol.LOGIN_PROTOCOL)
+                //.detail(Details.AUTH_TYPE, AuthorizationEndpoint.CODE_AUTH_TYPE)
+                .detail(Details.REDIRECT_URI, defaultRedirectUri)
+                .detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED)
+                .session(isUUID());
+    }
+
+    public ExpectedEvent expectClientLogin() {
+        return expect(EventType.CLIENT_LOGIN)
+                .detail(Details.CODE_ID, isCodeId())
+                .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+                .detail(Details.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
+                .removeDetail(Details.CODE_ID)
+                .session(isUUID());
+    }
+
+    public ExpectedEvent expectSocialLogin() {
+        return expect(EventType.LOGIN)
+                .detail(Details.CODE_ID, isCodeId())
+                .detail(Details.USERNAME, DEFAULT_USERNAME)
+                .detail(Details.AUTH_METHOD, "form")
+                .detail(Details.REDIRECT_URI, defaultRedirectUri)
+                .session(isUUID());
+    }
+
+    public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
+        return expect(EventType.CODE_TO_TOKEN)
+                .detail(Details.CODE_ID, codeId)
+                .detail(Details.TOKEN_ID, isUUID())
+                .detail(Details.REFRESH_TOKEN_ID, isUUID())
+                .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
+                .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+                .session(sessionId);
+    }
+
+    public ExpectedEvent expectRefresh(String refreshTokenId, String sessionId) {
+        return expect(EventType.REFRESH_TOKEN)
+                .detail(Details.TOKEN_ID, isUUID())
+                .detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
+                .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
+                .detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
+                .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+                .session(sessionId);
+    }
+
+    public ExpectedEvent expectLogout(String sessionId) {
+        return expect(EventType.LOGOUT).client((String) null)
+                .detail(Details.REDIRECT_URI, defaultRedirectUri)
+                .session(sessionId);
+    }
+
+    public ExpectedEvent expectRegister(String username, String email) {
+        UserRepresentation user = username != null ? getUser(username) : null;
+        return expect(EventType.REGISTER)
+                .user(user != null ? user.getId() : null)
+                .detail(Details.USERNAME, username)
+                .detail(Details.EMAIL, email)
+                .detail(Details.REGISTER_METHOD, "form")
+                .detail(Details.REDIRECT_URI, defaultRedirectUri);
+    }
+
+    public ExpectedEvent expectAccount(EventType event) {
+        return expect(event).client("account");
+    }
+
+    public ExpectedEvent expect(EventType event) {
+        return new ExpectedEvent()
+                .realm(realmRep.getId())
+                .client(DEFAULT_CLIENT_ID)
+                .user(defaultUser.getId())
+                .ipAddress(DEFAULT_IP_ADDRESS)
+                .session((String) null)
+                .event(event);
+    }
+
+    UserRepresentation getUser(String username) {
+        List<UserRepresentation> result = realmResource.users().search(username, null, null, null, 0, 1);
+        return result.size() > 0 ? result.get(0) : null;
+    }
+
+    public PublicKey getRealmPublicKey() {
+        return realmPublicKey;
+    }
+
+    public class ExpectedEvent {
+        private EventRepresentation expected = new EventRepresentation();
+        private Matcher<String> userId;
+        private Matcher<String> sessionId;
+        private HashMap<String, Matcher<String>> details;
+
+        public ExpectedEvent realm(RealmRepresentation realm) {
+            expected.setRealmId(realm.getId());
+            return this;
+        }
+
+        public ExpectedEvent realm(String realmId) {
+            expected.setRealmId(realmId);
+            return this;
+        }
+
+        public ExpectedEvent client(ClientRepresentation client) {
+            expected.setClientId(client.getClientId());
+            return this;
+        }
+
+        public ExpectedEvent client(String clientId) {
+            expected.setClientId(clientId);
+            return this;
+        }
+
+        public ExpectedEvent user(UserRepresentation user) {
+            return user(user.getId());
+        }
+
+        public ExpectedEvent user(String userId) {
+            return user(CoreMatchers.equalTo(userId));
+        }
+
+        public ExpectedEvent user(Matcher<String> userId) {
+            this.userId = userId;
+            return this;
+        }
+
+        public ExpectedEvent session(UserSessionRepresentation session) {
+            return session(session.getId());
+        }
+
+        public ExpectedEvent session(String sessionId) {
+            return session(CoreMatchers.equalTo(sessionId));
+        }
+
+        public ExpectedEvent session(Matcher<String> sessionId) {
+            this.sessionId = sessionId;
+            return this;
+        }
+
+        public ExpectedEvent ipAddress(String ipAddress) {
+            expected.setIpAddress(ipAddress);
+            return this;
+        }
+
+        public ExpectedEvent event(EventType e) {
+            expected.setType(e.name());
+            return this;
+        }
+
+        public ExpectedEvent detail(String key, String value) {
+            return detail(key, CoreMatchers.equalTo(value));
+        }
+
+        public ExpectedEvent detail(String key, Matcher<String> matcher) {
+            if (details == null) {
+                details = new HashMap<String, Matcher<String>>();
+            }
+            details.put(key, matcher);
+            return this;
+        }
+
+        public ExpectedEvent removeDetail(String key) {
+            if (details != null) {
+                details.remove(key);
+            }
+            return this;
+        }
+
+        public ExpectedEvent clearDetails() {
+            if (details != null) details.clear();
+            return this;
+        }
+
+        public ExpectedEvent error(String error) {
+            expected.setError(error);
+            return this;
+        }
+
+        public EventRepresentation assertEvent() {
+            return assertEvent(poll());
+        }
+
+        public EventRepresentation assertEvent(EventRepresentation actual) {
+            if (expected.getError() != null && !expected.getType().toString().endsWith("_ERROR")) {
+                expected.setType(expected.getType() + "_ERROR");
+            }
+            Assert.assertEquals(expected.getType(), actual.getType());
+            Assert.assertEquals(expected.getRealmId(), actual.getRealmId());
+            Assert.assertEquals(expected.getClientId(), actual.getClientId());
+            Assert.assertEquals(expected.getError(), actual.getError());
+            Assert.assertEquals(expected.getIpAddress(), actual.getIpAddress());
+            Assert.assertThat(actual.getUserId(), userId);
+            Assert.assertThat(actual.getSessionId(), sessionId);
+
+            if (details == null || details.isEmpty()) {
+//                Assert.assertNull(actual.getDetails());
+            } else {
+                Assert.assertNotNull(actual.getDetails());
+                for (Map.Entry<String, Matcher<String>> d : details.entrySet()) {
+                    String actualValue = actual.getDetails().get(d.getKey());
+                    if (!actual.getDetails().containsKey(d.getKey())) {
+                        Assert.fail(d.getKey() + " missing");
+                    }
+
+                    Assert.assertThat("Unexpected value for " + d.getKey(), actualValue, d.getValue());
+                }
+                /*
+                for (String k : actual.getDetails().keySet()) {
+                    if (!details.containsKey(k)) {
+                        Assert.fail(k + " was not expected");
+                    }
+                }
+                */
+            }
+
+            return actual;
+        }
+    }
+
+    public static Matcher<String> isCodeId() {
+        return isUUID();
+    }
+
+    public static Matcher<String> isUUID() {
+        return new TypeSafeMatcher<String>() {
+            @Override
+            protected boolean matchesSafely(String item) {
+                return 36 == item.length() && item.charAt(8) == '-' && item.charAt(13) == '-' && item.charAt(18) == '-' && item.charAt(23) == '-';
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Not an UUID");
+            }
+        };
+    }
+
+    private EventRepresentation fetchNextEvent() {
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+        try {
+            HttpPost post = new HttpPost(defaultEventsQueueUri + "/event-queue");
+            CloseableHttpResponse response = httpclient.execute(post);
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new RuntimeException("Failed to retrieve event from " + post.getURI() + ": " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
+            }
+
+            return JsonSerialization.readValue(response.getEntity().getContent(), EventRepresentation.class);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        finally {
+            try {
+                httpclient.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
index c66fd70..6ef0bbe 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
@@ -18,14 +18,27 @@
 package org.keycloak.testsuite.util;
 
 import javax.ws.rs.core.UriBuilder;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.Assert;
 import org.keycloak.testsuite.page.AbstractPage;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
 import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.support.ui.ExpectedCondition;
-import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.nio.charset.Charset;
 
 /**
  *
@@ -104,9 +117,68 @@ public class URLAssert {
     public static void assertCurrentUrlStartsWithLoginUrlOf(PageWithLoginUrl page) {
         assertCurrentUrlStartsWithLoginUrlOf(page.getDriver(), page);
     }
-    
+
     public static void assertCurrentUrlStartsWithLoginUrlOf(WebDriver driver, PageWithLoginUrl page) {
         assertCurrentUrlStartsWith(driver, page.getOIDCLoginUrl().toString());
     }
 
+    public static void assertGetURL(URI url, String accessToken, AssertResponseHandler handler) {
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+        try {
+            HttpGet get = new HttpGet(url);
+            get.setHeader("Authorization", "Bearer " + accessToken);
+
+            CloseableHttpResponse response = httpclient.execute(get);
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new RuntimeException("Response status error: " + response.getStatusLine().getStatusCode() + ": " + url);
+            }
+
+            handler.assertResponse(response);
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        finally {
+            try {
+                httpclient.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public interface AssertResponseHandler {
+        void assertResponse(CloseableHttpResponse response) throws IOException;
+    }
+
+    public static abstract class AssertJSONResponseHandler implements AssertResponseHandler {
+
+        @Override
+        public void assertResponse(CloseableHttpResponse response) throws IOException {
+            HttpEntity entity = response.getEntity();
+            Header contentType = entity.getContentType();
+            Assert.assertEquals("application/json", contentType.getValue());
+
+            char [] buf = new char[8192];
+            StringWriter out = new StringWriter();
+            Reader in = new InputStreamReader(entity.getContent(), Charset.forName("utf-8"));
+            int rc = 0;
+            try {
+                while ((rc = in.read(buf)) != -1) {
+                    out.write(buf, 0, rc);
+                }
+            } finally {
+                try {
+                    in.close();
+                } catch (Exception ignored) {}
+
+                out.close();
+            }
+
+            assertResponseBody(out.toString());
+        }
+
+        protected abstract void assertResponseBody(String body) throws IOException;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index ae0f5f9..f5d64a5 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -11,7 +11,8 @@
         "jboss-logging" : {
             "success-level": "debug",
             "error-level": "warn"
-        }
+        },
+        "event-queue": {}
     },
 
     "realm": {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
new file mode 100644
index 0000000..845adda
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
@@ -0,0 +1,186 @@
+{
+  "id": "test",
+  "realm": "test",
+  "enabled": true,
+  "sslRequired": "external",
+  "registrationAllowed": true,
+  "resetPasswordAllowed": true,
+  "editUsernameAllowed" : true,
+  "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+  "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "requiredCredentials": [ "password" ],
+  "defaultRoles": [ "user" ],
+  "smtpServer": {
+    "from": "auto@keycloak.org",
+    "host": "localhost",
+    "port":"3025"
+  },
+  "users" : [
+    {
+      "username" : "test-user@localhost",
+      "enabled": true,
+      "email" : "test-user@localhost",
+      "firstName": "Tom",
+      "lastName": "Brady",
+      "credentials" : [
+        { "type" : "password",
+          "value" : "password" }
+      ],
+      "realmRoles": ["user", "offline_access"],
+      "clientRoles": {
+        "test-app": [ "customer-user" ],
+        "account": [ "view-profile", "manage-account" ]
+      }
+    },
+    {
+      "username" : "john-doh@localhost",
+      "enabled": true,
+      "email" : "john-doh@localhost",
+      "firstName": "John",
+      "lastName": "Doh",
+      "credentials" : [
+        { "type" : "password",
+          "value" : "password" }
+      ],
+      "realmRoles": ["user"],
+      "clientRoles": {
+        "test-app": [ "customer-user" ],
+        "account": [ "view-profile", "manage-account" ]
+      }
+    },
+    {
+      "username" : "keycloak-user@localhost",
+      "enabled": true,
+      "email" : "keycloak-user@localhost",
+      "credentials" : [
+        { "type" : "password",
+          "value" : "password" }
+      ],
+      "realmRoles": ["user"],
+      "clientRoles": {
+        "test-app": [ "customer-user" ],
+        "account": [ "view-profile", "manage-account" ]
+      }
+    },
+    {
+      "username" : "topGroupUser",
+      "enabled": true,
+      "email" : "top@redhat.com",
+      "credentials" : [
+        { "type" : "password",
+          "value" : "password" }
+      ],
+      "groups": [
+        "/topGroup"
+      ]
+    },
+    {
+      "username" : "level2GroupUser",
+      "enabled": true,
+      "email" : "level2@redhat.com",
+      "credentials" : [
+        { "type" : "password",
+          "value" : "password" }
+      ],
+      "groups": [
+        "/topGroup/level2group"
+      ]
+    }
+  ],
+  "scopeMappings": [
+    {
+      "client": "third-party",
+      "roles": ["user"]
+    },
+    {
+      "client": "test-app",
+      "roles": ["user"]
+    }
+  ],
+  "clients": [
+    {
+      "clientId": "test-app",
+      "enabled": true,
+      "baseUrl": "http://localhost:8081/app",
+      "redirectUris": [
+        "http://localhost:8081/app/*"
+      ],
+      "adminUrl": "http://localhost:8081/app/logout",
+      "secret": "password"
+    },
+    {
+      "clientId" : "third-party",
+      "enabled": true,
+      "consentRequired": true,
+
+      "redirectUris": [
+        "http://localhost:8081/app/*"
+      ],
+      "secret": "password"
+    }
+  ],
+  "roles" : {
+    "realm" : [
+      {
+        "name": "user",
+        "description": "Have User privileges"
+      },
+      {
+        "name": "admin",
+        "description": "Have Administrator privileges"
+      }
+    ],
+    "client" : {
+      "test-app" : [
+        {
+          "name": "customer-user",
+          "description": "Have Customer User privileges"
+        },
+        {
+          "name": "customer-admin",
+          "description": "Have Customer Admin privileges"
+        }
+      ]
+    }
+
+  },
+  "groups" : [
+    {
+      "name": "topGroup",
+      "attributes": {
+        "topAttribute": ["true"]
+
+      },
+      "realmRoles": ["user"],
+
+      "subGroups": [
+        {
+          "name": "level2group",
+          "realmRoles": ["admin"],
+          "clientRoles": {
+            "test-app": ["customer-user"]
+          },
+          "attributes": {
+            "level2Attribute": ["true"]
+
+          }
+        }
+      ]
+    }
+  ],
+
+
+  "clientScopeMappings": {
+    "test-app": [
+      {
+        "client": "third-party",
+        "roles": ["customer-user"]
+      }
+    ]
+  },
+
+  "internationalizationEnabled": true,
+  "supportedLocales": ["en", "de"],
+  "defaultLocale": "en",
+  "eventsListeners": ["jboss-logging", "event-queue"]
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..0674eea
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java
@@ -0,0 +1,13 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.junit.Ignore;
+/**
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-eap")
+//@AdapterLibsLocationProperty("adapter.libs.eap")
+public class EAPJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
index 20bd52e..4f167ba 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
@@ -1,5 +1,6 @@
 package org.keycloak.testsuite.adapter.example;
 
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
 import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
 import org.junit.Ignore;
 /**
@@ -7,7 +8,6 @@ import org.junit.Ignore;
  */
 @AppServerContainer("app-server-eap6")
 //@AdapterLibsLocationProperty("adapter.libs.eap6")
-@Ignore //jsconsole example has hardcoded relative path to keycloak.js
 public class EAP6JSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
 
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..7834461
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java
@@ -0,0 +1,16 @@
+
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index f46954a..a2e20fd 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -220,8 +220,8 @@
                                                 <type>war</type>
                                             </artifactItem>       
                                             <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>js-console</artifactId>        
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-test-apps-js-console</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>war</type>
                                             </artifactItem>
@@ -287,6 +287,13 @@
                                                 <type>zip</type>
                                                 <includes>**/*realm.json,**/testsaml.json</includes>
                                             </artifactItem>
+                                            <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-test-apps-dist</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>zip</type>
+                                                <includes>**/*realm.json,**/testsaml.json</includes>
+                                            </artifactItem>
                                         </artifactItems>
                                         <outputDirectory>${examples.home}</outputDirectory>
                                         <overWriteIfNewer>true</overWriteIfNewer>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 2fcd713..727b2ec 100644
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -48,6 +48,7 @@
         
         <auth.server.port.offset>100</auth.server.port.offset>
         <auth.server.http.port>8180</auth.server.http.port>
+        <auth.server.events.http.port>8089</auth.server.events.http.port>
         <auth.server.https.port>8543</auth.server.https.port>
         <auth.server.management.port>10090</auth.server.management.port>
         <auth.server.management.port.jmx>10099</auth.server.management.port.jmx>
@@ -140,6 +141,7 @@
 
                             <auth.server.port.offset>${auth.server.port.offset}</auth.server.port.offset>
                             <auth.server.http.port>${auth.server.http.port}</auth.server.http.port>
+                            <auth.server.events.http.port>${auth.server.events.http.port}</auth.server.events.http.port>
                             <auth.server.https.port>${auth.server.https.port}</auth.server.https.port>
                             <auth.server.management.port>${auth.server.management.port}</auth.server.management.port>
                             <auth.server.management.port.jmx>${auth.server.management.port.jmx}</auth.server.management.port.jmx>