keycloak-memoizeit

KEYCLOAK-8047 Make Photoz tests great: run them on undertow

10/2/2018 5:40:35 AM

Changes

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js 256(+0 -256)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/identity.js 65(+0 -65)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js 214(+0 -214)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js 13(+0 -13)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js 14(+0 -14)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html 19(+0 -19)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html 9(+0 -9)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html 1(+0 -1)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html 22(+0 -22)

testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/profile.html 6(+0 -6)

testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java 51(+0 -51)

testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java 33(+0 -33)

testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java 56(+0 -56)

testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml 23(+0 -23)

Details

diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/container/UndertowDeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/container/UndertowDeploymentArchiveProcessor.java
index 6b0ecf4..d812f23 100644
--- a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/container/UndertowDeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/container/UndertowDeploymentArchiveProcessor.java
@@ -43,6 +43,8 @@ public class UndertowDeploymentArchiveProcessor implements ApplicationArchivePro
         modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH);
         modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1);
         modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2);
+
+        modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS);
     }
 
     private void modifyWebXML(Archive<?> archive, TestClass testClass) {
diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
index 01a4a08..b0a82de 100644
--- a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
+++ b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
@@ -30,6 +30,7 @@ import java.lang.reflect.Field;
 import java.nio.charset.Charset;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
@@ -51,6 +52,7 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
 import org.jboss.resteasy.spi.ResteasyDeployment;
 import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.Node;
 import org.jboss.shrinkwrap.api.asset.ClassAsset;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.jboss.shrinkwrap.descriptor.api.Descriptor;
@@ -125,9 +127,20 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
             di = ((UndertowWebArchive) archive).getDeploymentInfo();
         } else if (archive instanceof WebArchive) {
             WebArchive webArchive = (WebArchive)archive;
+
+            Optional<Node> applicationClassNode = archive.getContent(archivePath ->
+                    archivePath.get().startsWith("/WEB-INF/classes/") && archivePath.get().endsWith("Application.class"))
+                    .values().stream().findFirst();
+
             if (isJaxrsApp(webArchive)) {
-                di = new UndertowDeployerHelper().getDeploymentInfo(configuration, webArchive, 
+                di = new UndertowDeployerHelper().getDeploymentInfo(configuration, webArchive,
                         undertow.undertowDeployment(getCustomResteasyDeployment(webArchive)));
+            } else if (applicationClassNode.isPresent()) {
+                String applicationPath = applicationClassNode.get().getPath().get();
+
+                ResteasyDeployment deployment = new ResteasyDeployment();
+                deployment.setApplicationClass(extractClassName(applicationPath));
+                di = new UndertowDeployerHelper().getDeploymentInfo(configuration, (WebArchive) archive, undertow.undertowDeployment(deployment));
             } else {
                 di = new UndertowDeployerHelper().getDeploymentInfo(configuration, webArchive);
             }
@@ -155,6 +168,14 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
                 createHttpContextForDeploymentInfo(di));
     }
 
+    private String extractClassName(String applicationPath) {
+        applicationPath = applicationPath
+                .substring(0, applicationPath.lastIndexOf(".class")) // Remove .class
+                .replaceFirst("^/WEB-INF/classes/", ""); // Remove /WEB-INF/classes/ from beginning
+
+        return applicationPath.replaceAll("/", ".");
+    }
+
     @Override
     public void undeploy(Archive<?> archive) throws DeploymentException {
         log.info("Undeploying archive " + archive.getName());
@@ -218,7 +239,7 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
 
     private ResteasyDeployment getCustomResteasyDeployment(WebArchive webArchive) {
         //take all classes from war and add those with @Path annotation to RestSamlApplicationConfig
-        Set<Class<?>> classes = webArchive.getContent(archivePath -> 
+        Set<Class<?>> classes = webArchive.getContent(archivePath ->
                 archivePath.get().startsWith("/WEB-INF/classes/") &&
                 archivePath.get().endsWith(".class")
         ).values().stream()
diff --git a/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
index 47437dc..9eaf990 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
@@ -32,7 +32,7 @@
       },
       {
         "name" : "Album Resource",
-        "path" : "/album/{id}",
+        "path" : "/album/{id}/",
         "methods" : [
           {
             "method": "DELETE",
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
index a66fcdc..f604cb4 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
@@ -5,34 +5,63 @@
     <meta charset="utf-8">
     <title>Photoz HTML5 Client</title>
 
-    <!-- Load AngularJS -->
-    <script src="lib/angular/angular.min.js"></script>
-    <script src="lib/angular/angular-resource.min.js"></script>
-    <script src="lib/angular/angular-route.min.js"></script>
     <script src="lib/jwt-decode.min.js"></script>
 
     <script src="http://localhost:8180/auth/js/keycloak.js"></script>
     <script src="http://localhost:8180/auth/js/keycloak-authz.js"></script>
-    <script src="js/identity.js" type="text/javascript"></script>
-    <script src="js/app.js" type="text/javascript"></script>
 </head>
 
-<body data-ng-controller="TokenCtrl">
+<h2>Result</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="output"></pre>
 
-<!--<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a> | <a href data-ng-click="requestEntitlement()">Request Entitlement</a> |<a href="" ng-click="Identity.logout()">Sign Out</a>-->
-<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | 
-<a href data-ng-click="showAccessToken()">Show Access Token </a> | 
-<a id="entitlements" href data-ng-click="requestEntitlements()">Request Entitlements</a> | 
-<a id="entitlement" href data-ng-click="requestEntitlement()">Request Entitlement</a> |
-<a id="my-account" href ng-click="Identity.account()">My Account</a> |
-<a href="" ng-click="Identity.logout()">Sign Out</a>
+<h2>Events</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="events"></pre>
 
-<div id="content-area" class="col-md-9" role="main">
-    <div id="content" ng-view/>
-</div>
 
-<div style="display: none;" id="bearer"></div>
-<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="output"></pre>
+<script>
+     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 showError() {
+        output("Error: " + getParameterByName("error") + "\n" + "Error description: " + getParameterByName("error_description"));
+    }
+
+    function getParameterByName(name, url) {
+        if (!url) url = window.location.href;
+        name = name.replace(/[\[\]]/g, "\\$&");
+        var regex = new RegExp("[?&#]" + name + "(=([^&#]*)|&|#|$)"),
+            results = regex.exec(url);
+        if (!results) return null;
+        if (!results[2]) return '';
+        return decodeURIComponent(results[2].replace(/\+/g, " "));
+    }
+
+    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;
+    }
+</script>
 
 </body>
 </html>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
index e06fe64..5f1c150 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
@@ -70,6 +70,14 @@
                     <skip>false</skip>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.0.0</version>
+                <configuration>
+                    <attachClasses>true</attachClasses>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
index 22b5388..dff162c 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -17,10 +17,9 @@
  */
 package org.keycloak.example.photoz.admin;
 
+import org.keycloak.example.photoz.CustomDatabase;
 import org.keycloak.example.photoz.entity.Album;
 
-import javax.inject.Inject;
-import javax.persistence.EntityManager;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -39,8 +38,7 @@ public class AdminAlbumService {
 
     public static final String SCOPE_ADMIN_ALBUM_MANAGE = "admin:manage";
 
-    @Inject
-    private EntityManager entityManager;
+    private CustomDatabase entityManager = CustomDatabase.create();
 
     @Context
     private HttpHeaders headers;
@@ -49,7 +47,7 @@ public class AdminAlbumService {
     @Produces("application/json")
     public Response findAll() {
         HashMap<String, List<Album>> albums = new HashMap<>();
-        List<Album> result = this.entityManager.createQuery("from Album").getResultList();
+        List<Album> result = this.entityManager.getAll();
 
         for (Album album : result) {
             //We need to compile this under JDK7 so we can't use lambdas
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 40b1242..4e62e1e 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -3,14 +3,12 @@ package org.keycloak.example.photoz.album;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
+import org.keycloak.example.photoz.CustomDatabase;
+import org.keycloak.example.photoz.entity.Album;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
-import org.keycloak.example.photoz.entity.Album;
-import org.keycloak.example.photoz.util.Transaction;
 
-import javax.inject.Inject;
-import javax.persistence.EntityManager;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -29,7 +27,6 @@ import javax.ws.rs.core.HttpHeaders;
 import org.jboss.logging.Logger;
 
 @Path("/album")
-@Transaction
 public class AlbumService {
 
     private final Logger log = Logger.getLogger(AlbumService.class);
@@ -37,8 +34,7 @@ public class AlbumService {
     public static final String SCOPE_ALBUM_VIEW = "album:view";
     public static final String SCOPE_ALBUM_DELETE = "album:delete";
 
-    @Inject
-    private EntityManager entityManager;
+    private CustomDatabase customDatabase = CustomDatabase.create();
 
     @Context
     private HttpServletRequest request;
@@ -57,28 +53,28 @@ public class AlbumService {
         newAlbum.setUserId(userId);
 
         log.debug("PERSISTING " + newAlbum);
-        entityManager.persist(newAlbum);
+        customDatabase.addAlbum(newAlbum);
         try {
             createProtectedResource(newAlbum);
         } catch (RuntimeException e) {
             log.debug("ERROR " + e);
-            entityManager.remove(newAlbum);
-            throw e;
+            customDatabase.remove(newAlbum);
+            return Response.status(500).entity(e.getMessage()).build(); //
         }
 
         return Response.ok(newAlbum).build();
     }
 
-    @Path("{id}")
+    @Path("{name}")
     @DELETE
-    public Response delete(@PathParam("id") String id, @Context HttpHeaders headers) {
+    public Response delete(@PathParam("name") String name, @Context HttpHeaders headers) {
         printAuthHeaders(headers);
         
-        Album album = this.entityManager.find(Album.class, Long.valueOf(id));
+        Album album = this.customDatabase.findByName(name);
 
         try {
             deleteProtectedResource(album);
-            this.entityManager.remove(album);
+            this.customDatabase.remove(album);
         } catch (Exception e) {
             throw new RuntimeException("Could not delete album.", e);
         }
@@ -90,23 +86,23 @@ public class AlbumService {
     @Produces("application/json")
     public Response findAll(@QueryParam("getAll") Boolean getAll) {
         if (getAll != null && getAll) {
-            return Response.ok(this.entityManager.createQuery("from Album").getResultList()).build();
+            return Response.ok(this.customDatabase.getAll()).build();
         } else {
-            return Response.ok(this.entityManager.createQuery("from Album where userId = '" + request.getUserPrincipal().getName() + "'").getResultList()).build();
+            return Response.ok(this.customDatabase.findByUserId(request.getUserPrincipal().getName())).build();
         }
     }
 
     @GET
-    @Path("{id}")
+    @Path("{name}")
     @Produces("application/json")
-    public Response findById(@PathParam("id") String id) {
-        List result = this.entityManager.createQuery("from Album where id = " + Long.valueOf(id)).getResultList();
+    public Response findById(@PathParam("name") String name) {
+        Album result = this.customDatabase.findByName(name);
 
-        if (result.isEmpty()) {
+        if (result == null) {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        return Response.ok(result.get(0)).build();
+        return Response.ok(result).build();
     }
 
     private void createProtectedResource(Album album) {
@@ -117,7 +113,7 @@ public class AlbumService {
             scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
             scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE));
 
-            ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album");
+            ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getName(), "http://photoz.com/album");
 
             albumResource.setOwner(album.getUserId());
 
@@ -132,7 +128,7 @@ public class AlbumService {
     }
 
     private void deleteProtectedResource(Album album) {
-        String uri = "/album/" + album.getId();
+        String uri = "/album/" + album.getName();
 
         try {
             ProtectionResource protection = getAuthzClient().protection();
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
index 6e3e3b0..4d66499 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
@@ -17,8 +17,8 @@
  */
 package org.keycloak.example.photoz.album;
 
-import javax.inject.Inject;
-import javax.persistence.EntityManager;
+import org.keycloak.example.photoz.CustomDatabase;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -36,14 +36,13 @@ public class ProfileService {
 
     private static final String PROFILE_VIEW = "profile:view";
 
-    @Inject
-    private EntityManager entityManager;
+    private CustomDatabase customDatabase = CustomDatabase.create();
 
     @GET
     @Produces("application/json")
     public Response view(@Context HttpServletRequest request) {
         Principal userPrincipal = request.getUserPrincipal();
-        List albums = this.entityManager.createQuery("from Album where userId = '" + userPrincipal.getName() + "'").getResultList();
+        List albums = this.customDatabase.findByUserId(userPrincipal.getName());
         return Response.ok(new Profile(userPrincipal.getName(), albums.size())).build();
     }
 
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/CustomDatabase.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/CustomDatabase.java
new file mode 100644
index 0000000..9c06eff
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/CustomDatabase.java
@@ -0,0 +1,88 @@
+package org.keycloak.example.photoz;
+
+import org.keycloak.example.photoz.entity.Album;
+import org.keycloak.example.photoz.entity.Photo;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author mhajas
+ */
+public class CustomDatabase {
+
+    private static final CustomDatabase INSTANCE = new CustomDatabase();
+    private List<Album> albums;
+    private List<Photo> photos;
+    private Long lastIndex = 0L;
+
+
+    public static final CustomDatabase create() {
+        return INSTANCE;
+    }
+
+    private CustomDatabase() {
+        albums = new ArrayList<>();
+    }
+
+    public List<Album> getAll() {
+        return albums;
+    }
+
+    public void addAlbum(Album a) {
+        a.setId(lastIndex++);
+        albums.add(a);
+    }
+
+    public void remove(Album albumToRemove) {
+        Iterator<Album> iter = albums.iterator();
+
+        while (iter.hasNext()) {
+            Album a = iter.next();
+            if (a.getId().equals(albumToRemove.getId())) {
+                iter.remove();
+            }
+        }
+    }
+
+    public Album findById(Long id) {
+        for (Album a : albums) {
+            if(a.getId().equals(id)) {
+                return a;
+            }
+        }
+
+        return null;
+    }
+
+    public Album findByName(String name) {
+        for (Album a : albums) {
+            if(a.getName().equals(name)) {
+                return a;
+            }
+        }
+
+        return null;
+    }
+
+    public List<Album> findByUserId(String userId) {
+        List<Album> result = new ArrayList<>();
+
+        for (Album a : albums) {
+            if (a.getUserId().equals(userId)) {
+                result.add(a);
+            }
+        }
+
+        return result;
+    }
+
+    public int cleanAll() {
+        int result = albums.size() + photos.size();
+        albums.clear();
+        photos.clear();
+
+        return result;
+    }
+}
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java
index 5b8377c..90e0951 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java
@@ -1,12 +1,27 @@
 package org.keycloak.example.photoz;
 
+import org.keycloak.example.photoz.admin.AdminAlbumService;
+import org.keycloak.example.photoz.album.AlbumService;
+import org.keycloak.example.photoz.album.ProfileService;
+import org.keycloak.example.photoz.unsecured.UnsecuredService;
+
 import javax.ws.rs.ApplicationPath;
 import javax.ws.rs.core.Application;
+import java.util.LinkedHashSet;
+import java.util.Set;
 
 /**
  * Basic auth app.
  */
 @ApplicationPath("/")
 public class PhotozApplication extends Application {
-
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> resources = new LinkedHashSet<Class<?>>();
+        resources.add(AlbumService.class);
+        resources.add(AdminAlbumService.class);
+        resources.add(ProfileService.class);
+        resources.add(UnsecuredService.class);
+        return resources;
+    }
 }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/unsecured/UnsecuredService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/unsecured/UnsecuredService.java
index ff07d37..8991ea9 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/unsecured/UnsecuredService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/unsecured/UnsecuredService.java
@@ -17,13 +17,12 @@
 package org.keycloak.example.photoz.unsecured;
 
 
-import javax.inject.Inject;
-import javax.persistence.EntityManager;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.GET;
 import org.jboss.logging.Logger;
+import org.keycloak.example.photoz.CustomDatabase;
 
 /**
  * Service used to ensure there is clean DB before test 
@@ -35,17 +34,15 @@ public class UnsecuredService {
 
     private final Logger log = Logger.getLogger(UnsecuredService.class);
 
-    @Inject
-    private EntityManager entityManager;
+    private CustomDatabase customDatabase = CustomDatabase.create();
 
     @GET
     @Produces("application/json")
     public Response cleanAll() {
-        int deletedAlbums = entityManager.createQuery("delete from Album").executeUpdate();
-        int deletedPhotos = entityManager.createQuery("delete from Photo").executeUpdate();
+        int deleted = customDatabase.cleanAll();
         
-        if (deletedAlbums != 0 || deletedPhotos != 0) {
-            log.warnf("Database was not empty. Deleted {0} Albums, {1} Photos", deletedAlbums, deletedPhotos);
+        if (deleted != 0) {
+            log.warnf("Database was not empty. Deleted Albums + Photos {0}", deleted);
         } else {
             log.debug("Database was clean before test");
         }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/beans.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/beans.xml
index fbf2a32..cf30c7d 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/beans.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/beans.xml
@@ -3,7 +3,4 @@
    xsi:schemaLocation="
         http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
-    <interceptors>
-        <class>org.keycloak.example.photoz.util.TransactionInterceptor</class>
-    </interceptors>
 </beans>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index dd0cab1..2b03024 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -31,7 +31,7 @@
       },
       {
         "name" : "Album Resource",
-        "path" : "/album/{id}",
+        "path" : "/album/{id}/",
         "methods" : [
           {
             "method": "DELETE",
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
index 7327cba..ffb7126 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
@@ -99,7 +99,7 @@
       "decisionStrategy": "UNANIMOUS",
       "config": {
         "applyPolicies": "[]",
-        "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n    $evaluation.grant();\n}"
+        "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1') || contextAttributes.containsValue('kc.client.network.ip_address', '0:0:0:0:0:0:0:1')) {\n    $evaluation.grant();\n}"
       }
     },
     {
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index db9afca..768558e 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -86,6 +86,12 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.11.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.subethamail</groupId>
             <artifactId>subethasmtp</artifactId>
             <scope>compile</scope>
@@ -139,12 +145,8 @@
         <dependency>
             <groupId>org.keycloak.testsuite</groupId>
             <artifactId>photoz-restful-api</artifactId>
-            <type>war</type>
             <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.hibernate</groupId>
-            <artifactId>hibernate-c3p0</artifactId>
+            <classifier>classes</classifier>
         </dependency>
     </dependencies>
     
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
index 9db6c25..0a1a8ee 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
@@ -22,10 +22,13 @@ import org.jboss.arquillian.graphene.page.Page;
 import org.jboss.arquillian.test.api.ArquillianResource;
 import org.keycloak.testsuite.auth.page.login.OIDCLogin;
 import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
-import org.keycloak.testsuite.pages.ConsentPage;
 import org.keycloak.testsuite.util.JavascriptBrowser;
 import org.keycloak.testsuite.util.UIUtils;
 import org.keycloak.testsuite.util.URLUtils;
+import org.keycloak.testsuite.util.javascript.JavascriptStateValidator;
+import org.keycloak.testsuite.util.javascript.JavascriptTestExecutorWithAuthorization;
+import org.keycloak.testsuite.util.javascript.ResponseValidator;
+import org.keycloak.testsuite.util.javascript.XMLHttpRequest;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
@@ -33,8 +36,6 @@ import org.openqa.selenium.support.FindBy;
 
 import java.net.URL;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertThat;
 import static org.keycloak.testsuite.util.UIUtils.clickLink;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
 import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
@@ -61,9 +62,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
     @JavascriptBrowser
     protected OIDCLogin loginPage;
 
-    @Page
-    @JavascriptBrowser
-    protected ConsentPage consentPage;
 
     @FindBy(xpath = "//a[@ng-click = 'Identity.logout()']")
     @JavascriptBrowser
@@ -85,74 +83,59 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
     @JavascriptBrowser
     private WebElement output;
 
+    private JavascriptTestExecutorWithAuthorization testExecutor;
+    private String apiUrl;
+
+    public void setTestExecutorPlayground(JavascriptTestExecutorWithAuthorization executor, String apiUrl) {
+        testExecutor = executor;
+        this.apiUrl = apiUrl;
+    }
+
     public void createAlbum(String name) {
         createAlbum(name, false);
     }
 
     public void createAlbum(String name, boolean managed) {
-        if (managed) {
-            createAlbum(name, "save-managed-album");
-        } else {
-            createAlbum(name, "save-album");
-        }
+        createAlbum(name, managed, false, null);
     }
 
-    public void createAlbum(String name, String buttonId) {
-        log.debugf("Creating album {0} with buttonId: {1}", name, buttonId);
-        navigateTo();
-        WebElement createAlbum = driver.findElement(By.id("create-album"));
-        waitUntilElement(createAlbum).is().clickable();
-        createAlbum.click();
-        WebElement albumNameInput = driver.findElement(By.id("album.name"));
-        waitUntilElement(albumNameInput).is().present();
-        UIUtils.setTextInputValue(albumNameInput, name);
-        waitUntilElement(albumNameInput).attribute(UIUtils.VALUE_ATTR_NAME).contains(name);
-        WebElement button = driver.findElement(By.id(buttonId));
-        waitUntilElement(button).is().clickable();
-        button.click();
-        pause(WAIT_AFTER_OPERATION);
-        if (buttonId.equals("save-album-invalid")) {
-            waitForPageToLoad();
-            assertThat(driver.getPageSource(), containsString("Could not register protected resource."));
-        } else {
-            waitUntilElement(albumNameInput).is().not().present();
-        }
+    public void createAlbum(String name, boolean managed, boolean invalidUser, ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("POST")
+                        .url(apiUrl + "/album" + (invalidUser ? "?user=invalidUser" : ""))
+                        .content("JSON.stringify(JSON.parse('{\"name\" : \"" + name + "\", \"userManaged\": " + Boolean.toString(managed) + " }'))")
+                        .addHeader("Content-Type", "application/json; charset=UTF-8")
+                , validator);
     }
 
-    public void createAlbumWithInvalidUser(String name) {
-        createAlbum(name, "save-album-invalid");
+
+    public void createAlbumWithInvalidUser(String name, ResponseValidator validator) {
+        createAlbum(name, false, true, validator);
     }
 
+
+
     @Override
     public URL getInjectedUrl() {
         return this.url;
     }
 
-    public void deleteAlbum(String name, boolean shouldBeDenied) {
-        log.debugf("Deleting album {0}", name);
-        WebElement delete = driver.findElement(By.id("delete-" + name));
-        waitUntilElement(delete).is().clickable();
-        delete.click();
-        pause(WAIT_AFTER_OPERATION);
-        if (shouldBeDenied) {
-            waitForDenial();
-        } else {
-            waitUntilElement(delete).is().not().present();
-        }
+    public void deleteAlbum(String name, ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("DELETE")
+                        .url(apiUrl + "/album/" + name + "/") // it doesn't work without ending "/"
+                , validator);
     }
 
-    public void navigateToAdminAlbum(boolean shouldBeDenied) {
-        log.debug("Navigating to Admin Album");
-        URLUtils.navigateToUri(toString() + "/#/admin/album");
-        
-        driver.navigate().refresh(); // This is sometimes necessary for loading the new policy settings
-        waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
-        if (shouldBeDenied) {
-            waitForDenial();
-        } else {
-            waitUntilElement(output).text().equalTo("");
-        }
+    public void navigateToAdminAlbum(ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("GET")
+                        .addHeader("Accept", "application/json")
+                        .url(apiUrl + "/admin/album")
+                , validator);
     }
 
     public void logOut() {
@@ -161,73 +144,24 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         clickLink(signOutButton);
     }
     
-    public void requestEntitlement() {
-        waitUntilElement(entitlement).is().clickable();
-        entitlement.click();
-        waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
-        pause(WAIT_AFTER_OPERATION);
+    public void requestEntitlement(JavascriptStateValidator validator) {
+        testExecutor.executeAsyncScript("var callback = arguments[arguments.length - 1];" +
+                "window.authorization.entitlement('photoz-restful-api', {" +
+                "    \"permissions\": [" +
+                "        {" +
+                "            \"id\" : \"Album Resource\"" +
+                "        }" +
+                "    ]" +
+                "}).then(function (rpt) {" +
+                "    callback(JSON.stringify(jwt_decode(rpt), null, '  '));" +
+                "});", validator);
     }
     
-    public void requestEntitlements() {
-        waitUntilElement(entitlements).is().clickable();
-        entitlements.click();
-        waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
-        pause(WAIT_AFTER_OPERATION);
-    }
-
-    public void login(String username, String password, String... scopes) throws InterruptedException {
-        String currentUrl = this.driver.getCurrentUrl();
-
-        if (scopes.length > 0) {
-            StringBuilder scopesValue = new StringBuilder();
-
-            for (String scope : scopes) {
-                if (scopesValue.length() != 0) {
-                    scopesValue.append(" ");
-                }
-                scopesValue.append(scope);
-            }
-
-            scopesValue.append(" openid");
-
-
-            StringBuilder urlWithScopeParam = new StringBuilder(currentUrl);
-
-            int scopeIndex = currentUrl.indexOf("scope");
-
-            if (scopeIndex != -1) {
-                // Remove scope param from url
-                urlWithScopeParam.delete(scopeIndex, currentUrl.indexOf('&', scopeIndex));
-                // Add scope param to the end of query
-                urlWithScopeParam.append("&").append("scope=");
-            }
-
-            if (!currentUrl.contains("?")) {
-                urlWithScopeParam.append("?scope=");
-            }
-
-            urlWithScopeParam.append(scopesValue);
-
-            URLUtils.navigateToUri(urlWithScopeParam.toString());
-        }
-
-        this.loginPage.form().login(username, password);
-        waitForPageToLoad();//guess
-
-        try {
-            if (!isCurrent()) {
-                // simple check if we are at the consent page, if so just click 'Yes'
-                if (this.consentPage.isCurrent(driver)) {
-                    consentPage.confirm();
-                }
-            }
-        } catch (Exception ignore) {
-            // ignore errors when checking consent page, if an error tests will also fail
-        }
-
-        pause(WAIT_AFTER_OPERATION);
+    public void requestEntitlements(JavascriptStateValidator validator) {
+        testExecutor.executeAsyncScript("var callback = arguments[arguments.length - 1];" +
+                "window.authorization.entitlement('photoz-restful-api').then(function (rpt) {" +
+                "     callback(JSON.stringify(jwt_decode(rpt), null, '  '));" +
+                "});", validator);
     }
 
     private void waitForDenial() {
@@ -243,28 +177,18 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         pause(WAIT_AFTER_OPERATION);
     }
 
-    public void viewAlbum(String name, boolean shouldBeDenied) {
-        WebElement viewalbum = driver.findElement(By.xpath("//a[text() = '" + name + "']"));
-        waitUntilElement(viewalbum).is().clickable();
-        viewalbum.click();
-        waitForPageToLoad();
-        driver.navigate().refresh(); // This is sometimes necessary for loading the new policy settings
-        if (shouldBeDenied) {
-            waitForDenial();
-        } else {
-            waitForNotDenial();
-        }
-        waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
+    public void viewAlbum(String name, ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("GET")
+                        .addHeader("Accept", "application/json")
+                        .url(apiUrl + "/album/" + name + "/")
+                , validator);
     }
 
     public void accountPage() {
-        navigateTo();
-        WebElement myAccount = driver.findElement(By.id("my-account"));
-        waitUntilElement(myAccount).is().clickable();
-        myAccount.click();
+        testExecutor.openAccountPage(null);
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountMyResources() {
@@ -273,7 +197,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(myResources).is().clickable();
         myResources.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountMyResource(String name) {
@@ -282,7 +205,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(myResource).is().clickable();
         myResource.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountGrantResource(String name, String requester) {
@@ -291,7 +213,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(grantResource).is().clickable();
         grantResource.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountGrantRemoveScope(String name, String requester, String scope) {
@@ -300,7 +221,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(grantRemoveScope).is().clickable();
         grantRemoveScope.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountRevokeResource(String name, String requester) {
@@ -309,7 +229,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(revokeResource).is().clickable();
         revokeResource.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountShareResource(String name, String user) {
@@ -323,7 +242,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(shareButton).is().clickable();
         shareButton.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountShareRemoveScope(String name, String user, String scope) {
@@ -344,7 +262,6 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         shareButton.click();
         
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
     public void accountDenyResource(String name) {
@@ -353,25 +270,22 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
         waitUntilElement(denyLink).is().clickable();
         denyLink.click();
         waitForPageToLoad();
-        pause(WAIT_AFTER_OPERATION);
     }
 
-    public void requestResourceProtectedAnyScope(boolean shouldBeDenied) {
-        navigateTo();
-        WebElement requestPathWithAnyProtectedScope = driver.findElement(By.id("requestPathWithAnyProtectedScope"));
-        waitUntilElement(requestPathWithAnyProtectedScope).is().clickable();
-        requestPathWithAnyProtectedScope.click();
-        if (shouldBeDenied) waitForDenial();
-        pause(WAIT_AFTER_OPERATION);
+    public void requestResourceProtectedAnyScope(ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("GET")
+                        .url(apiUrl + "/scope-any")
+                , validator);
     }
 
-    public void requestResourceProtectedAllScope(boolean shouldBeDenied) {
-        navigateTo();
-        WebElement requestPathWithAllProtectedScope = driver.findElement(By.id("requestPathWithAllProtectedScope"));
-        waitUntilElement(requestPathWithAllProtectedScope).is().clickable();
-        requestPathWithAllProtectedScope.click();
-        if (shouldBeDenied) waitForDenial();
-        pause(WAIT_AFTER_OPERATION);
+    public void requestResourceProtectedAllScope(ResponseValidator validator) {
+        testExecutor.sendXMLHttpRequest(
+                XMLHttpRequest.create()
+                        .method("GET")
+                        .url(apiUrl + "/scope-all")
+                , validator);
     }
 
     public WebElement getOutput() {
@@ -380,8 +294,8 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
 
     @Override
     public void navigateTo() {
-        super.navigateTo();
-        pause(WAIT_AFTER_OPERATION);
+        driver.navigate().to(toString() + "/");
+        waitForPageToLoad();
     }
 
     @Override
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/javascript/JavascriptTestExecutorWithAuthorization.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/javascript/JavascriptTestExecutorWithAuthorization.java
new file mode 100644
index 0000000..8ccc554
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/javascript/JavascriptTestExecutorWithAuthorization.java
@@ -0,0 +1,167 @@
+package org.keycloak.testsuite.util.javascript;
+
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.auth.page.login.OAuthGrant;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.pages.ConsentPage;
+import org.keycloak.testsuite.util.URLUtils;
+import org.openqa.selenium.WebDriver;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
+
+/**
+ * @author mhajas
+ */
+public class JavascriptTestExecutorWithAuthorization extends JavascriptTestExecutor {
+
+    public static JavascriptTestExecutorWithAuthorization create(WebDriver driver, OIDCLogin loginPage) {
+        return new JavascriptTestExecutorWithAuthorization(driver, loginPage);
+    }
+
+    private JavascriptTestExecutorWithAuthorization(WebDriver driver, OIDCLogin loginPage) {
+        super(driver, loginPage);
+    }
+
+
+    @Override
+    public JavascriptTestExecutorWithAuthorization init(JSObjectBuilder argumentsBuilder, JavascriptStateValidator validator) {
+        super.init(argumentsBuilder, validator);
+        Object output = jsExecutor.executeScript(
+                "var callback = arguments[arguments.length - 1];" +
+                "window.authorization = new KeycloakAuthorization(window.keycloak);" +
+                "while (typeof window.authorization === 'undefined') {}" + // Wait until authorization is initialized
+                "return 'Authz initialized'");
+
+        assertThat(output, is("Authz initialized"));
+        return this;
+    }
+
+    @Override
+    public JavascriptTestExecutorWithAuthorization login(JavascriptStateValidator validator) {
+        super.login(validator);
+        return this;
+    }
+
+    public JavascriptTestExecutorWithAuthorization loginFormWithScopesWithPossibleConsentPage(UserRepresentation user, JavascriptStateValidator validator, OAuthGrant oAuthGrantPage, String... scopes) {
+        String currentUrl = jsDriver.getCurrentUrl();
+
+        if (scopes.length > 0) {
+            StringBuilder scopesValue = new StringBuilder();
+
+            for (String scope : scopes) {
+                if (scopesValue.length() != 0) {
+                    scopesValue.append(" ");
+                }
+                scopesValue.append(scope);
+            }
+
+            scopesValue.append(" openid");
+
+
+            StringBuilder urlWithScopeParam = new StringBuilder(currentUrl);
+
+            int scopeIndex = currentUrl.indexOf("scope");
+
+            if (scopeIndex != -1) {
+                // Remove scope param from url
+                urlWithScopeParam.delete(scopeIndex, currentUrl.indexOf('&', scopeIndex));
+                // Add scope param to the end of query
+                urlWithScopeParam.append("&").append("scope=");
+            }
+
+            if (!currentUrl.contains("?")) {
+                urlWithScopeParam.append("?scope=");
+            }
+
+            urlWithScopeParam.append(scopesValue);
+
+            URLUtils.navigateToUri(urlWithScopeParam.toString());
+            waitForPageToLoad();
+        }
+
+        loginFormWithPossibleConsentPage(user, oAuthGrantPage, validator);
+        return this;
+    }
+
+    public JavascriptTestExecutorWithAuthorization loginFormWithPossibleConsentPage(UserRepresentation user, OAuthGrant oAuthGrantPage, JavascriptStateValidator validator) {
+        super.loginForm(user);
+
+        try {
+            // simple check if we are at the consent page, if so just click 'Yes'
+            if (oAuthGrantPage.isCurrent(jsDriver)) {
+                oAuthGrantPage.accept();
+                waitForPageToLoad();
+            }
+        } catch (Exception ignore) {
+            // ignore errors when checking consent page, if an error tests will also fail
+        }
+
+        if (validator != null) {
+            validator.validate(jsDriver, null, events);
+        }
+
+        return this;
+    }
+
+    @Override
+    public JavascriptTestExecutor sendXMLHttpRequest(XMLHttpRequest request, ResponseValidator validator) {
+        // Intercept all requests and add rpt or token
+
+        // check if rpt is already present
+        Object o = jsExecutor.executeScript("if(window.authorization && window.authorization.rpt) return true; else return false;");
+
+
+
+        if (o == null || o.equals(false)) {
+            // RPT is not present yet, lets try to use bearer token
+            request.includeBearerToken();
+        } else {
+            // RPT token is present so we will use it
+            request.includeRpt();
+        }
+
+        // Try to send request
+        Map<String, Object> result = request.send(jsExecutor);
+
+        // If request was denied do UMA
+        if ((Long.valueOf(403).equals(result.get("status")) || Long.valueOf(401).equals(result.get("status")))) {
+            //extracting ticket from response
+            String headersString = (String) result.get("responseHeaders");
+
+            List<String> headersList = Arrays.asList(headersString.split("\r\n"));
+            String wwwAuthenticate = headersList.stream().filter(s -> s.toLowerCase().startsWith("www-authenticate:")).findFirst().get();
+
+            if (wwwAuthenticate.contains("UMA") && wwwAuthenticate.contains("ticket")) {
+                String ticket = Arrays.asList(wwwAuthenticate.split(",")).stream().filter(s -> s.startsWith("ticket")).findFirst().get();
+
+                ticket = ticket.substring(0, ticket.length() - 1).replaceFirst("ticket=\"", "");
+
+                // AuthorizationRequest for RPT
+                o = jsExecutor.executeAsyncScript(
+                        "var callback = arguments[arguments.length - 1];" +
+                        "window.authorization" +
+                        ".authorize(" + JSObjectBuilder.create().add("ticket", ticket).build() + ")" +
+                        ".then(function (rpt) {callback(rpt)}, function() {callback('failed1')}, function() {callback('failed2')});");
+
+                o = jsExecutor.executeScript("if(window.authorization && window.authorization.rpt) return true; else return false;"); // return window.authorization && window.authorization.rpt doesn't work
+
+                if (o != null && o.equals(true)) {
+                    request.includeRpt();
+                    result = request.send(jsExecutor);
+                }
+            }
+        }
+
+        if (validator != null) {
+            validator.validate(result);
+        }
+
+        return this;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index 344d68e..a1edd7f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -20,25 +20,19 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.hamcrest.CoreMatchers;
 import org.jboss.arquillian.container.test.api.Deployer;
-import org.jboss.arquillian.drone.api.annotation.Drone;
 import org.jboss.arquillian.graphene.page.Page;
 import org.jboss.arquillian.test.api.ArquillianResource;
-import org.jboss.shrinkwrap.api.asset.StringAsset;
-import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.ClientScopesResource;
 import org.keycloak.admin.client.resource.ClientsResource;
-import org.keycloak.admin.client.resource.ProtocolMappersResource;
 import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.admin.client.resource.RealmsResource;
 import org.keycloak.admin.client.resource.ResourcesResource;
 import org.keycloak.admin.client.resource.RoleResource;
 import org.keycloak.admin.client.resource.UserResource;
@@ -54,84 +48,89 @@ import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
-import org.keycloak.testsuite.ProfileAssume;
-import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
 import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
 import org.keycloak.testsuite.admin.ApiUtil;
-import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.arquillian.AppServerTestEnricher;
+import org.keycloak.testsuite.auth.page.login.OAuthGrant;
 import org.keycloak.testsuite.util.ContainerAssume;
 import org.keycloak.testsuite.util.DroneUtils;
 import org.keycloak.testsuite.util.JavascriptBrowser;
-import org.keycloak.testsuite.utils.io.IOUtil;
+import org.keycloak.testsuite.util.javascript.JavascriptTestExecutorWithAuthorization;
 import org.keycloak.util.JsonSerialization;
-import org.openqa.selenium.By;
-import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
 import static org.keycloak.testsuite.utils.io.IOUtil.loadJson;
 import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
 import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
-import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 
 import javax.ws.rs.core.Response;
-import org.keycloak.testsuite.arquillian.AppServerTestEnricher;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAdapterTest {
+public abstract class AbstractPhotozExampleAdapterTest extends AbstractPhotozJavascriptExecutorTest {
 
-    private static final String REALM_NAME = "photoz";
     protected static final String RESOURCE_SERVER_ID = "photoz-restful-api";
+    protected static final String ALICE_ALBUM_NAME = "Alice-Family-Album";
     private static final int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
 
     @ArquillianResource
     private Deployer deployer;
 
-    // Javascript browser needed KEYCLOAK-4703
-    @Drone
+    @Page
     @JavascriptBrowser
-    protected WebDriver jsDriver;
+    private PhotozClientAuthzTestApp clientPage;
 
     @Page
     @JavascriptBrowser
-    protected OIDCLogin jsDriverTestRealmLoginPage;
+    private OAuthGrant oAuthGrantPage;
 
-    @Page
+    private JavascriptTestExecutorWithAuthorization testExecutor;
+
+    @FindBy(id = "output")
     @JavascriptBrowser
-    private PhotozClientAuthzTestApp clientPage;
+    protected WebElement outputArea;
+
+    @FindBy(id = "events")
+    @JavascriptBrowser
+    protected WebElement eventsArea;
 
     @Override
     public void setDefaultPageUriParameters() {
         super.setDefaultPageUriParameters();
         testRealmPage.setAuthRealm(REALM_NAME);
+        oAuthGrantPage.setAuthRealm(REALM_NAME);
     }
 
     @Before
     public void beforePhotozExampleAdapterTest() throws Exception {
         DroneUtils.addWebDriver(jsDriver);
-        deleteAllCookiesForClientPage();
         this.deployer.deploy(RESOURCE_SERVER_ID);
-        
+
+        clientPage.navigateTo();
+        waitForPageToLoad();
+        assertCurrentUrlStartsWith(clientPage.toString());
+
+        testExecutor = JavascriptTestExecutorWithAuthorization.create(jsDriver, jsDriverTestRealmLoginPage);
+        clientPage.setTestExecutorPlayground(testExecutor, appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
+        jsDriver.manage().deleteAllCookies();
+
         try (CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build()) {
             HttpGet request = new HttpGet(clientPage.toString() + "/unsecured/clean");
             httpClient.execute(request).close();
@@ -159,18 +158,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         importResourceServerSettings();
     }
 
-    //revert provider in persistence.xml for EAP6 backward compatibility
-    protected static void updatePersistenceXml(WebArchive war) {
-        //double check app server is eap6
-        if (AppServerTestEnricher.isEAP6AppServer()) {
-            String persistanceXmlPath = "/WEB-INF/classes/META-INF/persistence.xml";
-            org.w3c.dom.Document persistenceXml = IOUtil.loadXML(war.get(persistanceXmlPath).getAsset().openStream());
-            IOUtil.modifyDocElementValue(persistenceXml, "provider", 
-                    "org.hibernate.jpa.HibernatePersistenceProvider", "org.hibernate.ejb.HibernatePersistence");
-            war.add(new StringAsset((IOUtil.documentToString(persistenceXml))), persistanceXmlPath);
-        }
-    }
-
     private List<ResourceRepresentation> getResourcesOfUser(String username) throws FileNotFoundException {
         return getAuthorizationResource().resources().resources().stream().filter(resource -> resource.getOwner().getName().equals(username)).collect(Collectors.toList());
     }
@@ -178,50 +165,62 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
     private void printUpdatedPolicies() throws FileNotFoundException {
         log.debug("Check updated policies");
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
-            log.debugf("Policy: {0}", policy.getName());
+            log.debugf("Policy: %s", policy.getName());
             for (String key : policy.getConfig().keySet()) {
-                log.debugf("-- key: {0}, value: {1}", key, policy.getConfig().get(key));
+                log.debugf("-- key: %s, value: %s", key, policy.getConfig().get(key));
             }
         }
         log.debug("------------------------------");
     }
+
+    private void assertOnTestAppUrl(WebDriver jsDriver, Object output, WebElement events) {
+        waitForPageToLoad();
+        assertCurrentUrlStartsWith(clientPage.toString(), jsDriver);
+    }
+
+    private void assertWasDenied(Map<String, Object> response) {
+        assertThat(response.get("status")).isEqualTo(401L);
+    }
+
+    private void assertWasNotDenied(Map<String, Object> response) {
+        assertThat(response.get("status")).isEqualTo(200L);
+    }
     
     @Test
     public void testUserCanCreateAndDeleteAlbum() throws Exception {
-        loginToClientPage("alice", "alice");
+        loginToClientPage(aliceUser);
 
-        clientPage.createAlbum("Alice Family Album");
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
         log.debug("Check if alice has resources stored");
-        assertThat(getResourcesOfUser("alice"), is(not(empty())));
+        assertThat(getResourcesOfUser("alice")).isNotEmpty();
 
-        clientPage.deleteAlbum("Alice Family Album", false);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
         log.debug("Check if alice has resources deleted");
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        assertThat(getResourcesOfUser("alice")).isEmpty();
     }
 
     @Test
     public void createAlbumWithInvalidUser() throws Exception {
-        loginToClientPage("alice", "alice");
-
-        clientPage.createAlbumWithInvalidUser("Alice Family Album");
+        loginToClientPage(aliceUser);
 
-        log.debug("Check if the album was not created.");
-        waitUntilElement(clientPage.getOutput()).text().not().contains("Request was successful");
-        waitUntilElement(clientPage.getOutput()).text().contains("Could not register protected resource");
+        clientPage.createAlbumWithInvalidUser(ALICE_ALBUM_NAME, response -> {
+            assertThat(response.get("status")).isEqualTo(500L);
+            assertThat(response.get("res")).isEqualTo("Could not register protected resource.");
+        });
     }
 
     @Test
     public void testOnlyOwnerCanDeleteAlbum() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
+        ContainerAssume.assumeNotAppServerUndertow();
 
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice-Family-Album");
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
 
-        loginToClientPage("admin", "admin");
-        clientPage.navigateToAdminAlbum(false);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
 
         log.debug("Check if alice has resources stored");
-        assertThat(getResourcesOfUser("alice"), is(not(empty())));
+        assertThat(getResourcesOfUser("alice")).isNotEmpty();
 
         log.debug("Adding applyPolicies \"Only Owner Policy\" to \"Delete Album Permission\" policies.");
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
@@ -232,13 +231,13 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        loginToClientPage("admin", "admin");
+        loginToClientPage(adminUser);
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice-Family-Album", true);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
         
         log.debug("Check if alice has resources stored");
-        assertThat(getResourcesOfUser("alice"), is(not(empty())));
+        assertThat(getResourcesOfUser("alice")).isNotEmpty();
 
         log.debug("Adding applyPolicies \"Only Owner and Administrators Policy\" to \"Delete Album Permission\" policies.");
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
@@ -249,51 +248,52 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice-Family-Album", false);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
         
         log.debug("Check if alice has resources deleted");
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        assertThat(getResourcesOfUser("alice")).isEmpty();
     }
  
     
     @Test
     public void testRegularUserCanNotAccessAdminResources() throws Exception {
-        loginToClientPage("alice", "alice");
-        clientPage.navigateToAdminAlbum(true);
+        loginToClientPage(aliceUser);
+        clientPage.navigateToAdminAlbum(this::assertWasDenied);
     }
 
     @Test
     public void testAdminOnlyFromSpecificAddress() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
-
-        loginToClientPage("admin", "admin");
-        clientPage.navigateToAdminAlbum(false);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
 
         log.debug("Changing codes \"127.0.0.1\" to \"127.3.3.3\" of \"Only From a Specific Client Address\" policies.");
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Only From a Specific Client Address".equals(policy.getName())) {
-                String code = policy.getConfig().get("code");
-                policy.getConfig().put("code", code.replaceAll("127.0.0.1", "127.3.3.3"));
+                String code = policy.getConfig().get("code")
+                        .replaceAll("127.0.0.1", "127.3.3.3")
+                        .replaceAll("0:0:0:0:0:0:0:1", "0:0:0:0:0:ffff:7f03:303");
+                policy.getConfig().put("code", code);
                 getAuthorizationResource().policies().policy(policy.getId()).update(policy);
             }
         }
         printUpdatedPolicies();
 
-        clientPage.navigateToAdminAlbum(true);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasDenied);
     }
 
     @Test
     public void testAdminWithoutPermissionsToTypedResource() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
+        ContainerAssume.assumeNotAppServerUndertow();
 
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice Family Album");
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
         
-        loginToClientPage("admin", "admin");
-        clientPage.navigateToAdminAlbum(false);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
 
-        clientPage.viewAlbum("Alice Family Album", false);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
 
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Album Resource Permission".equals(policy.getName())) {
@@ -315,11 +315,13 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        clientPage.navigateToAdminAlbum(false);
+        loginToClientPage(adminUser); // Clear cache
+
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
 
-        clientPage.viewAlbum("Alice Family Album", true);
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice Family Album", true);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Album Resource Permission".equals(policy.getName())) {
@@ -329,26 +331,27 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum("Alice Family Album", false);
+        loginToClientPage(adminUser); // Clear cache
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice Family Album", false);
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        assertThat(getResourcesOfUser("alice")).isEmpty();
     }
 
     @Test
     public void testAdminWithoutPermissionsToDeleteAlbum() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
-
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice Family Album");
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
 
-        loginToClientPage("admin", "admin");
-        clientPage.navigateToAdminAlbum(false);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
 
-        clientPage.deleteAlbum("Alice Family Album", false);
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        assertThat(getResourcesOfUser("alice")).isEmpty();
         
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Delete Album Permission".equals(policy.getName())) {
@@ -358,16 +361,16 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice Family Album");
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
 
-        loginToClientPage("admin", "admin");
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum("Alice Family Album", false);
-        assertThat(getResourcesOfUser("alice"), is(not(empty())));
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        assertThat(getResourcesOfUser("alice")).isNotEmpty();
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice Family Album", true);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Delete Album Permission".equals(policy.getName())) {
@@ -377,18 +380,18 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum("Alice Family Album", false);
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        loginToClientPage(adminUser); // Clear cache
+
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        assertThat(getResourcesOfUser("alice")).isEmpty();
     }
 
     @Test
     public void testClientRoleRepresentingUserConsent() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
-
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice Family Album");
-        clientPage.viewAlbum("Alice Family Album", false);
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
 
         RealmResource realmResource = realmsResouce().realm(REALM_NAME);
         UsersResource usersResource = realmResource.users();
@@ -406,19 +409,19 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
 
         setManageAlbumScopeRequired();
 
-        loginToClientPage("alice", "alice");
-        clientPage.viewAlbum("Alice Family Album", true);
+        loginToClientPage(aliceUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
-        loginToClientPage("alice", "alice", "manage-albums");
-        clientPage.viewAlbum("Alice Family Album", false);
+        loginToClientPage(aliceUser, "manage-albums");
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
     }
 
     @Test
     public void testClientRoleNotRequired() throws Exception {
-        loginToClientPage("alice", "alice");
+        loginToClientPage(aliceUser);
 
-        clientPage.createAlbum("Alice Family Album");
-        clientPage.viewAlbum("Alice Family Album", false);
+        clientPage.createAlbum(ALICE_ALBUM_NAME);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
 
         UsersResource usersResource = realmsResouce().realm(REALM_NAME).users();
         List<UserRepresentation> users = usersResource.search("alice", null, null, null, null, null);
@@ -440,8 +443,8 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
 
         manageAlbumRole.update(roleRepresentation);
 
-        loginToClientPage("alice", "alice");
-        clientPage.viewAlbum("Alice Family Album", true);
+        loginToClientPage(aliceUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
         for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
             if ("Any User Policy".equals(policy.getName())) {
@@ -460,34 +463,28 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
         printUpdatedPolicies();
 
-        loginToClientPage("alice", "alice");
-        clientPage.viewAlbum("Alice Family Album", false);
+        loginToClientPage(aliceUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
     }
 
     @Test
     public void testOverridePermissionFromResourceParent() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
-
-        loginToClientPage("alice", "alice");
-        String resourceName = "My Resource Instance";
+        loginToClientPage(aliceUser);
+        String resourceName = "My-Resource-Instance";
         clientPage.createAlbum(resourceName);
 
-        clientPage.viewAlbum(resourceName, false);
-
-        clientPage.navigateTo();
-        clientPage.deleteAlbum(resourceName, false);
+        clientPage.viewAlbum(resourceName, this::assertWasNotDenied);
+        clientPage.deleteAlbum(resourceName, this::assertWasNotDenied);
 
         clientPage.createAlbum(resourceName);
 
-        loginToClientPage("admin", "admin");
+        loginToClientPage(adminUser);
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum(resourceName, false);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(resourceName, this::assertWasNotDenied);
+        clientPage.deleteAlbum(resourceName, this::assertWasNotDenied);
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum(resourceName, false);
-
-        loginToClientPage("alice", "alice");
+        loginToClientPage(aliceUser);
         clientPage.createAlbum(resourceName);
 
         getAuthorizationResource().resources().resources().forEach(resource -> {
@@ -512,54 +509,45 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         });
         printUpdatedPolicies();
 
-        loginToClientPage("admin", "admin");
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum(resourceName, true);
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum(resourceName, true);
+        loginToClientPage(adminUser);
 
-        loginToClientPage("alice", "alice");
-        clientPage.deleteAlbum(resourceName, false);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(resourceName, this::assertWasDenied);
+        clientPage.deleteAlbum(resourceName, this::assertWasDenied);
 
-        assertThat(getResourcesOfUser("alice"), is(empty()));
+        loginToClientPage(aliceUser);
+        clientPage.deleteAlbum(resourceName, this::assertWasNotDenied);
+        assertThat(getResourcesOfUser("alice")).isEmpty();
     }
 
     @Test
     public void testInheritPermissionFromResourceParent() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
+        ContainerAssume.assumeNotAppServerUndertow();
 
-        loginToClientPage("alice", "alice");
+        loginToClientPage(aliceUser);
 
-        String resourceName = "My Resource Instance";
-        clientPage.createAlbum(resourceName);
+        final String RESOURCE_NAME = "My-Resource-Instance";
+        clientPage.createAlbum(RESOURCE_NAME);
+        clientPage.viewAlbum(RESOURCE_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(RESOURCE_NAME, this::assertWasNotDenied);
 
-        clientPage.viewAlbum(resourceName, false);
+        clientPage.createAlbum(RESOURCE_NAME);
 
-        clientPage.navigateTo();
-        clientPage.deleteAlbum(resourceName, false);
+        loginToClientPage(adminUser);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(RESOURCE_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(RESOURCE_NAME, this::assertWasNotDenied);
 
-        clientPage.createAlbum(resourceName);
-
-        loginToClientPage("admin", "admin");
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum(resourceName, false);
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum(resourceName, false);
-
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum(resourceName);
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(RESOURCE_NAME);
 
         ResourcesResource resourcesResource = getAuthorizationResource().resources();
         resourcesResource.resources().forEach(resource -> {
-            if (resource.getName().equals(resourceName)) {
+            if (resource.getName().equals(RESOURCE_NAME)) {
                 try {
                     PolicyRepresentation resourceInstancePermission = new PolicyRepresentation();
 
-                    resourceInstancePermission.setName(resourceName + "Permission");
+                    resourceInstancePermission.setName(RESOURCE_NAME + "Permission");
                     resourceInstancePermission.setType("resource");
 
                     Map<String, String> config = new HashMap<>();
@@ -575,140 +563,151 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
             }
         });
 
-        loginToClientPage("admin", "admin");
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum(resourceName, true);
+        loginToClientPage(adminUser);
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum(resourceName, true);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(RESOURCE_NAME, this::assertWasDenied);
+        clientPage.deleteAlbum(RESOURCE_NAME, this::assertWasDenied);
 
         resourcesResource.resources().forEach(resource -> {
-            if (resource.getName().equals(resourceName)) {
+            if (resource.getName().equals(RESOURCE_NAME)) {
                 resource.setScopes(resource.getScopes().stream().filter(scope -> !scope.getName().equals("album:view")).collect(Collectors.toSet()));
                 resourcesResource.resource(resource.getId()).update(resource);
             }
         });
 
-        loginToClientPage("admin", "admin");
-
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.viewAlbum(resourceName, false);
+        loginToClientPage(adminUser);
 
-        clientPage.navigateToAdminAlbum(false);
-        clientPage.deleteAlbum(resourceName, true);
+        clientPage.navigateToAdminAlbum(this::assertWasNotDenied);
+        clientPage.viewAlbum(RESOURCE_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(RESOURCE_NAME, this::assertWasDenied);
 
-        loginToClientPage("alice", "alice");
-        clientPage.deleteAlbum(resourceName, false);
+        loginToClientPage(aliceUser);
+        clientPage.deleteAlbum(RESOURCE_NAME, this::assertWasNotDenied);
         List<ResourceRepresentation> resources = resourcesResource.resources();
         assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
-
-        resourcesResource.resources().forEach(resource -> {
-            if (resource.getName().equals(resourceName)) {
-                resource.setScopes(Collections.emptySet());
-                resourcesResource.resource(resource.getId()).update(resource);
-            }
-        });
     }
 
     //KEYCLOAK-3777
 
     @Test
     public void testEntitlementRequest() throws Exception {
-        ContainerAssume.assumeNotAuthServerUndertow();
-
-        clientPage.navigateTo();
-        loginToClientPage("admin", "admin");
+        loginToClientPage(adminUser);
 
-        clientPage.requestEntitlements();
-        assertTrue(jsDriver.getPageSource().contains("admin:manage"));
+        clientPage.requestEntitlements((driver1, output, events) -> assertThat((String)output).contains("admin:manage"));
 
-        clientPage.requestEntitlement();
-        String pageSource = jsDriver.getPageSource();
-        assertTrue(pageSource.contains("album:view"));
-        assertTrue(pageSource.contains("album:delete"));
+        loginToClientPage(adminUser);
+        clientPage.requestEntitlement((driver1, output, events) -> assertThat((String)output)
+                .doesNotContain("admin:manage")
+                .contains("album:view")
+                .contains("album:delete")
+        );
     }
+
     @Test
     public void testResourceProtectedWithAnyScope() throws Exception {
-        loginToClientPage("alice", "alice");
-        clientPage.requestResourceProtectedAllScope(true);
-        clientPage.requestResourceProtectedAnyScope(false);
+        loginToClientPage(aliceUser);
+
+        clientPage.requestResourceProtectedAllScope(this::assertWasDenied);
+        clientPage.requestResourceProtectedAnyScope(response -> {
+            assertThat(response.get("status")).isIn(404L, 0L); // PhantomJS returns 0 and chrome 404
+        });
     }
 
+
     @Test
     public void testRequestResourceToOwner() throws Exception {
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice-Family-Album", true);
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME, true);
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", true);
-        clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", true);
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
-        loginToClientPage("alice", "alice");
-        clientPage.accountGrantResource("Alice-Family-Album", "jdoe");
+        loginToClientPage(aliceUser);
+        clientPage.accountGrantResource(ALICE_ALBUM_NAME, "jdoe");
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", false);
+        // get back to clientPage and init javascript adapter in order to log out correctly
         clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", false);
 
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice-Family-Album", true);
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                    .login()
+                    .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", true);
-        clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", true);
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME, true);
 
-        loginToClientPage("alice", "alice");
-        clientPage.accountGrantRemoveScope("Alice-Family-Album", "jdoe", "album:delete");
-        clientPage.accountGrantResource("Alice-Family-Album", "jdoe");
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", false);
+        loginToClientPage(aliceUser);
+        clientPage.accountGrantRemoveScope(ALICE_ALBUM_NAME, "jdoe", "album:delete");
+
+        // get back to clientPage and init javascript adapter in order to navigate to accountPage again
+        clientPage.navigateTo();
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login(this::assertOnTestAppUrl)
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
+        clientPage.accountGrantResource(ALICE_ALBUM_NAME, "jdoe");
+
+        // get back to clientPage and init javascript adapter in order to log out correctly
         clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", true);
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login()
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
+
+
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
     }
 
     @Test
     public void testOwnerSharingResource() throws Exception {
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice-Family-Album", true);
-        clientPage.accountShareResource("Alice-Family-Album", "jdoe");
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME, true);
+        clientPage.accountShareResource(ALICE_ALBUM_NAME, "jdoe");
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", false);
+        // get back to clientPage and init javascript adapter in order to log out correctly
         clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", false);
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login()
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
 
-        loginToClientPage("alice", "alice");
-        clientPage.createAlbum("Alice-Family-Album", true);
-        clientPage.accountShareRemoveScope("Alice-Family-Album", "jdoe", "album:delete");
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", false);
+        loginToClientPage(aliceUser);
+        clientPage.createAlbum(ALICE_ALBUM_NAME, true);
+        clientPage.accountShareRemoveScope(ALICE_ALBUM_NAME, "jdoe", "album:delete");
+
+        // get back to clientPage and init javascript adapter in order to log out correctly
         clientPage.navigateTo();
-        clientPage.viewAllAlbums();
-        clientPage.deleteAlbum("Alice-Family-Album", true);
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login(this::assertOnTestAppUrl)
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
+
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasNotDenied);
+        clientPage.deleteAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
 
-        loginToClientPage("alice", "alice");
-        clientPage.accountRevokeResource("Alice-Family-Album", "jdoe");
+        loginToClientPage(aliceUser);
+        clientPage.accountRevokeResource(ALICE_ALBUM_NAME, "jdoe");
 
-        loginToClientPage("jdoe", "jdoe");
-        clientPage.viewAllAlbums();
-        clientPage.viewAlbum("Alice-Family-Album", true);
+        // get back to clientPage and init javascript adapter in order to log out correctly
+        clientPage.navigateTo();
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login()
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
+
+        loginToClientPage(jdoeUser);
+        clientPage.viewAlbum(ALICE_ALBUM_NAME, this::assertWasDenied);
     }
 
     private void importResourceServerSettings() throws FileNotFoundException {
@@ -731,41 +730,23 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         return clients.get(resourceServer.getId());
     }
 
-    private void deleteAllCookiesForClientPage() {
-        jsDriver.manage().deleteAllCookies();
-    }
+    private void loginToClientPage(UserRepresentation user, String... scopes) throws InterruptedException {
+        log.debugf("--logging in as {0} with password: {1}; scopes: {2}", user.getUsername(), user.getCredentials().get(0).getValue(), Arrays.toString(scopes));
 
-    private void loginToClientPage(String username, String password, String... scopes) throws InterruptedException {
-        log.debugf("--logging in as {0} with password: {1}; scopes: {2}", username, password, Arrays.toString(scopes));
+        if (testExecutor.isLoggedIn()) {
+            testExecutor.logout(this::assertOnTestAppUrl);
+            jsDriver.manage().deleteAllCookies();
 
-        clientPage.navigateTo();
-        if (jsDriver.getCurrentUrl().startsWith(clientPage.toString())) {
-            try {
-                clientPage.logOut();
-            } catch (NoSuchElementException ex) {
-                if ("phantomjs".equals(System.getProperty("js.browser"))) {
-                    // PhantomJS is broken, it can't logout using sign out button sometimes, we have to clean sessions and remove cookies
-                    adminClient.realm(REALM_NAME).logoutAll();
-
-                    jsDriverTestRealmLoginPage.navigateTo();
-                    driver.manage().deleteAllCookies();
-
-                    clientPage.navigateTo();
-                    driver.manage().deleteAllCookies();
-
-                    clientPage.navigateTo();
-                    // Check for correct logout
-                    assertCurrentUrlStartsWithLoginUrlOf(jsDriverTestRealmLoginPage);
-                } else {
-                    throw ex;
-                }
-            }
+            jsDriver.navigate().to(testRealmLoginPage.toString());
+            waitForPageToLoad();
+            jsDriver.manage().deleteAllCookies();
         }
 
         clientPage.navigateTo();
-        waitForPageToLoad();
-        clientPage.login(username, password, scopes);
-        waitUntilElement(By.linkText("Sign Out")).is().clickable();
+        testExecutor.init(defaultArguments(), this::assertInitNotAuth)
+                .login(this::assertOnLoginPage)
+                .loginFormWithScopesWithPossibleConsentPage(user, this::assertOnTestAppUrl, oAuthGrantPage, scopes)
+                .init(defaultArguments(), this::assertSuccessfullyLoggedIn);
     }
 
     private void setManageAlbumScopeRequired() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozJavascriptExecutorTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozJavascriptExecutorTest.java
new file mode 100644
index 0000000..030ad72
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozJavascriptExecutorTest.java
@@ -0,0 +1,108 @@
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.adapter.javascript.AbstractJavascriptTest;
+import org.keycloak.testsuite.auth.page.login.OAuthGrant;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.util.JavascriptBrowser;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.testsuite.util.javascript.JSObjectBuilder;
+import org.keycloak.testsuite.util.javascript.JavascriptStateValidator;
+import org.keycloak.testsuite.util.javascript.ResponseValidator;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+/**
+ * @author mhajas
+ */
+public abstract class AbstractPhotozJavascriptExecutorTest extends AbstractExampleAdapterTest {
+
+    @FunctionalInterface
+    interface QuadFunction<T, U, V, W> {
+        void apply(T a, U b, V c, W d);
+    }
+
+    protected static final String REALM_NAME = "photoz";
+
+    @Page
+    @JavascriptBrowser
+    protected OIDCLogin jsDriverTestRealmLoginPage;
+
+    @Page
+    @JavascriptBrowser
+    private OAuthGrant oAuthGrantPage;
+
+    @Drone
+    @JavascriptBrowser
+    protected WebDriver jsDriver;
+
+    protected UserRepresentation aliceUser = UserBuilder.create().username("alice").password("alice").build();
+
+    protected UserRepresentation adminUser = UserBuilder.create().username("admin").password("admin").build();
+
+    protected UserRepresentation jdoeUser = UserBuilder.create().username("jdoe").password("jdoe").build();
+
+    @Before
+    public void setDefaultValues() {
+        jsDriverTestRealmLoginPage.setAuthRealm(REALM_NAME);
+    }
+
+    protected <T> JavascriptStateValidator buildFunction(QuadFunction<T, WebDriver, Object, WebElement> f, T x) {
+        return (y,z,w) -> f.apply(x, y, z, w);
+    }
+
+    public void assertOutputContains(String value, WebDriver driver1, Object output, WebElement events) {
+        if (output instanceof WebElement) {
+            waitUntilElement((WebElement) output).text().contains(value);
+        } else {
+            Assert.assertThat((String) output, containsString(value));
+        }
+    }
+
+    protected JSObjectBuilder defaultArguments() {
+        return JSObjectBuilder.create().defaultSettings();
+    }
+
+    protected void assertSuccessfullyLoggedIn(WebDriver driver1, Object output, WebElement events) {
+        buildFunction(this::assertOutputContains, "Init Success (Authenticated)").validate(driver1, output, events);
+    }
+
+    protected void assertInitNotAuth(WebDriver driver1, Object output, WebElement events) {
+        buildFunction(this::assertOutputContains, "Init Success (Not Authenticated)").validate(driver1, output, events);
+    }
+
+    protected void assertOnLoginPage(WebDriver driver1, Object output, WebElement events) {
+        waitUntilElement(By.tagName("body")).is().present();
+        try {
+            assertCurrentUrlStartsWith(jsDriverTestRealmLoginPage, driver1);
+        } catch (AssertionError e) {
+            System.out.println("Test");
+            throw e;
+        }
+    }
+
+    protected JavascriptStateValidator all(JavascriptStateValidator[] toValidate)  {
+        return ((driver1, output, events) -> {
+            for (JavascriptStateValidator val : toValidate) {
+                val.validate(driver1, output, events);
+            }
+        });
+    }
+    protected ResponseValidator all(ResponseValidator[] toValidate) {
+        return ((response) -> {
+            for (ResponseValidator val : toValidate) {
+                val.validate(response);
+            }
+        });
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleLazyLoadPathsAdapterTest.java
index 0b8f51d..df41c8b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleLazyLoadPathsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleLazyLoadPathsAdapterTest.java
@@ -34,6 +34,7 @@ import org.keycloak.testsuite.arquillian.containers.ContainerConstants;
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP)
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
+@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
 public class PhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
 
     @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
@@ -43,15 +44,7 @@ public class PhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExample
 
     @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
     public static WebArchive deploymentResourceServer() throws IOException {
-        WebArchive war = exampleDeployment(RESOURCE_SERVER_ID)
+        return exampleDeployment(RESOURCE_SERVER_ID)
                 .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/photoz/keycloak-lazy-load-path-authz-service.json"), "keycloak.json");
-        
-        // cannot use EAP6DeploymentArchiveProcessor because the deployment is marked
-        // as non managed by arquillian
-        if (AppServerTestEnricher.isEAP6AppServer()) {
-            updatePersistenceXml(war);
-        }
-        return war;
     }
-
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleNoLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleNoLazyLoadPathsAdapterTest.java
index b416d99..36443c0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleNoLazyLoadPathsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/PhotozExampleNoLazyLoadPathsAdapterTest.java
@@ -33,6 +33,7 @@ import org.keycloak.testsuite.arquillian.containers.ContainerConstants;
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP)
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
 @AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
+@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
 public class PhotozExampleNoLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
 
     @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
@@ -42,14 +43,7 @@ public class PhotozExampleNoLazyLoadPathsAdapterTest extends AbstractPhotozExamp
 
     @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
     public static WebArchive deploymentResourceServer() throws IOException {
-        WebArchive war = exampleDeployment(RESOURCE_SERVER_ID);
-        
-        // cannot use EAP6DeploymentArchiveProcessor because the deployment is marked
-        // as non managed by arquillian
-        if (AppServerTestEnricher.isEAP6AppServer()) {
-            updatePersistenceXml(war);
-        }
-        return war;
+        return exampleDeployment(RESOURCE_SERVER_ID);
     }
 
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/AbstractJavascriptTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/AbstractJavascriptTest.java
index 789bebf..a649840 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/AbstractJavascriptTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/AbstractJavascriptTest.java
@@ -17,6 +17,8 @@ import org.keycloak.testsuite.util.JavascriptBrowser;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.RolesBuilder;
 import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.testsuite.util.javascript.JavascriptStateValidator;
+import org.keycloak.testsuite.util.javascript.ResponseValidator;
 import org.openqa.selenium.By;
 import org.openqa.selenium.Cookie;
 import org.openqa.selenium.JavascriptExecutor;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/JavascriptAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/JavascriptAdapterTest.java
index 8d62b28..e4a05c1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/JavascriptAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/javascript/JavascriptAdapterTest.java
@@ -24,6 +24,9 @@ import org.keycloak.testsuite.util.JavascriptBrowser;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.testsuite.util.javascript.JSObjectBuilder;
+import org.keycloak.testsuite.util.javascript.JavascriptTestExecutor;
+import org.keycloak.testsuite.util.javascript.XMLHttpRequest;
 import org.openqa.selenium.TimeoutException;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebDriverException;
@@ -83,10 +86,10 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
         applicationsPage.setAuthRealm(REALM_NAME);
 
         jsDriver.navigate().to(testAppUrl);
-        testExecutor = JavascriptTestExecutor.create(jsDriver, jsDriverTestRealmLoginPage);
 
         waitUntilElement(outputArea).is().present();
         assertCurrentUrlStartsWith(testAppUrl, jsDriver);
+        testExecutor = JavascriptTestExecutor.create(jsDriver, jsDriverTestRealmLoginPage);
 
         jsDriver.manage().deleteAllCookies();
 
@@ -168,6 +171,8 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
 
     @Test
     public void grantBrowserBasedApp() {
+        Assume.assumeTrue("This test doesn't work with phantomjs", !"phantomjs".equals(System.getProperty("js.browser")));
+
         ClientResource clientResource = ApiUtil.findClientResourceByClientId(adminClient.realm(REALM_NAME), CLIENT_ID);
         ClientRepresentation client = clientResource.toRepresentation();
         client.setConsentRequired(true);
@@ -403,7 +408,7 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
                             List<UserRepresentation> users = adminClient.realm(REALM_NAME).users().search("mhajas", 0, 1);
                             assertEquals("There should be created user mhajas", 1, users.size());
 
-                            assertThat((String) response.get("responseHeaders"), containsString("location: " + authServerContextRootPage.toString() + "/auth/admin/realms/" + REALM_NAME + "/users/" + users.get(0).getId()));
+                            assertThat(((String) response.get("responseHeaders")).toLowerCase(), containsString("location: " + authServerContextRootPage.toString() + "/auth/admin/realms/" + REALM_NAME + "/users/" + users.get(0).getId()));
                         });
     }
 
diff --git a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/undertow/UndertowDeployerHelper.java b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/undertow/UndertowDeployerHelper.java
index cb91e39..a4f11ee 100644
--- a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/undertow/UndertowDeployerHelper.java
+++ b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/undertow/UndertowDeployerHelper.java
@@ -75,7 +75,7 @@ public class UndertowDeployerHelper {
                 di = new DeploymentInfo();
             }
 
-            UndertowWarClassLoader classLoader = new UndertowWarClassLoader(UndertowDeployerHelper.class.getClassLoader(), archive);
+            UndertowWarClassLoader classLoader = new UndertowWarClassLoader(Thread.currentThread().getContextClassLoader(), archive);
             di.setClassLoader(classLoader);
 
             di.setDeploymentName(archiveName);