keycloak-memoizeit

Changes

Details

diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractAccountPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractAccountPage.java
new file mode 100644
index 0000000..9b0a386
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractAccountPage.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import org.keycloak.testsuite.page.PatternFlyClosableAlert;
+
+import javax.ws.rs.core.UriBuilder;
+import java.util.List;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class AbstractAccountPage extends AuthRealm {
+    @Page
+    private PatternFlyClosableAlert alert;
+
+    public AbstractAccountPage() {
+        setAuthRealm(TEST);
+    }
+
+    /**
+     * Account Console is based on hash routing, e.g. [server_root]/auth/realms/test/account/#/password.
+     * All page objects for Account Console need to specify their "hash path" using this method.
+     *
+     * @return the hash path
+     */
+    protected abstract List<String> createHashPath();
+
+    @Override
+    public UriBuilder createUriBuilder() {
+        String fragment = null;
+        final List<String> hashPath = createHashPath();
+        if (hashPath != null) {
+            fragment = "/" + String.join("/", hashPath);
+        }
+
+        return super.createUriBuilder().path("account/").fragment(fragment);
+    }
+
+    public PatternFlyClosableAlert alert() {
+        return alert;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractLoggedInPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractLoggedInPage.java
new file mode 100644
index 0000000..1b65ce4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AbstractLoggedInPage.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.VerticalNavBar;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class AbstractLoggedInPage extends AbstractAccountPage {
+    @Page
+    protected WelcomeScreen welcomeScreen;
+
+    @FindBy(className = "nav-pf-vertical nav-pf-vertical-with-badges")
+    private VerticalNavBar verticalNavBar;
+
+    @FindBy(id = "pageTitle")
+    protected WebElement pageTitle;
+
+    @Override
+    protected List<String> createHashPath() {
+        return new ArrayList<>();
+    }
+
+    /**
+     * This should simulate a user navigating to this page using links in the nav bar. It assume that user is logged in
+     * and at some Account Console page (not Welcome Screen), i.e. that the nav bar is visible.
+     */
+    public abstract void navigateToUsingNavBar();
+
+    public VerticalNavBar verticalNavBar() {
+        return verticalNavBar;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ApplicationsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ApplicationsPage.java
new file mode 100644
index 0000000..662a514
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ApplicationsPage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ApplicationsPage extends AbstractLoggedInPage {
+    @Page
+    private ApplicationsCard applicationsCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("applications");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && applicationsCard.isVisible();
+    }
+
+    public ApplicationsCard applications() {
+        return applicationsCard;
+    }
+
+    public class ApplicationsCard extends Card {
+        @FindBy(className = "card-pf-application")
+        private WebElement cardRoot;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(cardRoot);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AuthenticatorPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AuthenticatorPage.java
new file mode 100644
index 0000000..638d161
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/AuthenticatorPage.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class AuthenticatorPage extends AbstractLoggedInPage {
+    @Page
+    private YourAuthenticatorsCard yourAuthenticatorsCard;
+    @Page
+    private AddAuthenticatorCard addAuthenticatorCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("authenticator");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && yourAuthenticators().isVisible() && addAuthenticator().isVisible();
+    }
+
+    public YourAuthenticatorsCard yourAuthenticators() {
+        return yourAuthenticatorsCard;
+    }
+
+    public AddAuthenticatorCard addAuthenticator() {
+        return addAuthenticatorCard;
+    }
+
+    public class YourAuthenticatorsCard extends Card {
+        @FindBy(id = "authenticatorFinishSetUpTitle")
+        private WebElement yourAuthenticatorsTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(yourAuthenticatorsTitle);
+        }
+    }
+
+    public class AddAuthenticatorCard extends Card {
+        @FindBy(id = "authenticatorSubTitle")
+        private WebElement addAuthenticatorTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(addAuthenticatorTitle);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ChangePasswordPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ChangePasswordPage.java
new file mode 100644
index 0000000..ab10ace
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ChangePasswordPage.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.clickBtnAndWaitForAlert;
+import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ChangePasswordPage extends AbstractLoggedInPage {
+    @Page
+    private UpdatePasswordCard updatePasswordCard;
+    @Page
+    private PasswordLastUpdateCard passwordLastUpdateCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("password");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && updatePassword().isVisible();
+    }
+
+    public PasswordLastUpdateCard passwordLastUpdate() {
+        return passwordLastUpdateCard;
+    }
+
+    public UpdatePasswordCard updatePassword() {
+        return updatePasswordCard;
+    }
+
+    public class PasswordLastUpdateCard extends Card {
+        @FindBy(id = "passwordLastUpdate")
+        private WebElement cardRoot;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(cardRoot);
+        }
+
+        public String getTextDateTime() {
+            return getTextFromElement(cardRoot.findElement(By.tagName("strong")));
+        }
+
+        public LocalDateTime getDateTime() {
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d, yyyy, h:m:s a"); // e.g. Aug 31, 2018, 5:41:24 PM
+            return LocalDateTime.from(formatter.parse(getTextDateTime()));
+        }
+    }
+
+    public class UpdatePasswordCard extends Card {
+        @FindBy(id = "updatePasswordSubTitle")
+        private WebElement updatePasswordSubTitle;
+        @FindBy(id = "password")
+        private WebElement currentPassword;
+        @FindBy(id = "newPassword")
+        private WebElement newPassword;
+        @FindBy(id = "confirmation")
+        private WebElement confirmPassword;
+        @FindBy(name = "submitAction")
+        private WebElement submitBtn;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(updatePasswordSubTitle);
+        }
+
+        public void setCurrentPassword(String value) {
+            setTextInputValue(currentPassword, value);
+        }
+
+        public void setNewPassword(String value) {
+            setTextInputValue(newPassword, value);
+        }
+
+        public void setConfirmPassword(String value) {
+            setTextInputValue(confirmPassword, value);
+        }
+
+        public boolean isSaveDisabled() {
+            return submitBtn.getAttribute("disabled") != null;
+        }
+
+        public void clickSave() {
+            clickBtnAndWaitForAlert(submitBtn);
+        }
+
+        public void setPasswords(String currentPassword, String newPassword) {
+            setCurrentPassword(currentPassword);
+            setNewPassword(newPassword);
+            setConfirmPassword(newPassword);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/DeviceActivityPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/DeviceActivityPage.java
new file mode 100644
index 0000000..e5f5904
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/DeviceActivityPage.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class DeviceActivityPage extends AbstractLoggedInPage {
+    @Page
+    private SignedInDevicesCard signedInDevicesCard;
+    @Page
+    private RecentlyUsedDevicesCard recentlyUsedDevicesCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("device-activity");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && signedInDevices().isVisible() && recentlyUsedDevices().isVisible();
+    }
+
+    public SignedInDevicesCard signedInDevices() {
+        return signedInDevicesCard;
+    }
+
+    public RecentlyUsedDevicesCard recentlyUsedDevices() {
+        return recentlyUsedDevicesCard;
+    }
+
+    public class SignedInDevicesCard extends Card {
+        @FindBy(id = "signedInDevicesTitle")
+        private WebElement signedInDevicesTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(signedInDevicesTitle);
+        }
+    }
+
+    public class RecentlyUsedDevicesCard extends Card {
+        @FindBy(id = "recentlyUsedDevicesTitle")
+        private WebElement recentlyUsedDevicesTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(recentlyUsedDevicesTitle);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/Card.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/Card.java
new file mode 100644
index 0000000..b8540ed
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/Card.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2.fragment;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class Card {
+    @Drone
+    private WebDriver driver;
+
+    public abstract boolean isVisible();
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/VerticalNavBar.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/VerticalNavBar.java
new file mode 100644
index 0000000..ddcf46f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/fragment/VerticalNavBar.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2.fragment;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import static org.keycloak.testsuite.util.UIUtils.clickLink;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+// TODO rewrite this (blocked by KEYCLOAK-8217)
+public class VerticalNavBar {
+    @Root
+    private WebElement navBarRoot;
+
+    public void clickNavLinkByIndex(int i) {
+        clickLink(getNavLinkByIndex(i));
+    }
+
+    public void clickSubNavLinkByIndex(int i1, int i2) {
+        clickLink(getNavLinkByIndex(i1));
+        clickLink(getSubNavLinkByIndex(i1, i2));
+    }
+
+    public boolean isNavLinkActive(int i) {
+        return isNavLinkActive(getNavLinkByIndex(i));
+    }
+
+    public boolean isSubNavLinkActive(int i1, int i2) {
+        return isNavLinkActive(getSubNavLinkByIndex(i1, i2));
+    }
+
+    private WebElement getNavLinkByIndex(int i) {
+        return navBarRoot.findElement(By.xpath(String.format("./ul/li[%d]", i)));
+    }
+
+    private WebElement getSubNavLinkByIndex(int i1, int i2) {
+        return navBarRoot.findElement(By.xpath(String.format("./ul/li[%d]/div[contains(@class,'nav-pf-secondary-nav')]/ul/li[%d]", i1, i2)));
+    }
+
+    private boolean isNavLinkActive(WebElement navLink) {
+        return navLink.getAttribute("class").contains("active");
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/LinkedAccountsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/LinkedAccountsPage.java
new file mode 100644
index 0000000..e00bfdc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/LinkedAccountsPage.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class LinkedAccountsPage extends AbstractLoggedInPage {
+    @Page
+    private AuthorizedProvidersCard authorizedProvidersCard;
+    @Page
+    private AvailableProvidersCard availableProvidersCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("linked-accounts");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && authorizedProviders().isVisible() && availableProviders().isVisible();
+    }
+
+    public AuthorizedProvidersCard authorizedProviders() {
+        return authorizedProvidersCard;
+    }
+
+    public AvailableProvidersCard availableProviders() {
+        return availableProvidersCard;
+    }
+
+    public class AuthorizedProvidersCard extends Card {
+        @FindBy(id = "authorizedProvidersSubTitle")
+        private WebElement authorizedProvidersTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(authorizedProvidersTitle);
+        }
+    }
+
+    public class AvailableProvidersCard extends Card {
+        @FindBy(id = "identityProviderSubTitle")
+        private WebElement availableProvidersTitle;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(availableProvidersTitle);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/PersonalInfoPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/PersonalInfoPage.java
new file mode 100644
index 0000000..7f3dfc0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/PersonalInfoPage.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.clickBtnAndWaitForAlert;
+import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class PersonalInfoPage extends AbstractLoggedInPage {
+    @Page
+    private PersonalInfoCard personalInfoCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("account");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && personalInfo().isVisible();
+    }
+
+    public PersonalInfoCard personalInfo() {
+        return personalInfoCard;
+    }
+
+    public class PersonalInfoCard extends Card {
+        @FindBy(id = "personalSubTitle")
+        private WebElement personalSubTitle;
+        @FindBy(id = "username")
+        private WebElement username;
+        @FindBy(id = "email")
+        private WebElement email;
+        @FindBy(id = "firstName")
+        private WebElement firstName;
+        @FindBy(id = "lastName")
+        private WebElement lastName;
+        @FindBy(name = "submitAction")
+        private WebElement submitBtn;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(personalSubTitle);
+        }
+        
+        public boolean isUsernameDisabled() {
+            return !username.getTagName().equals("input"); // <div> for disabled
+        }
+        
+        public String getUsername() {
+            return getTextInputValue(username);
+        }
+        
+        public void setUsername(String value) {
+            setTextInputValue(username, value);
+        }
+        
+        public String getEmail() {
+            return getTextInputValue(email);
+        }
+        
+        public void setEmail(String value) {
+            setTextInputValue(email, value);
+        }
+
+        public String getFirstName() {
+            return getTextInputValue(firstName);
+        }
+
+        public void setFirstName(String value) {
+            setTextInputValue(firstName, value);
+        }
+
+        public String getLastName() {
+            return getTextInputValue(lastName);
+        }
+
+        public void setLastName(String value) {
+            setTextInputValue(lastName, value);
+        }
+
+        public boolean isSaveDisabled() {
+            return submitBtn.getAttribute("disabled") != null;
+        }
+
+        public void clickSave() {
+            clickBtnAndWaitForAlert(submitBtn);
+        }
+
+        public void setValues(UserRepresentation user) {
+            if (!isUsernameDisabled()) {setUsername(user.getUsername());}
+            setEmail(user.getEmail());
+            setFirstName(user.getFirstName());
+            setLastName(user.getLastName());
+        }
+
+        public boolean valuesEqual(UserRepresentation user) {
+            return user.getUsername().equals(getUsername())
+                    && user.getEmail().equals(getEmail())
+                    && user.getFirstName().equals(getFirstName())
+                    && user.getLastName().equals(getLastName());
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ResourcesPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ResourcesPage.java
new file mode 100644
index 0000000..4004dc8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/ResourcesPage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ResourcesPage extends AbstractLoggedInPage {
+    @Page
+    private ResourcesListCard resourcesListCard;
+
+    @Override
+    protected List<String> createHashPath() {
+        List<String> hashPath = super.createHashPath();
+        hashPath.add("my-resources");
+        return hashPath;
+    }
+
+    @Override
+    public void navigateToUsingNavBar() {
+        // TODO
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return super.isCurrent() && resourcesListCard.isVisible();
+    }
+
+    public ResourcesListCard resourcesList() {
+        return resourcesListCard;
+    }
+
+    public class ResourcesListCard extends Card {
+        @FindBy(className = "resources-list")
+        private WebElement cardRoot;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(cardRoot);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/WelcomeScreen.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/WelcomeScreen.java
new file mode 100644
index 0000000..74e01e9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account2/WelcomeScreen.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.auth.page.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account2.fragment.Card;
+import org.keycloak.testsuite.util.URLUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.UIUtils.clickLink;
+import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class WelcomeScreen extends AbstractAccountPage {
+    @Page
+    private PersonalInfoCard personalInfo;
+    @Page
+    private AccountSecurityCard accountSecurityCard;
+    @Page
+    private ApplicationsCard applicationsCard;
+    @Page
+    private MyResourcesCard myResourcesCard;
+    @FindBy(id = "welcomeMsg")
+    private WebElement welcomeMsg;
+    @FindBy(id = "signInButton")
+    private WebElement signInBtn;
+
+    @Override
+    protected List<String> createHashPath() {
+        return null;
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return URLUtils.currentUrlEquals(toString() + "#/") && isElementVisible(welcomeMsg); // the hash will be eventually added after the page is loaded
+    }
+
+    public PersonalInfoCard personalInfo() {
+        return personalInfo;
+    }
+
+    public AccountSecurityCard accountSecurityCard() {
+        return accountSecurityCard;
+    }
+
+    public ApplicationsCard applicationsCard() {
+        return applicationsCard;
+    }
+
+    public MyResourcesCard myResourcesCard() {
+        return myResourcesCard;
+    }
+
+    public void clickLoginBtn() {
+        clickLink(signInBtn);
+    }
+
+    public boolean isLoginBtnVisible() {
+        return isElementVisible(signInBtn);
+    }
+
+    public class PersonalInfoCard extends Card {
+        @FindBy(id = "personalInfoCard")
+        private WebElement personalInfoCard;
+        @FindBy(id = "personalInfoLink")
+        private WebElement personalInfoLink;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(personalInfoCard);
+        }
+
+        public void clickPersonalInfo() {
+            clickLink(personalInfoLink);
+        }
+    }
+
+    public class AccountSecurityCard extends Card {
+        @FindBy(id = "accountSecurityCard")
+        private WebElement accountSecurityCard;
+        @FindBy(id = "changePasswordLink")
+        private WebElement changePasswordLink;
+        @FindBy(id = "authenticatorLink")
+        private WebElement authenticatorLink;
+        @FindBy(id = "deviceActivityLink")
+        private WebElement deviceActivityLink;
+        @FindBy(id = "linkedAccountsLink")
+        private WebElement linkedAccountsLink;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(accountSecurityCard);
+        }
+
+        public void clickChangePassword() {
+            clickLink(changePasswordLink);
+        }
+
+        public void clickAuthenticator() {
+            clickLink(authenticatorLink);
+        }
+
+        public void clickDeviceActivity() {
+            clickLink(deviceActivityLink);
+        }
+
+        public void clickLinkedAccounts() {
+            clickLink(linkedAccountsLink);
+        }
+
+        public boolean isLinkedAccountsVisible() {
+            return isElementVisible(linkedAccountsLink);
+        }
+    }
+
+    public class ApplicationsCard extends Card {
+        @FindBy(id = "applicationsCard")
+        private WebElement applicationsCard;
+        @FindBy(id = "applicationsLink")
+        private WebElement applicationsLink;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(applicationsCard);
+        }
+
+        public void clickApplicationsLink() {
+            clickLink(applicationsLink);
+        }
+    }
+
+    public class MyResourcesCard extends Card {
+        @FindBy(id = "myResourcesCard")
+        private WebElement myResourcesCard;
+        @FindBy(id = "myResourcesLink")
+        private WebElement myResourcesLink;
+
+        @Override
+        public boolean isVisible() {
+            return isElementVisible(myResourcesCard);
+        }
+
+        public void clickMyResources() {
+            clickLink(myResourcesLink);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UIUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UIUtils.java
index 81c7b4a..c497e1a 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UIUtils.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UIUtils.java
@@ -1,7 +1,12 @@
 package org.keycloak.testsuite.util;
 
 import io.appium.java_client.android.AndroidDriver;
+import org.apache.commons.lang3.StringUtils;
+import org.keycloak.testsuite.page.AbstractPatternFlyAlert;
+import org.openqa.selenium.By;
 import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.TimeoutException;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
@@ -19,6 +24,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
 public final class UIUtils {
 
     public static final String VALUE_ATTR_NAME = "value";
+    public static final short EXPECTED_UI_LAYOUT = Short.parseShort(System.getProperty("testsuite.ui.layout")); // 0 == desktop layout, 1 == smartphone layout, 2 == tablet layout
 
     public static boolean selectContainsOption(Select select, String optionText) {
         for (WebElement option : select.getOptions()) {
@@ -56,6 +62,18 @@ public final class UIUtils {
     }
 
     /**
+     * This is as an alternative for {@link #clickLink(WebElement)} and should be used in situations where we can't use
+     * {@link WaitUtils#waitForPageToLoad()}. This is because {@link WaitUtils#waitForPageToLoad()} would wait until the
+     * alert would disappeared itself (timeout).
+     *
+     * @param button to click on
+     */
+    public static void clickBtnAndWaitForAlert(WebElement button) {
+        button.click();
+        AbstractPatternFlyAlert.waitUntilDisplayed();
+    }
+
+    /**
      * Navigates to a link directly instead of clicking on it.
      * Some browsers are sometimes having problems with clicking on links, so this should be used only in that cases,
      * i.e. only when clicking directly doesn't work
@@ -93,9 +111,13 @@ public final class UIUtils {
     public static void setTextInputValue(WebElement input, String value) {
         input.click();
         input.clear();
-        if (value != null) {
+        if (!StringUtils.isEmpty(value)) { // setting new input
             input.sendKeys(value);
         }
+        else { // just clearing the input; input.clear() may not fire all JS events so we need to let the page know that something's changed
+            input.sendKeys("a");
+            input.sendKeys(Keys.BACK_SPACE);
+        }
 
         WebDriver driver = getCurrentDriver();
         if (driver instanceof AndroidDriver) {
@@ -117,4 +139,21 @@ public final class UIUtils {
         }
         return text;
     }
+
+    /**
+     * Should be used solely with {@link org.jboss.arquillian.graphene.GrapheneElement}, i.e. all elements annotated by
+     * {@link org.openqa.selenium.support.FindBy}. CANNOT be used with elements found directly using
+     * {@link WebDriver#findElement(By)} and similar.
+     *
+     * @param element
+     * @return true if element is present and visible
+     */
+    public static boolean isElementVisible(WebElement element) {
+        try {
+            return element.isDisplayed();
+        }
+        catch (NoSuchElementException e) {
+            return false;
+        }
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
index 7d40f68..73398cf 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java
@@ -16,21 +16,25 @@
  */
 package org.keycloak.testsuite.util;
 
-import java.time.Duration;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import org.jboss.arquillian.graphene.wait.ElementBuilder;
 import org.openqa.selenium.By;
 import org.openqa.selenium.TimeoutException;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.htmlunit.HtmlUnitDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
 import org.openqa.selenium.support.ui.FluentWait;
 import org.openqa.selenium.support.ui.WebDriverWait;
 
+import java.time.Duration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import static org.jboss.arquillian.graphene.Graphene.waitGui;
 import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
-import static org.openqa.selenium.support.ui.ExpectedConditions.*;
+import static org.openqa.selenium.support.ui.ExpectedConditions.javaScriptThrowsNoExceptions;
+import static org.openqa.selenium.support.ui.ExpectedConditions.not;
+import static org.openqa.selenium.support.ui.ExpectedConditions.urlToBe;
 
 /**
  *
@@ -97,9 +101,11 @@ public final class WaitUtils {
             return; // not needed
         }
 
+        String currentUrl = null;
+
         // Ensure the URL is "stable", i.e. is not changing anymore; if it'd changing, some redirects are probably still in progress
-        for (int maxRedirects = 2; maxRedirects > 0; maxRedirects--) {
-            String currentUrl = driver.getCurrentUrl();
+        for (int maxRedirects = 4; maxRedirects > 0; maxRedirects--) {
+            currentUrl = driver.getCurrentUrl();
             FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(Duration.ofMillis(250));
             try {
                 wait.until(not(urlToBe(currentUrl)));
@@ -113,19 +119,34 @@ public final class WaitUtils {
         }
 
         WebDriverWait wait = new WebDriverWait(getCurrentDriver(), PAGELOAD_TIMEOUT_MILLIS / 1000);
+        ExpectedCondition waitCondition = null;
 
-        try {
-            // Checks if the document is ready and asks AngularJS, if present, whether there are any REST API requests
-            // in progress
-            wait.until(javaScriptThrowsNoExceptions(
+        // Different wait strategies for Admin and Account Consoles
+        if (currentUrl.matches("^[^\\/]+:\\/\\/[^\\/]+\\/auth\\/admin\\/.*$")) { // Admin Console
+            // Checks if the document is ready and asks AngularJS, if present, whether there are any REST API requests in progress
+            waitCondition = javaScriptThrowsNoExceptions(
                     "if (document.readyState !== 'complete' "
                     + "|| (typeof angular !== 'undefined' && angular.element(document.body).injector().get('$http').pendingRequests.length !== 0)) {"
                     + "throw \"Not ready\";"
-                    + "}"));
-        } catch (TimeoutException e) {
-            // Sometimes, for no obvious reason, the browser/JS doesn't set document.readyState to 'complete' correctly
-            // but that's no reason to let the test fail; after the timeout the page is surely fully loaded
-            log.warn("waitForPageToLoad time exceeded!");
+                    + "}");
+        }
+        else if (
+                currentUrl.matches("^[^\\/]+:\\/\\/[^\\/]+\\/auth\\/realms\\/[^\\/]+\\/account\\/.*$") // check for Account Console URL
+                && driver.getPageSource().contains("patternfly-ng") // check for new Account Console (don't use this strategy with the old one)
+        ) {
+            waitCondition = javaScriptThrowsNoExceptions(
+                    "if (!window.getAngularTestability(document.querySelector('app-root')).isStable()) {" +
+                        "throw 'Not ready';" +
+                    "}"
+            );
+        }
+
+        if (waitCondition != null) {
+            try {
+                wait.until(waitCondition);
+            } catch (TimeoutException e) {
+                log.warn("waitForPageToLoad time exceeded!");
+            }
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
index ee8ab62..d488c78 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
@@ -137,7 +137,7 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
         deleteAllSessionsInRealm(testRealmAccountPage.getAuthRealm());
     }
 
-    private void resetTestRealmSession() {
+    protected void resetTestRealmSession() {
         resetRealmSession(testRealmAccountPage.getAuthRealm());
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractAccountManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractAccountManagementTest.java
index 9eda4a4..fe1b9da 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractAccountManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractAccountManagementTest.java
@@ -21,8 +21,7 @@ import org.jboss.arquillian.graphene.page.Page;
 import org.junit.Before;
 import org.keycloak.testsuite.AbstractAuthTest;
 import org.keycloak.testsuite.auth.page.account.AccountManagement;
-import org.keycloak.testsuite.auth.page.account.fragment.AccountManagementAlert;
-import org.openqa.selenium.support.FindBy;
+import org.keycloak.testsuite.auth.page.account.fragment.AccountManagementPatternFlyAlert;
 
 import static org.junit.Assert.assertTrue;
 import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
@@ -36,8 +35,8 @@ public abstract class AbstractAccountManagementTest extends AbstractAuthTest {
     @Page
     protected AccountManagement testRealmAccountManagementPage;
 
-    @FindBy(className = "alert")
-    protected AccountManagementAlert alert;
+    @Page
+    protected AccountManagementPatternFlyAlert alert;
 
     @Override
     public void setDefaultPageUriParameters() {
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/AbstractUiTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/AbstractUiTest.java
index 389a6d5..93d847d 100644
--- a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/AbstractUiTest.java
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/AbstractUiTest.java
@@ -17,16 +17,16 @@
 
 package org.keycloak.testsuite.ui;
 
+import org.junit.Before;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractAuthTest;
 
 import java.util.Arrays;
-import java.util.List;
+import java.util.HashMap;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
-
 /**
  * @author Vaclav Muzikar <vmuzikar@redhat.com>
  */
@@ -34,14 +34,9 @@ public abstract class AbstractUiTest extends AbstractAuthTest {
     public static final String LOCALIZED_THEME = "localized-theme";
     public static final String CUSTOM_LOCALE_NAME = "Přísný jazyk";
 
-    @Override
-    public void addTestRealms(List<RealmRepresentation> testRealms) {
-        RealmRepresentation testRealmRep = new RealmRepresentation();
-        testRealmRep.setId(TEST);
-        testRealmRep.setRealm(TEST);
-        testRealmRep.setEnabled(true);
-        configureInternationalizationForRealm(testRealmRep);
-        testRealms.add(testRealmRep);
+    @Before
+    public void addTestUser() {
+        createTestUserWithAdminClient(false);
     }
 
     protected void configureInternationalizationForRealm(RealmRepresentation realm) {
@@ -58,4 +53,12 @@ public abstract class AbstractUiTest extends AbstractAuthTest {
         realm.setAccountTheme(LOCALIZED_THEME);
         realm.setEmailTheme(LOCALIZED_THEME);
     }
+
+    protected IdentityProviderRepresentation createIdentityProviderRepresentation(String alias, String providerId) {
+        IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation();
+        idpRep.setProviderId(providerId);
+        idpRep.setAlias(alias);
+        idpRep.setConfig(new HashMap<>());
+        return idpRep;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/AbstractAccountTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/AbstractAccountTest.java
new file mode 100644
index 0000000..2006872
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/AbstractAccountTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.keycloak.common.Profile;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.auth.page.account2.WelcomeScreen;
+import org.keycloak.testsuite.ui.AbstractUiTest;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class AbstractAccountTest extends AbstractUiTest {
+    public static final String ACCOUNT_THEME_NAME = "keycloak-preview";
+
+    @Page
+    protected WelcomeScreen accountWelcomeScreen;
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+        RealmRepresentation testRealmRep = testRealms.get(0);
+        testRealmRep.setAccountTheme(ACCOUNT_THEME_NAME);
+    }
+
+    @BeforeClass
+    public static void enabled() {
+        ProfileAssume.assumeFeatureEnabled(Profile.Feature.ACCOUNT2);
+    }
+
+    @Before
+    public void beforeAccountTest() {
+        accountWelcomeScreen.navigateTo();
+    }
+
+    protected void loginToAccount() {
+        assertCurrentUrlStartsWithLoginUrlOf(accountWelcomeScreen);
+        loginPage.form().login(testUser);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/BaseAccountPageTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/BaseAccountPageTest.java
new file mode 100644
index 0000000..a83f558
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/BaseAccountPageTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.account2;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class BaseAccountPageTest extends AbstractAccountTest {
+    protected abstract AbstractLoggedInPage getAccountPage();
+
+    @Before
+    public void beforeBaseAccountPageTest() {
+        getAccountPage().navigateTo();
+        loginToAccount();
+        getAccountPage().assertCurrent();
+    }
+
+    @Test
+    @Ignore // TODO, blocked by KEYCLOAK-8217
+    public void navigationTest() {
+        getAccountPage().navigateToUsingNavBar();
+        getAccountPage().assertCurrent();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/ChangePasswordTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/ChangePasswordTest.java
new file mode 100644
index 0000000..801d4ed
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/ChangePasswordTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
+import org.keycloak.testsuite.auth.page.account2.ChangePasswordPage;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ChangePasswordTest extends BaseAccountPageTest {
+    @Page
+    private ChangePasswordPage changePasswordPage;
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+        RealmRepresentation realm = testRealms.get(0);
+        realm.setPasswordPolicy("length(3)");
+    }
+
+    @Override
+    protected AbstractLoggedInPage getAccountPage() {
+        return changePasswordPage;
+    }
+
+    @Test
+    public void changePassword() {
+        final LocalDateTime testStartTime = LocalDateTime.now();
+
+        final String oldPwd = getPasswordOf(testUser);
+        final String newPwd = "nějaké nové heslo s háčky a čárkami";
+        setPasswordFor(testUser, newPwd);
+
+        assertTrue("The current password should be older than the start time of this test",
+                testStartTime.isAfter(changePasswordPage.passwordLastUpdate().getDateTime()));
+
+        changePasswordPage.updatePassword().setPasswords(oldPwd, newPwd);
+        changePasswordPage.updatePassword().clickSave();
+        changePasswordPage.alert().assertSuccess();
+
+        // try the new password
+        deleteAllSessionsInTestRealm(); // logout
+        changePasswordPage.navigateTo();
+        loginToAccount();
+        changePasswordPage.assertCurrent();
+
+        assertTrue("The new password should be newer than the start time of this test",
+                testStartTime.isBefore(changePasswordPage.passwordLastUpdate().getDateTime()));
+    }
+
+    @Test
+    public void formValidationTest() {
+        assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
+        changePasswordPage.updatePassword().setPasswords("abc", "def");
+        assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
+
+        // clear current password
+        changePasswordPage.updatePassword().setCurrentPassword("");
+        assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
+        changePasswordPage.updatePassword().setCurrentPassword("abc");
+        assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
+
+        // clear new password
+        changePasswordPage.updatePassword().setNewPassword("");
+        assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
+        changePasswordPage.updatePassword().setNewPassword("def");
+        assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
+
+        // clear confirm password
+        changePasswordPage.updatePassword().setConfirmPassword("");
+        assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
+        changePasswordPage.updatePassword().setConfirmPassword("def");
+        assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
+
+        // invalid current password
+        changePasswordPage.updatePassword().setPasswords("invalid", "ab");
+        changePasswordPage.updatePassword().clickSave();
+        changePasswordPage.alert().assertDanger("Invalid existing password.");
+
+        // non-matching passwords
+        changePasswordPage.updatePassword().setPasswords(getPasswordOf(testUser), "ab");
+        changePasswordPage.updatePassword().setConfirmPassword("no match");
+        changePasswordPage.updatePassword().clickSave();
+        changePasswordPage.alert().assertDanger("Passwords don't match.");
+
+        // password policy
+        changePasswordPage.updatePassword().setPasswords(getPasswordOf(testUser), "ab");
+        changePasswordPage.updatePassword().clickSave();
+        changePasswordPage.alert().assertDanger("Invalid password: minimum length 3.");
+
+        // check the password is not changed
+        deleteAllSessionsInTestRealm();
+        changePasswordPage.navigateTo();
+        loginToAccount();
+        changePasswordPage.assertCurrent();
+    }
+
+    // TODO test the last update timestamp when the password was never updated (blocked by KEYCLOAK-8193)
+    // TODO test internationalization for last update timestamp (blocked by KEYCLOAK-8194)
+}
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/PersonalInfoTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/PersonalInfoTest.java
new file mode 100644
index 0000000..d40baa2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/PersonalInfoTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
+import org.keycloak.testsuite.auth.page.account2.PersonalInfoPage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class PersonalInfoTest extends BaseAccountPageTest {
+    private UserRepresentation testUser2;
+    @Page
+    private PersonalInfoPage personalInfoPage;
+
+    @Before
+    public void setTestUser() {
+        testUser2 = new UserRepresentation();
+        testUser2.setUsername("vmuzikar");
+        testUser2.setEmail("vmuzikar@redhat.com");
+        testUser2.setFirstName("Václav");
+        testUser2.setLastName("Muzikář");
+        ApiUtil.removeUserByUsername(testRealmResource(), testUser2.getUsername());
+    }
+
+    @Override
+    protected AbstractLoggedInPage getAccountPage() {
+        return personalInfoPage;
+    }
+
+    @Test
+    public void updateUserInfo() {
+        setEditUsernameAllowed(true);
+
+        assertTrue(personalInfoPage.personalInfo().valuesEqual(testUser));
+        assertFalse(personalInfoPage.personalInfo().isUsernameDisabled());
+        assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
+
+        personalInfoPage.personalInfo().setValues(testUser2);
+        assertTrue(personalInfoPage.personalInfo().valuesEqual(testUser2));
+        assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
+        personalInfoPage.personalInfo().clickSave();
+        personalInfoPage.alert().assertSuccess();
+
+        personalInfoPage.navigateTo();
+        personalInfoPage.personalInfo().valuesEqual(testUser2);
+
+        // change just first and last name
+        testUser2.setFirstName("Another");
+        testUser2.setLastName("Name");
+        personalInfoPage.personalInfo().setValues(testUser2);
+        personalInfoPage.personalInfo().clickSave();
+        personalInfoPage.alert().assertSuccess();
+        personalInfoPage.navigateTo();
+        personalInfoPage.personalInfo().valuesEqual(testUser2);
+    }
+
+    @Test
+    public void formValidationTest() {
+        setEditUsernameAllowed(true);
+        
+        // clear username
+        personalInfoPage.personalInfo().setUsername("");
+        assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
+        personalInfoPage.personalInfo().setUsername("abc");
+        assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
+
+        // clear email
+        personalInfoPage.personalInfo().setEmail("");
+        assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
+        personalInfoPage.personalInfo().setEmail("vmuzikar@redhat.com");
+        assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
+
+        // TODO test email validation (blocked by KEYCLOAK-8098)
+
+        // clear first name
+        personalInfoPage.personalInfo().setFirstName("");
+        assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
+        personalInfoPage.personalInfo().setFirstName("abc");
+        assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
+
+        // clear last name
+        personalInfoPage.personalInfo().setLastName("");
+        assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
+        personalInfoPage.personalInfo().setLastName("abc");
+        assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
+
+        // duplicity tests
+        ApiUtil.createUserWithAdminClient(testRealmResource(), testUser2);
+        // duplicate username
+        personalInfoPage.personalInfo().setUsername(testUser2.getUsername());
+        personalInfoPage.personalInfo().clickSave();
+        personalInfoPage.alert().assertDanger("Username already exists.");
+        personalInfoPage.personalInfo().setUsername(testUser.getUsername());
+        // duplicate email
+        personalInfoPage.personalInfo().setEmail(testUser2.getEmail());
+        personalInfoPage.personalInfo().clickSave();
+        personalInfoPage.alert().assertDanger("Email already exists.");
+        // check no changes were saved
+        personalInfoPage.navigateTo();
+        personalInfoPage.personalInfo().valuesEqual(testUser);
+    }
+
+    @Test
+    public void disabledEditUsername() {
+        setEditUsernameAllowed(false);
+
+        assertTrue(personalInfoPage.personalInfo().isUsernameDisabled());
+        personalInfoPage.personalInfo().setValues(testUser2);
+        personalInfoPage.personalInfo().clickSave();
+        personalInfoPage.alert().assertSuccess();
+
+        testUser2.setUsername(testUser.getUsername()); // the username should remain the same
+        personalInfoPage.navigateTo();
+        personalInfoPage.personalInfo().valuesEqual(testUser2);
+    }
+
+    private void setEditUsernameAllowed(boolean value) {
+        RealmRepresentation realm = testRealmResource().toRepresentation();
+        realm.setEditUsernameAllowed(value);
+        testRealmResource().update(realm);
+        personalInfoPage.navigateTo();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/WelcomeScreenTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/WelcomeScreenTest.java
new file mode 100644
index 0000000..dedef72
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/account2/WelcomeScreenTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.account2;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account2.ApplicationsPage;
+import org.keycloak.testsuite.auth.page.account2.AuthenticatorPage;
+import org.keycloak.testsuite.auth.page.account2.ChangePasswordPage;
+import org.keycloak.testsuite.auth.page.account2.DeviceActivityPage;
+import org.keycloak.testsuite.auth.page.account2.LinkedAccountsPage;
+import org.keycloak.testsuite.auth.page.account2.PersonalInfoPage;
+import org.keycloak.testsuite.auth.page.account2.ResourcesPage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class WelcomeScreenTest extends AbstractAccountTest {
+    @Page
+    private PersonalInfoPage personalInfoPage;
+    @Page
+    private ChangePasswordPage changePasswordPage;
+    @Page
+    private AuthenticatorPage authenticatorPage;
+    @Page
+    private DeviceActivityPage deviceActivityPage;
+    @Page
+    private LinkedAccountsPage linkedAccountsPage;
+    @Page
+    private ApplicationsPage applicationsPage;
+    @Page
+    private ResourcesPage resourcesPage;
+
+    @Test
+    public void loginTest() {
+        accountWelcomeScreen.assertCurrent();
+        assertTrue(accountWelcomeScreen.isLoginBtnVisible());
+
+        // login
+        accountWelcomeScreen.clickLoginBtn();
+        loginToAccount();
+        accountWelcomeScreen.assertCurrent();
+        assertFalse(accountWelcomeScreen.isLoginBtnVisible());
+
+        // TODO logout test (blocked by KEYCLOAK-8084)
+    }
+
+    @Test
+    public void personalInfoTest() {
+        assertTrue(accountWelcomeScreen.personalInfo().isVisible());
+        accountWelcomeScreen.personalInfo().clickPersonalInfo();
+        loginToAccount();
+        personalInfoPage.assertCurrent();
+    }
+
+    @Test
+    public void accountSecurityTest() {
+        assertTrue(accountWelcomeScreen.accountSecurityCard().isVisible());
+
+        // change password link
+        accountWelcomeScreen.accountSecurityCard().clickChangePassword();
+        loginToAccount();
+        changePasswordPage.assertCurrent();
+
+        // authenticator link
+        accountWelcomeScreen.navigateTo();
+        accountWelcomeScreen.accountSecurityCard().clickAuthenticator();
+        authenticatorPage.assertCurrent();
+
+        // device activity link
+        accountWelcomeScreen.navigateTo();
+        accountWelcomeScreen.accountSecurityCard().clickDeviceActivity();
+        deviceActivityPage.assertCurrent();
+
+        // linked accounts link
+        accountWelcomeScreen.navigateTo();
+        assertFalse(accountWelcomeScreen.accountSecurityCard().isLinkedAccountsVisible());
+        // add simple IdP
+        testRealmResource().identityProviders().create(createIdentityProviderRepresentation("test-idp", "test-provider"));
+        // test link appeared
+        accountWelcomeScreen.navigateTo();
+        accountWelcomeScreen.accountSecurityCard().clickLinkedAccounts();
+        linkedAccountsPage.assertCurrent();
+        // no need to remove the IdP
+    }
+
+    @Test
+    public void applicationsTest() {
+        assertTrue(accountWelcomeScreen.applicationsCard().isVisible());
+        accountWelcomeScreen.applicationsCard().clickApplicationsLink();
+        loginToAccount();
+        applicationsPage.assertCurrent();
+    }
+
+    @Test
+    public void resourcesTest() {
+        assertFalse(accountWelcomeScreen.myResourcesCard().isVisible());
+
+        // set user managed access
+        RealmRepresentation testRealm = testRealmResource().toRepresentation();
+        testRealm.setUserManagedAccessAllowed(true);
+        testRealmResource().update(testRealm);
+
+        // test my resources appeared
+        accountWelcomeScreen.navigateTo();
+        assertTrue(accountWelcomeScreen.myResourcesCard().isVisible());
+        accountWelcomeScreen.myResourcesCard().clickMyResources();
+        loginToAccount();
+        resourcesPage.assertCurrent();
+        // no need to disable user managed access
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/login/AbstractLoginTest.java b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/login/AbstractLoginTest.java
index e9c673e..9ff8bbc 100644
--- a/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/login/AbstractLoginTest.java
+++ b/testsuite/integration-arquillian/tests/other/base-ui/src/test/java/org/keycloak/testsuite/ui/login/AbstractLoginTest.java
@@ -17,9 +17,11 @@
 
 package org.keycloak.testsuite.ui.login;
 
-import org.junit.Before;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.ui.AbstractUiTest;
 
+import java.util.List;
+
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
@@ -29,9 +31,11 @@ import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
  * @author Vaclav Muzikar <vmuzikar@redhat.com>
  */
 public abstract class AbstractLoginTest extends AbstractUiTest {
-    @Before
-    public void addTestUser() {
-        createTestUserWithAdminClient(false);
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+        RealmRepresentation testRealmRep = testRealms.get(0);
+        configureInternationalizationForRealm(testRealmRep);
     }
 
     protected void assertLoginFailed(String message) {
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java
index d01e4a5..183e91d 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java
@@ -26,8 +26,8 @@ import org.keycloak.testsuite.console.page.AdminConsole;
 import org.keycloak.testsuite.console.page.AdminConsoleRealm;
 import org.keycloak.testsuite.console.page.AdminConsoleRealm.ConfigureMenu;
 import org.keycloak.testsuite.console.page.AdminConsoleRealm.ManageMenu;
-import org.keycloak.testsuite.console.page.fragment.AdminConsoleAlert;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import org.keycloak.testsuite.page.PatternFlyClosableAlert;
 import org.openqa.selenium.support.FindBy;
 
 import static org.junit.Assert.assertTrue;
@@ -53,8 +53,8 @@ public abstract class AbstractConsoleTest extends AbstractAuthTest {
     @FindBy(xpath = "//div[@class='modal-dialog']")
     protected ModalDialog modalDialog;
 
-    @FindBy(className = "alert")
-    protected AdminConsoleAlert alert;
+    @Page
+    protected PatternFlyClosableAlert alert;
 
     protected boolean adminLoggedIn = false;
 
@@ -111,13 +111,11 @@ public abstract class AbstractConsoleTest extends AbstractAuthTest {
     }
 
     public void assertAlertSuccess() {
-        assertTrue("Alert is not success", alert.isSuccess());
-        alert.close();
+        alert.assertSuccess();
     }
 
     public void assertAlertDanger() {
-        assertTrue("Alert is not danger", alert.isDanger());
-        alert.close();
+        alert.assertDanger();
     }
 
     public ConfigureMenu configure() {
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 4f03c09..9793ade 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -167,6 +167,7 @@
         <backends.console.output>true</backends.console.output>
 
         <testsuite.constants>${project.build.directory}/dependency/test-constants.properties</testsuite.constants>
+        <testsuite.ui.layout>0</testsuite.ui.layout> <!-- this is to tell the UI tests what type of layout to expect (doesn't setup the layout, just what to expect); 0 == desktop layout, 1 == smartphone layout, 2 == tablet layout -->
 
         <skip.add.user.json>false</skip.add.user.json>
         <skip.clean.second.cache>true</skip.clean.second.cache>
@@ -461,6 +462,7 @@
                             <kie.maven.settings>${kie.maven.settings}</kie.maven.settings>
 
                             <testsuite.constants>${testsuite.constants}</testsuite.constants>
+                            <testsuite.ui.layout>${testsuite.ui.layout}</testsuite.ui.layout>
                             <cli.log.output>${cli.log.output}</cli.log.output>
                             <test.intermittent>${test.intermittent}</test.intermittent>
 
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
index e094738..7fd1897 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
+++ b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
@@ -194,7 +194,7 @@
                               <h2>${msg("accountSecurityTitle")}</h2>
                               <p class="card-pf-content-intro">${msg("accountSecurityIntroMessage")}</p>
                               <h3 id="changePasswordLink"><a href="${baseUrl}/#/password">${msg("changePasswordHtmlTitle")}</a></h3>
-                              <h3 id="authenticatiorLink"><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
+                              <h3 id="authenticatorLink"><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
                               <h3 id="deviceActivityLink"><a href="${baseUrl}/#/device-activity">${msg("deviceActivityHtmlTitle")}</a></h3>
                               <h3 id="linkedAccountsLink"><a href="${baseUrl}/#/linked-accounts">${msg("linkedAccountsHtmlTitle")}</a></h3>
                             </div>