diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java b/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java
index b115efa69f3..13d8205e847 100644
--- a/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java
+++ b/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java
@@ -240,6 +240,11 @@ public class RealmConfigBuilder {
return this;
}
+ public RealmConfigBuilder resetPasswordAllowed(boolean allowed) {
+ rep.setResetPasswordAllowed(allowed);
+ return this;
+ }
+
public RealmConfigBuilder clientPolicy(ClientPolicyRepresentation clienPolicyRep) {
ClientPoliciesRepresentation clientPolicies = rep.getParsedClientPolicies();
if (clientPolicies == null) {
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java
new file mode 100644
index 00000000000..cc7a2ad5f37
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/AbstractLoginPage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testframework.ui.page;
+
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public abstract class AbstractLoginPage extends AbstractPage {
+
+ @FindBy(xpath = "//select[@aria-label='languages']/option[@selected]")
+ private WebElement selectedLanguage;
+
+ @FindBy(xpath = "//select[@aria-label='languages']")
+ private WebElement languages;
+
+ @FindBy(id = "kc-current-locale-link")
+ private WebElement languageTextBase; // base theme
+
+ @FindBy(id = "kc-locale-dropdown")
+ private WebElement localeDropdownBase; // base theme
+
+ public AbstractLoginPage(ManagedWebDriver driver) {
+ super(driver);
+ }
+
+ public String getSelectedLanguage() {
+ try {
+ final String text = selectedLanguage.getText();
+ return text == null ? text : text.trim();
+ } catch (NoSuchElementException ex) {
+ // Fallback for Login v1
+ return languageTextBase.getText();
+ }
+ }
+
+ public void selectLanguage(String language){
+ try {
+ WebElement langLink = languages.findElement(By.xpath("//option[text()[contains(.,'" + language + "')]]"));
+ langLink.click();
+ } catch (NoSuchElementException ex) {
+ // Fallback for Login v1
+ WebElement langLink = localeDropdownBase.findElement(By.xpath("//a[text()[contains(.,'" + language + "')]]"));
+ langLink.click();
+ }
+ }
+
+}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/ErrorPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/ErrorPage.java
index af584a8b185..1fc5f9ff460 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/ErrorPage.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/ErrorPage.java
@@ -24,7 +24,7 @@ import org.openqa.selenium.support.FindBy;
/**
* @author Stian Thorgersen
*/
-public class ErrorPage extends AbstractPage {
+public class ErrorPage extends AbstractLoginPage {
@FindBy(className = "instruction")
private WebElement errorMessage;
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/InfoPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/InfoPage.java
index d60e6187e49..2bde860c1f7 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/InfoPage.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/InfoPage.java
@@ -25,7 +25,7 @@ import org.openqa.selenium.support.FindBy;
/**
* @author Stian Thorgersen
*/
-public class InfoPage extends AbstractPage {
+public class InfoPage extends AbstractLoginPage {
@FindBy(className = "instruction")
private WebElement infoMessage;
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginExpiredPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginExpiredPage.java
new file mode 100644
index 00000000000..2c4fa4a3111
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginExpiredPage.java
@@ -0,0 +1,33 @@
+package org.keycloak.testframework.ui.page;
+
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class LoginExpiredPage extends AbstractLoginPage {
+
+ public LoginExpiredPage(ManagedWebDriver driver) {
+ super(driver);
+ }
+
+ @FindBy(id = "loginRestartLink")
+ private WebElement loginRestartLink;
+
+ @FindBy(id = "loginContinueLink")
+ private WebElement loginContinueLink;
+
+
+ public void clickLoginRestartLink() {
+ loginRestartLink.click();
+ }
+
+ public void clickLoginContinueLink() {
+ loginContinueLink.click();
+ }
+
+ @Override
+ public String getExpectedPageId() {
+ return "login-login-page-expired";
+ }
+}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java
index 76e26da948b..532c686a4a6 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPage.java
@@ -3,10 +3,11 @@ package org.keycloak.testframework.ui.page;
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
-public class LoginPage extends AbstractPage {
+public class LoginPage extends AbstractLoginPage {
@FindBy(id = "username")
private WebElement usernameInput;
@@ -20,12 +21,23 @@ public class LoginPage extends AbstractPage {
@FindBy(id = "rememberMe")
private WebElement rememberMe;
+ @FindBy(linkText = "Forgot Password?")
+ private WebElement resetPasswordLink;
+
+ @FindBy(className = "pf-m-success")
+ private WebElement loginSuccessMessage;
+
+ @FindBy(id = "input-error-username")
+ private WebElement userNameInputError;
+
public LoginPage(ManagedWebDriver driver) {
super(driver);
}
public void fillLogin(String username, String password) {
+ usernameInput.clear();
usernameInput.sendKeys(username);
+ passwordInput.clear();
passwordInput.sendKeys(password);
}
@@ -54,6 +66,14 @@ public class LoginPage extends AbstractPage {
return rememberMe.isSelected();
}
+ public void resetPassword() {
+ resetPasswordLink.click();
+ }
+
+ public String getSuccessMessage() {
+ return loginSuccessMessage != null ? loginSuccessMessage.getText() : null;
+ }
+
@Override
public String getExpectedPageId() {
return "login-login";
@@ -66,4 +86,13 @@ public class LoginPage extends AbstractPage {
public void clearUsernameInput() {
usernameInput.clear();
}
+
+ public String getUsernameInputError() {
+ try {
+ return userNameInputError.getText();
+ } catch (NoSuchElementException e) {
+ return null;
+ }
+ }
+
}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java
new file mode 100644
index 00000000000..380bf14db4e
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordResetPage.java
@@ -0,0 +1,39 @@
+package org.keycloak.testframework.ui.page;
+
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class LoginPasswordResetPage extends AbstractLoginPage {
+
+ @FindBy(id = "username")
+ private WebElement usernameInput;
+
+ @FindBy(css = "[type=\"submit\"]")
+ private WebElement submitButton;
+
+ @FindBy(id = "kc-reset-password-form")
+ private WebElement formResetPassword;
+
+ public LoginPasswordResetPage(ManagedWebDriver driver) {
+ super(driver);
+ }
+
+ public void changePassword(String username) {
+ usernameInput.clear();
+ usernameInput.sendKeys(username);
+
+ submitButton.click();
+ }
+
+ public String getFormUrl() {
+ return formResetPassword.getAttribute("action");
+ }
+
+ @Override
+ public String getExpectedPageId() {
+ return "login-login-reset-password";
+ }
+
+}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java
index 63d44c857db..73531c0edac 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/LoginPasswordUpdatePage.java
@@ -24,7 +24,7 @@ import org.openqa.selenium.support.FindBy;
/**
* @author Stian Thorgersen
*/
-public class LoginPasswordUpdatePage extends AbstractPage {
+public class LoginPasswordUpdatePage extends AbstractLoginPage {
@FindBy(id = "password-new")
private WebElement newPasswordInput;
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/OAuthGrantPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/OAuthGrantPage.java
new file mode 100644
index 00000000000..b2d17200c3e
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/OAuthGrantPage.java
@@ -0,0 +1,63 @@
+package org.keycloak.testframework.ui.page;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+
+import org.junit.jupiter.api.Assertions;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class OAuthGrantPage extends AbstractLoginPage {
+
+ // Locale-resolved built-in client scope consents
+ public static final String PROFILE_CONSENT_TEXT = "User profile";
+ public static final String EMAIL_CONSENT_TEXT = "Email address";
+ public static final String ADDRESS_CONSENT_TEXT = "Address";
+ public static final String PHONE_CONSENT_TEXT = "Phone number";
+ public static final String OFFLINE_ACCESS_CONSENT_TEXT = "Offline Access";
+ public static final String ROLES_CONSENT_TEXT = "User roles";
+
+ @FindBy(css = "[name=\"accept\"]")
+ private WebElement acceptButton;
+ @FindBy(css = "[name=\"cancel\"]")
+ private WebElement cancelButton;
+
+ public OAuthGrantPage(ManagedWebDriver driver) {
+ super(driver);
+ }
+
+ public void accept(){
+ acceptButton.click();
+ }
+
+ public void cancel(){
+ cancelButton.click();
+ }
+
+ public List getDisplayedGrants() {
+ List table = new ArrayList<>();
+ WebElement divKcOauth = driver.findElement(By.id("kc-oauth"));
+ for (WebElement li : divKcOauth.findElements(By.tagName("li"))) {
+ WebElement span = li.findElement(By.tagName("span"));
+ table.add(span.getText());
+ }
+ return table;
+ }
+
+ public void assertGrants(String... expectedGrants) {
+ List displayed = getDisplayedGrants();
+ List expected = Arrays.asList(expectedGrants);
+ Assertions.assertTrue(displayed.containsAll(expected) && expected.containsAll(displayed),
+ "Not matched grants. Displayed grants: " + displayed + ", expected grants: " + expected);
+ }
+
+ @Override
+ public String getExpectedPageId() {
+ return "login-login-oauth-grant";
+ }
+
+}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/TermsAndConditionsPage.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/TermsAndConditionsPage.java
new file mode 100644
index 00000000000..e47fd4e5341
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/page/TermsAndConditionsPage.java
@@ -0,0 +1,31 @@
+package org.keycloak.testframework.ui.page;
+
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class TermsAndConditionsPage extends AbstractLoginPage {
+
+ @FindBy(id = "kc-accept")
+ private WebElement submitButton;
+
+ @FindBy(id = "kc-decline")
+ private WebElement cancelButton;
+
+ public TermsAndConditionsPage(ManagedWebDriver driver) {
+ super(driver);
+ }
+
+ public void acceptTerms() {
+ submitButton.click();
+ }
+ public void declineTerms() {
+ cancelButton.click();
+ }
+
+ @Override
+ public String getExpectedPageId() {
+ return "login-terms";
+ }
+}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/AssertionUtils.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/AssertionUtils.java
index 747431a9f9f..401027b2b9d 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/AssertionUtils.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/AssertionUtils.java
@@ -1,6 +1,7 @@
package org.keycloak.testframework.ui.webdriver;
import org.junit.jupiter.api.Assertions;
+import org.openqa.selenium.By;
public class AssertionUtils {
@@ -11,7 +12,8 @@ public class AssertionUtils {
}
public void assertTitle(String title) {
- Assertions.assertEquals(title, managed.page().getTitle());
+ String kcPageTitle = managed.findElement(By.id("kc-page-title")).getText();
+ Assertions.assertEquals(title, kcPageTitle);
}
}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/CookieUtils.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/CookieUtils.java
index 3e2260221de..8065e2ea019 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/CookieUtils.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/CookieUtils.java
@@ -1,5 +1,9 @@
package org.keycloak.testframework.ui.webdriver;
+import java.util.Set;
+
+import org.keycloak.cookie.CookieType;
+
import org.openqa.selenium.Cookie;
public class CookieUtils {
@@ -14,6 +18,18 @@ public class CookieUtils {
managed.driver().manage().addCookie(cookie);
}
+ public Cookie get(CookieType cookieType) {
+ return managed.driver().manage().getCookieNamed(cookieType.getName());
+ }
+
+ public Set getAll() {
+ return managed.driver().manage().getCookies();
+ }
+
+ public Cookie get(String name) {
+ return managed.driver().manage().getCookieNamed(name);
+ }
+
public void deleteAll() {
managed.driver().manage().deleteAllCookies();
}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java
index 2f9e07e0f92..d48f824a4c3 100644
--- a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/ManagedWebDriver.java
@@ -16,6 +16,7 @@ public class ManagedWebDriver {
private AssertionUtils assertionUtils = new AssertionUtils(this);
private CookieUtils cookieUtils = new CookieUtils(this);
private PageUtils pageUtils = new PageUtils(this);
+ private NavigateUtils navigateUtils = new NavigateUtils(this);
private WaitUtils waitUtils = new WaitUtils(this);
public ManagedWebDriver(WebDriver driver) {
@@ -65,6 +66,10 @@ public class ManagedWebDriver {
return pageUtils;
}
+ public NavigateUtils navigate() {
+ return navigateUtils;
+ }
+
public WaitUtils waiting() {
return waitUtils;
}
diff --git a/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java
new file mode 100644
index 00000000000..1aa69ac5c0d
--- /dev/null
+++ b/test-framework/ui/src/main/java/org/keycloak/testframework/ui/webdriver/NavigateUtils.java
@@ -0,0 +1,28 @@
+package org.keycloak.testframework.ui.webdriver;
+
+import org.keycloak.testframework.ui.page.AbstractPage;
+
+public class NavigateUtils {
+
+ private final ManagedWebDriver driver;
+
+ NavigateUtils(ManagedWebDriver driver) {
+ this.driver = driver;
+ }
+
+ public void refresh() {
+ driver.driver().navigate().refresh();
+ }
+
+ public void backWithRefresh(AbstractPage expectedPage) {
+ driver.driver().navigate().back();
+
+ String currentPageId = driver.page().getCurrentPageId();
+ if (!expectedPage.getExpectedPageId().equals(currentPageId) && driver.getBrowserType().equals(BrowserType.CHROME)) {
+ driver.driver().navigate().refresh();
+ }
+
+ expectedPage.assertCurrent();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java b/tests/base/src/test/java/org/keycloak/tests/i18n/EmailTest.java
old mode 100755
new mode 100644
similarity index 60%
rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
rename to tests/base/src/test/java/org/keycloak/tests/i18n/EmailTest.java
index 94e4010f06f..dab9e3ff078
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/i18n/EmailTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.keycloak.testsuite.i18n;
+package org.keycloak.tests.i18n;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -29,20 +29,34 @@ import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.ws.rs.core.HttpHeaders;
+import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.http.simple.SimpleHttp;
import org.keycloak.http.simple.SimpleHttpResponse;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.testsuite.admin.ApiUtil;
-import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
-import org.keycloak.testsuite.pages.InfoPage;
-import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.pages.LoginPasswordResetPage;
-import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
-import org.keycloak.testsuite.util.DroneUtils;
-import org.keycloak.testsuite.util.GreenMailRule;
-import org.keycloak.testsuite.util.MailUtils;
-import org.keycloak.testsuite.util.WaitUtils;
+import org.keycloak.testframework.annotations.InjectAdminClient;
+import org.keycloak.testframework.annotations.InjectKeycloakUrls;
+import org.keycloak.testframework.annotations.InjectRealm;
+import org.keycloak.testframework.annotations.InjectSimpleHttp;
+import org.keycloak.testframework.annotations.InjectUser;
+import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
+import org.keycloak.testframework.mail.MailServer;
+import org.keycloak.testframework.mail.annotations.InjectMailServer;
+import org.keycloak.testframework.oauth.OAuthClient;
+import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
+import org.keycloak.testframework.realm.ManagedRealm;
+import org.keycloak.testframework.realm.ManagedUser;
+import org.keycloak.testframework.server.KeycloakUrls;
+import org.keycloak.testframework.ui.annotations.InjectPage;
+import org.keycloak.testframework.ui.annotations.InjectWebDriver;
+import org.keycloak.testframework.ui.page.InfoPage;
+import org.keycloak.testframework.ui.page.LoginPage;
+import org.keycloak.testframework.ui.page.LoginPasswordResetPage;
+import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage;
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+import org.keycloak.tests.common.BasicUserConfig;
+import org.keycloak.tests.utils.MailUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
@@ -51,10 +65,9 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
-import org.jboss.arquillian.graphene.page.Page;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
@@ -63,33 +76,55 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Michael Gerber
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
-public class EmailTest extends AbstractI18NTest {
+@KeycloakIntegrationTest
+public class EmailTest {
- @Rule
- public GreenMailRule greenMail = new GreenMailRule();
+ @InjectRealm(config = RealmWithInternationalization.class)
+ ManagedRealm realm;
- @Page
- protected LoginPage loginPage;
+ @InjectUser(config = BasicUserConfig.class)
+ ManagedUser user;
- @Page
- protected LoginPasswordResetPage resetPasswordPage;
+ @InjectMailServer
+ MailServer mailServer;
- @Page
- private InfoPage infoPage;
+ @InjectAdminClient
+ Keycloak adminClient;
- @Page
- private LoginPasswordUpdatePage loginPasswordUpdatePage;
+ @InjectPage
+ LoginPage loginPage;
+
+ @InjectPage
+ LoginPasswordResetPage resetPasswordPage;
+
+ @InjectPage
+ InfoPage infoPage;
+
+ @InjectSimpleHttp
+ SimpleHttp simpleHttp;
+
+ @InjectWebDriver
+ ManagedWebDriver driver;
+
+ @InjectOAuthClient
+ OAuthClient oauth;
+
+ @InjectPage
+ LoginPasswordUpdatePage loginPasswordUpdatePage;
+
+ @InjectKeycloakUrls
+ KeycloakUrls keycloakUrls;
private void changeUserLocale(String locale) {
- UserRepresentation user = findUser("login-test");
- user.singleAttribute(UserModel.LOCALE, locale);
- ApiUtil.findUserByUsernameId(testRealm(), "login-test").update(user);
+ UserRepresentation userRep = user.admin().toRepresentation();
+ userRep.singleAttribute(UserModel.LOCALE, locale);
+ user.admin().update(userRep);
}
@Test
@@ -112,16 +147,16 @@ public class EmailTest extends AbstractI18NTest {
String subjectEn = "Subject EN";
String expectedBodyContentEn = "Body EN";
String bodyMessageEn = expectedBodyContentEn + placeholders;
- testRealm().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), subjectMessageKey, subjectEn);
- testRealm().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), bodyMessageKey, bodyMessageEn);
- getCleanup().addLocalization(Locale.ENGLISH.toLanguageTag());
+ realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(Locale.ENGLISH.toLanguageTag()));
+ realm.admin().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), subjectMessageKey, subjectEn);
+ realm.admin().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), bodyMessageKey, bodyMessageEn);
String subjectDe = "Subject DE";
String expectedBodyContentDe = "Body DE";
String bodyMessageDe = expectedBodyContentDe + placeholders;
- testRealm().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), subjectMessageKey, subjectDe);
- testRealm().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), bodyMessageKey, bodyMessageDe);
- getCleanup().addLocalization(Locale.GERMAN.toLanguageTag());
+ realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(Locale.GERMAN.toLanguageTag()));
+ realm.admin().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), subjectMessageKey, subjectDe);
+ realm.admin().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), bodyMessageKey, bodyMessageDe);
try {
sendResetPasswordEmail();
@@ -150,12 +185,11 @@ public class EmailTest extends AbstractI18NTest {
}
@Test
- public void updatePasswordFromAdmin() throws MessagingException, IOException {
+ public void updatePasswordFromAdmin() {
changeUserLocale(null);
try {
- UserResource testUser = ApiUtil.findUserByUsernameId(testRealm(), "login-test");
- CloseableHttpClient httpClient = HttpClientBuilder.create().build();
- SimpleHttpResponse responseGet = SimpleHttpDefault.doPut(getAuthServerRoot() + "admin/realms/test/users/" + testUser.toRepresentation().getId() + "/execute-actions-email", httpClient)
+ UserResource testUser = user.admin();
+ SimpleHttpResponse responseGet = simpleHttp.doPut(keycloakUrls.getAdmin() + "/realms/" + realm.getName() + "/users/" + testUser.toRepresentation().getId() + "/execute-actions-email")
.auth(adminClient.tokenManager().getAccessTokenString())
.header("Accept-Language", "de")
.json(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()))
@@ -163,13 +197,13 @@ public class EmailTest extends AbstractI18NTest {
assertEquals(responseGet.getStatus(), 204);
- MimeMessage message = greenMail.getReceivedMessages()[0];
+ MimeMessage message = mailServer.getReceivedMessages()[0];
String textBody = MailUtils.getBody(message).getText();
- Assert.assertThat(textBody, containsString("Your administrator has just requested"));
+ MatcherAssert.assertThat(textBody, containsString("Your administrator has just requested"));
} catch (Exception e) {
- Assert.fail(e.getMessage());
+ Assertions.fail(e.getMessage());
} finally {
// Revert
changeUserLocale("en");
@@ -181,12 +215,12 @@ public class EmailTest extends AbstractI18NTest {
changeUserLocale(null);
try {
- loginPage.open();
+ oauth.openLoginForm();
loginPage.resetPassword();
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
- Set cookies = oauth.getDriver().manage().getCookies();
+ Set cookies = driver.cookies().getAll();
String cookieHeader = cookies.stream()
.map(cookie -> cookie.getName() + "=" + cookie.getValue())
.collect(Collectors.joining("; "));
@@ -197,7 +231,7 @@ public class EmailTest extends AbstractI18NTest {
post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "de");
List parameters = new LinkedList<>();
- parameters.add(new BasicNameValuePair("username", "login-test"));
+ parameters.add(new BasicNameValuePair("username", "basic-user"));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
post.setEntity(formEntity);
@@ -213,16 +247,18 @@ public class EmailTest extends AbstractI18NTest {
private void sendResetPasswordEmail() {
- loginPage.open();
+ oauth.openLoginForm();
loginPage.resetPassword();
- resetPasswordPage.changePassword("login-test");
+ resetPasswordPage.assertCurrent();
+ resetPasswordPage.changePassword("basic-user");
}
private void verifyResetPassword(String expectedSubject, String expectedTextBodyContent, String expectedHtmlBodyContent, int expectedMsgCount)
throws MessagingException, IOException {
- assertEquals(expectedMsgCount, greenMail.getReceivedMessages().length);
+ mailServer.waitForIncomingEmail(expectedMsgCount);
+ assertEquals(expectedMsgCount, mailServer.getReceivedMessages().length);
- MimeMessage message = greenMail.getReceivedMessages()[expectedMsgCount - 1];
+ MimeMessage message = mailServer.getReceivedMessages()[expectedMsgCount - 1];
assertEquals(expectedSubject, message.getSubject());
@@ -242,44 +278,41 @@ public class EmailTest extends AbstractI18NTest {
// Issue 13922
@Test
public void changeLocaleOnInfoPage() throws InterruptedException, IOException {
- UserResource testUser = ApiUtil.findUserByUsernameId(testRealm(), "login-test");
+ UserResource testUser = user.admin();
testUser.executeActionsEmail(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
- if (!greenMail.waitForIncomingEmail(1)) {
- Assert.fail("Error when receiving email");
+ if (!mailServer.waitForIncomingEmail(1)) {
+ Assertions.fail("Error when receiving email");
}
- String link = MailUtils.getPasswordResetEmailLink(greenMail.getLastReceivedMessage());
+ String link = MailUtils.getPasswordResetEmailLink(mailServer.getLastReceivedMessage());
// Make sure kc_locale added to link doesn't set locale
link += "&kc_locale=de";
- DroneUtils.getCurrentDriver().navigate().to(link);
- WaitUtils.waitForPageToLoad();
+ driver.open(link);
- Assert.assertTrue("Expected to be on InfoPage, but it was on " + DroneUtils.getCurrentDriver().getTitle(), infoPage.isCurrent());
- assertThat(infoPage.getLanguageDropdownText(), is(equalTo("English")));
+ infoPage.assertCurrent();
+ assertThat(infoPage.getSelectedLanguage(), is(equalTo("English")));
- infoPage.openLanguage("Deutsch");
+ infoPage.selectLanguage("Deutsch");
- assertThat(DroneUtils.getCurrentDriver().getPageSource(), containsString("Passwort aktualisieren"));
+ assertThat(driver.page().getPageSource(), containsString("Passwort aktualisieren"));
- infoPage.clickToContinueDe();
+ driver.findElement(By.linkText("» Klicken Sie hier um fortzufahren")).click();
- loginPasswordUpdatePage.openLanguage("English");
+ loginPasswordUpdatePage.selectLanguage("English");
loginPasswordUpdatePage.changePassword("pass", "pass");
- WaitUtils.waitForPageToLoad();
- Assert.assertTrue("Expected to be on InfoPage, but it was on " + DroneUtils.getCurrentDriver().getTitle(), infoPage.isCurrent());
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
// Change language again when on final info page with the message about updated account (authSession removed already at this point)
- infoPage.openLanguage("Deutsch");
- assertEquals("Deutsch", infoPage.getLanguageDropdownText());
+ infoPage.selectLanguage("Deutsch");
+ assertEquals("Deutsch", infoPage.getSelectedLanguage());
assertThat(infoPage.getInfo(), containsString("Ihr Benutzerkonto wurde aktualisiert."));
- infoPage.openLanguage("English");
- assertEquals("English", infoPage.getLanguageDropdownText());
+ infoPage.selectLanguage("English");
+ assertEquals("English", infoPage.getSelectedLanguage());
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
}
@@ -289,16 +322,16 @@ public class EmailTest extends AbstractI18NTest {
public void resetPasswordOriginalUiLocalePreservedAfterForgetPassword() throws MessagingException, IOException {
// Assert login page is in german
oauth.loginForm().uiLocales("de").open();
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
// Click "Forget password"
driver.findElement(By.linkText("Passwort vergessen?")).click();
- assertEquals("Deutsch", resetPasswordPage.getLanguageDropdownText());
- resetPasswordPage.changePassword("login-test");
+ assertEquals("Deutsch", resetPasswordPage.getSelectedLanguage());
+ resetPasswordPage.changePassword("basic-user");
// Ensure that page is still in german (after authenticationSession was forked on server). The emailSentMessage should be also displayed in german
loginPage.assertCurrent();
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
assertEquals("Sie sollten in Kürze eine E-Mail mit weiteren Instruktionen erhalten.", loginPage.getSuccessMessage());
}
diff --git a/tests/base/src/test/java/org/keycloak/tests/i18n/LoginPageTest.java b/tests/base/src/test/java/org/keycloak/tests/i18n/LoginPageTest.java
new file mode 100644
index 00000000000..8685182b6f4
--- /dev/null
+++ b/tests/base/src/test/java/org/keycloak/tests/i18n/LoginPageTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.tests.i18n;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.util.KeycloakUriBuilder;
+import org.keycloak.cookie.CookieType;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.forms.login.freemarker.DetachedInfoStateChecker;
+import org.keycloak.http.simple.SimpleHttp;
+import org.keycloak.locale.LocaleSelectorProvider;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testframework.annotations.InjectEvents;
+import org.keycloak.testframework.annotations.InjectRealm;
+import org.keycloak.testframework.annotations.InjectSimpleHttp;
+import org.keycloak.testframework.annotations.InjectUser;
+import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
+import org.keycloak.testframework.events.EventAssertion;
+import org.keycloak.testframework.events.Events;
+import org.keycloak.testframework.injection.LifeCycle;
+import org.keycloak.testframework.oauth.OAuthClient;
+import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
+import org.keycloak.testframework.realm.ClientConfigBuilder;
+import org.keycloak.testframework.realm.ManagedRealm;
+import org.keycloak.testframework.realm.ManagedUser;
+import org.keycloak.testframework.realm.RealmConfigBuilder;
+import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
+import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
+import org.keycloak.testframework.server.KeycloakServerConfig;
+import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
+import org.keycloak.testframework.ui.annotations.InjectPage;
+import org.keycloak.testframework.ui.annotations.InjectWebDriver;
+import org.keycloak.testframework.ui.page.AbstractLoginPage;
+import org.keycloak.testframework.ui.page.ErrorPage;
+import org.keycloak.testframework.ui.page.LoginExpiredPage;
+import org.keycloak.testframework.ui.page.LoginPage;
+import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage;
+import org.keycloak.testframework.ui.page.OAuthGrantPage;
+import org.keycloak.testframework.ui.page.TermsAndConditionsPage;
+import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
+import org.keycloak.tests.common.BasicUserConfig;
+import org.keycloak.testsuite.forms.ClickThroughAuthenticator;
+import org.keycloak.testsuite.util.FlowUtil;
+import org.keycloak.testsuite.util.IdentityProviderBuilder;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Cookie;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Michael Gerber
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+@KeycloakIntegrationTest(config = LoginPageTest.ServerConfig.class)
+public class LoginPageTest {
+
+ @InjectRealm(config = LoginPageRealmConfig.class)
+ ManagedRealm realm;
+
+ @InjectUser(config = BasicUserConfig.class, lifecycle = LifeCycle.METHOD)
+ ManagedUser user;
+
+ @InjectOAuthClient
+ protected OAuthClient oauth;
+
+ @InjectPage
+ protected LoginPage loginPage;
+
+ @InjectPage
+ protected ErrorPage errorPage;
+
+ @InjectPage
+ protected LoginPasswordUpdatePage changePasswordPage;
+
+ @InjectPage
+ protected OAuthGrantPage grantPage;
+
+ @InjectPage
+ protected LoginExpiredPage loginExpiredPage;
+
+ @InjectPage
+ protected TermsAndConditionsPage termsPage;
+
+ @InjectWebDriver
+ protected ManagedWebDriver driver;
+
+ @InjectSimpleHttp
+ protected SimpleHttp simpleHttp;
+
+ @InjectEvents
+ protected Events events;
+
+ @InjectRunOnServer
+ protected RunOnServerClient runOnServer;
+
+ @Test
+ public void languageDropdown() {
+ oauth.openLoginForm();
+ assertEquals("English", loginPage.getSelectedLanguage());
+
+ switchLanguageToGermanAndBack("Username or email", "Benutzername oder E-Mail", loginPage);
+ }
+
+ @Test
+ public void uiLocalesParameter() {
+ oauth.loginForm().open();
+ assertEquals("English", loginPage.getSelectedLanguage());
+
+ //test if cookie works
+ oauth.loginForm().uiLocales("de").open();
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
+
+ driver.cookies().deleteAll();
+ oauth.loginForm().uiLocales("de").open();
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
+
+ driver.cookies().deleteAll();
+ oauth.loginForm().uiLocales("en de").open();
+ assertEquals("English", loginPage.getSelectedLanguage());
+
+ driver.cookies().deleteAll();
+ oauth.loginForm().uiLocales("fr de").open();
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
+ }
+
+ @Test
+ public void htmlLangAttributeWithInternationalizationEnabled() {
+ oauth.openLoginForm();
+ assertEquals("en", getHtmlLanguage());
+
+ oauth.loginForm().uiLocales("de").open();
+ assertEquals("de", getHtmlLanguage());
+ }
+
+ @Test
+ public void htmlLangAttributeWithInternationalizationDisabled() {
+ realm.updateWithCleanup(r -> r.internationalizationEnabled(false));
+
+ oauth.openLoginForm();
+ assertEquals("en", getHtmlLanguage());
+ }
+
+ @Test
+ public void acceptLanguageHeader() throws IOException {
+ String responseDe = simpleHttp.doGet(oauth.loginForm().build()).header("Accept-Language", "de").header("Accept", "text/html").asString();
+ Assertions.assertTrue(responseDe.contains("Bei Ihrem Konto anmelden"));
+
+ String responseEn = simpleHttp.doGet(oauth.loginForm().build()).header("Accept-Language", "en").header("Accept", "text/html").asString();
+ Assertions.assertTrue(responseEn.contains("Sign in to your account"));
+ }
+
+ @Test
+ public void testIdentityProviderCapitalization(){
+ oauth.openLoginForm();
+ // contains even name of sub-item - svg element in this case
+ assertThat(loginPage.findSocialButton("github").getText(), is("GitHub"));
+ assertThat(loginPage.findSocialButton("mysaml").getText(), is("mysaml"));
+ assertThat(loginPage.findSocialButton("myoidc").getText(), is("MyOIDC"));
+ }
+
+
+ // KEYCLOAK-3887
+ @Test
+ public void languageChangeRequiredActions() {
+ UserResource user = this.user.admin();
+ UserRepresentation userRep = user.toRepresentation();
+ userRep.setRequiredActions(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
+ user.update(userRep);
+
+ oauth.openLoginForm();
+ oauth.fillLoginForm("basic-user", "password");
+
+ changePasswordPage.assertCurrent();
+ assertEquals("English", changePasswordPage.getSelectedLanguage());
+
+ // Switch language
+ switchLanguageToGermanAndBack("Update password", "Passwort aktualisieren", changePasswordPage);
+
+ // Update password
+ changePasswordPage.changePassword("password", "password");
+
+ Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
+ }
+
+
+ // KEYCLOAK-3887
+ @Test
+ public void languageChangeConsentScreen() {
+ // Set client, which requires consent
+ oauth.client("third-party", "password");
+
+ oauth.openLoginForm();
+ oauth.fillLoginForm("basic-user", "password");
+
+ grantPage.assertCurrent();
+ assertEquals("English", grantPage.getSelectedLanguage());
+
+ // Switch language
+ switchLanguageToGermanAndBack("Do you grant these access privileges?", "Wollen Sie diese Zugriffsrechte", changePasswordPage);
+
+ // Confirm grant
+ grantPage.accept();
+
+ Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
+
+ // Revert client
+ oauth.client("test-app", "password");
+ }
+
+ @Test
+ public void languageUserUpdates() throws InterruptedException {
+ oauth.openLoginForm();
+ loginPage.selectLanguage("Deutsch");
+
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
+
+ Cookie localeCookie = driver.cookies().get(CookieType.LOCALE);
+ assertEquals("de", localeCookie.getValue());
+
+ loginPage.fillLogin("basic-user", "password");
+ loginPage.submit();
+
+ Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
+
+ EventAssertion.assertSuccess(events.poll()).type(EventType.UPDATE_PROFILE).userId(user.getId()).details(Details.PREF_UPDATED + UserModel.LOCALE, "de");
+ EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN).userId(user.getId());
+
+ UserRepresentation userRep = user.admin().toRepresentation();
+ assertEquals("de", userRep.getAttributes().get("locale").get(0));
+
+ String code = oauth.parseLoginResponse().getCode();
+ String idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
+ oauth.logoutRequest().idTokenHint(idTokenHint).send();
+ oauth.openLoginForm();
+
+ assertEquals("Deutsch", loginPage.getSelectedLanguage());
+
+ userRep.getAttributes().remove("locale");
+ user.admin().update(userRep);
+
+ oauth.doLogin("basic-user", "password");
+
+ // User locale should not be updated due to previous cookie
+ userRep = user.admin().toRepresentation();
+ Assertions.assertNull(userRep.getAttributes());
+
+ code = oauth.parseLoginResponse().getCode();
+ idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
+ oauth.logoutRequest().idTokenHint(idTokenHint).send();
+
+ oauth.openLoginForm();
+
+ // Cookie should be removed as last user to login didn't have a locale
+ localeCookie = driver.cookies().get(CookieType.LOCALE);
+ Assertions.assertNull(localeCookie);
+ }
+
+
+ // Test for user updating locale on the error page (when authenticationSession is not available)
+ @Test
+ public void languageUserUpdatesOnErrorPage() {
+ // Login page with invalid redirect_uri
+ String redirectUri = oauth.getRedirectUri();
+ oauth.redirectUri("http://invalid");
+ oauth.openLoginForm();
+
+ errorPage.assertCurrent();
+ Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
+
+ // Change language should be OK
+ errorPage.selectLanguage("Deutsch");
+ assertEquals("Deutsch", errorPage.getSelectedLanguage());
+ Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
+
+ // Refresh browser button should keep german language
+ driver.navigate().refresh();
+ assertEquals("Deutsch", errorPage.getSelectedLanguage());
+ Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
+
+ // Changing to english should work
+ errorPage.selectLanguage("English");
+ assertEquals("English", errorPage.getSelectedLanguage());
+ Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
+
+ oauth.redirectUri(redirectUri);
+ }
+
+ @Test
+ public void languageUserUpdatesOnErrorPageStateCheckerTest() throws URISyntaxException {
+ String redirectUri = oauth.getRedirectUri();
+
+ // Login page with invalid redirect_uri
+ oauth.redirectUri("http://invalid");
+ oauth.openLoginForm();
+
+ errorPage.assertCurrent();
+ Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
+
+ errorPage.selectLanguage("Deutsch");
+ Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
+
+ // Add incorrect state checker parameter. Error page should be shown about expired action. Language won't be changed
+ String currentUrl = driver.getCurrentUrl();
+ String newUrl = KeycloakUriBuilder.fromUri(new URI(currentUrl))
+ .replaceQueryParam(LocaleSelectorProvider.KC_LOCALE_PARAM, "en")
+ .replaceQueryParam(DetachedInfoStateChecker.STATE_CHECKER_PARAM, "invalid").buildAsString();
+ driver.open(newUrl);
+
+ Assertions.assertEquals("Die Aktion ist nicht mehr gültig.", errorPage.getError()); // Action expired.
+
+ oauth.redirectUri(redirectUri);
+ }
+
+ @Test
+ public void languageUserUpdatesOnExpiredPage() throws Exception {
+ UserRepresentation userRep = user.admin().toRepresentation();
+ userRep.setRequiredActions(Collections.singletonList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
+ user.admin().update(userRep);
+
+ oauth.openLoginForm();
+ oauth.fillLoginForm("basic-user", "invalid-password");
+ loginPage.assertCurrent();
+
+ assertThat(loginPage.getUsernameInputError(), is("Invalid username or password."));
+ loginPage.fillLogin("basic-user", "password");
+ loginPage.submit();
+
+ changePasswordPage.assertCurrent();
+
+ // navigate back to the login expired page and change language to german
+ driver.navigate().backWithRefresh(loginExpiredPage);
+ errorPage.selectLanguage("Deutsch");
+ assertEquals("Deutsch", errorPage.getSelectedLanguage());
+ driver.assertions().assertTitle("Diese Seite ist nicht mehr gültig.");
+
+ // continue should show password update in german
+ loginExpiredPage.clickLoginContinueLink();
+ assertEquals("Deutsch", changePasswordPage.getSelectedLanguage());
+ driver.assertions().assertTitle("Passwort aktualisieren");
+ }
+
+ // GH issue 41292
+ @Test
+ public void languageUserUpdatesOnCustomAuthenticatorPage() {
+ configureBrowserFlowWithClickThroughAuthenticator();
+
+ oauth.openLoginForm();
+ termsPage.assertCurrent();
+
+ // Change language on the custom page
+ switchLanguageToGermanAndBack("Terms and Conditions", "Bedingungen und Konditionen", termsPage);
+
+ // Revert dummy flow
+ RealmRepresentation rep = realm.admin().toRepresentation();
+ rep.setBrowserFlow("browser");
+ realm.admin().update(rep);
+ }
+
+ @Test
+ public void realmLocalizationMessagesAreApplied() {
+ String realmLocalizationMessageKey = "loginAccountTitle";
+
+ String realmLocalizationMessageValueEn = "Localization Test EN";
+ saveLocalizationText(Locale.ENGLISH.toLanguageTag(), realmLocalizationMessageKey,
+ realmLocalizationMessageValueEn);
+ String realmLocalizationMessageValueDe = "Localization Test DE";
+ saveLocalizationText(Locale.GERMAN.toLanguageTag(), realmLocalizationMessageKey,
+ realmLocalizationMessageValueDe);
+
+ oauth.openLoginForm();
+ switchLanguageToGermanAndBack(realmLocalizationMessageValueEn, realmLocalizationMessageValueDe, loginPage);
+ }
+
+ // KEYCLOAK-18590
+ @Test
+ public void realmLocalizationMessagesAreNotCachedWithinTheTheme() {
+ final String locale = Locale.ENGLISH.toLanguageTag();
+
+ final String realmLocalizationMessageKey = "loginAccountTitle";
+ final String realmLocalizationMessageValue = "Localization Test";
+
+ saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
+ oauth.openLoginForm();
+ assertThat(driver.page().getPageSource(), containsString(realmLocalizationMessageValue));
+
+ realm.admin().localization().deleteRealmLocalizationText(locale, realmLocalizationMessageKey);
+ oauth.openLoginForm();
+ assertThat(driver.page().getPageSource(), not(containsString(realmLocalizationMessageValue)));
+ }
+
+ @Test
+ public void realmLocalizationMessagesUsedDuringErrorHandling() {
+ final String locale = Locale.ENGLISH.toLanguageTag();
+
+ final String realmLocalizationMessageKey = "errorTitle";
+ final String realmLocalizationMessageValue = "We are really sorry...";
+
+ saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
+ String nonExistingUrl = oauth.loginForm().build().split("protocol")[0] + "incorrect-path";
+ driver.open(nonExistingUrl);
+
+ assertThat(driver.page().getPageSource(), containsString(realmLocalizationMessageValue));
+ }
+
+ private String getHtmlLanguage() {
+ return driver.findElement(By.xpath("//html")).getAttribute("lang");
+ }
+
+ private void saveLocalizationText(String locale, String key, String value) {
+ realm.admin().localization().saveRealmLocalizationText(locale, key, value);
+ realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(locale));
+ }
+
+ private void switchLanguageToGermanAndBack(String expectedEnglishMessage, String expectedGermanMessage, AbstractLoginPage page) {
+ // Switch language to Deutsch
+ page.selectLanguage("Deutsch");
+ assertEquals("Deutsch", page.getSelectedLanguage());
+ String pageSource = driver.page().getPageSource();
+ assertThat(pageSource, not(containsString(expectedEnglishMessage)));
+ assertThat(pageSource, containsString(expectedGermanMessage));
+
+ // Revert language
+ page.selectLanguage("English");
+ assertEquals("English", page.getSelectedLanguage());
+ pageSource = driver.page().getPageSource();
+ assertThat(pageSource, containsString(expectedEnglishMessage));
+ assertThat(pageSource, not(containsString(expectedGermanMessage)));
+ }
+
+ private void configureBrowserFlowWithClickThroughAuthenticator() {
+ final String newFlowAlias = "browser - rule";
+ runOnServer.run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
+ runOnServer.run(session -> FlowUtil.inCurrentRealm(session)
+ .selectFlow(newFlowAlias)
+ .inForms(forms -> forms
+ .clear()
+ // Update the browser forms with a UsernamePasswordForm
+ .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, ClickThroughAuthenticator.PROVIDER_ID)
+ )
+ .defineAsBrowserFlow()
+ );
+ }
+
+ public static class LoginPageRealmConfig extends RealmWithInternationalization {
+
+ @Override
+ public RealmConfigBuilder configure(RealmConfigBuilder realm) {
+ realm = super.configure(realm);
+ realm.identityProvider(IdentityProviderBuilder.create()
+ .providerId("github")
+ .alias("github")
+ .build());
+ realm.identityProvider(IdentityProviderBuilder.create()
+ .providerId("saml")
+ .alias("mysaml")
+ .build());
+ realm.identityProvider(IdentityProviderBuilder.create()
+ .providerId("oidc")
+ .alias("myoidc")
+ .displayName("MyOIDC")
+ .build());
+ realm.client(ClientConfigBuilder.create().clientId("third-party").secret("password").consentRequired(true).redirectUris("*").build());
+ return realm;
+ }
+ }
+
+ protected static class ServerConfig implements KeycloakServerConfig {
+
+ @Override
+ public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
+ return config.dependency("org.keycloak.tests", "keycloak-tests-custom-providers");
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/RealmLocalizationTest.java b/tests/base/src/test/java/org/keycloak/tests/i18n/RealmLocalizationTest.java
similarity index 58%
rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/RealmLocalizationTest.java
rename to tests/base/src/test/java/org/keycloak/tests/i18n/RealmLocalizationTest.java
index eb3e6c978b9..d69572d178f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/RealmLocalizationTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/i18n/RealmLocalizationTest.java
@@ -1,15 +1,22 @@
-package org.keycloak.testsuite.i18n;
+package org.keycloak.tests.i18n;
import java.util.Map;
import org.keycloak.admin.client.resource.RealmLocalizationResource;
+import org.keycloak.testframework.annotations.InjectRealm;
+import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
+import org.keycloak.testframework.realm.ManagedRealm;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;
-public class RealmLocalizationTest extends AbstractI18NTest {
+@KeycloakIntegrationTest
+public class RealmLocalizationTest {
+
+ @InjectRealm(config = RealmWithInternationalization.class)
+ ManagedRealm managedRealm;
/**
* Make sure that realm localization texts support unicode ().
@@ -19,7 +26,7 @@ public class RealmLocalizationTest extends AbstractI18NTest {
String locale = "en";
String key = "Äǜṳǚǘǖ";
String text = "Öṏṏ";
- RealmLocalizationResource localizationResource = testRealm().localization();
+ RealmLocalizationResource localizationResource = managedRealm.admin().localization();
localizationResource.saveRealmLocalizationText(locale, key, text);
Map localizationTexts = localizationResource.getRealmLocalizationTexts(locale, false);
diff --git a/tests/base/src/test/java/org/keycloak/tests/i18n/RealmWithInternationalization.java b/tests/base/src/test/java/org/keycloak/tests/i18n/RealmWithInternationalization.java
new file mode 100644
index 00000000000..a63e59365d8
--- /dev/null
+++ b/tests/base/src/test/java/org/keycloak/tests/i18n/RealmWithInternationalization.java
@@ -0,0 +1,13 @@
+package org.keycloak.tests.i18n;
+
+import org.keycloak.testframework.realm.RealmConfig;
+import org.keycloak.testframework.realm.RealmConfigBuilder;
+
+public class RealmWithInternationalization implements RealmConfig {
+
+ @Override
+ public RealmConfigBuilder configure(RealmConfigBuilder realm) {
+ return realm.resetPasswordAllowed(true).internationalizationEnabled(true).supportedLocales("de", "en");
+ }
+
+}
diff --git a/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java b/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java
index ec866fbccc0..108566888cf 100644
--- a/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java
+++ b/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java
@@ -10,6 +10,7 @@ import org.junit.platform.suite.api.Suite;
"org.keycloak.tests.cors",
"org.keycloak.tests.db",
"org.keycloak.tests.forms",
+ "org.keycloak.tests.i18n",
"org.keycloak.tests.infinispan",
"org.keycloak.tests.keys",
"org.keycloak.tests.oauth",
diff --git a/tests/base/src/test/java/org/keycloak/tests/suites/LoginV1TestSuite.java b/tests/base/src/test/java/org/keycloak/tests/suites/LoginV1TestSuite.java
index edf23f7452e..2c6aaf49ba7 100644
--- a/tests/base/src/test/java/org/keycloak/tests/suites/LoginV1TestSuite.java
+++ b/tests/base/src/test/java/org/keycloak/tests/suites/LoginV1TestSuite.java
@@ -1,12 +1,13 @@
package org.keycloak.tests.suites;
-import org.keycloak.tests.admin.AdminHeadersTest;
+import org.keycloak.tests.i18n.LoginPageTest;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
@Suite
-// TODO: Select relevant test classes or packages once they have been migrated
-@SelectClasses({AdminHeadersTest.class})
+@SelectClasses({
+ LoginPageTest.class
+})
public class LoginV1TestSuite {
}
diff --git a/tests/custom-providers/src/main/java/org/keycloak/testsuite/forms/ClickThroughAuthenticator.java b/tests/custom-providers/src/main/java/org/keycloak/testsuite/forms/ClickThroughAuthenticator.java
new file mode 100644
index 00000000000..c763c3ad43b
--- /dev/null
+++ b/tests/custom-providers/src/main/java/org/keycloak/testsuite/forms/ClickThroughAuthenticator.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.forms;
+
+import java.util.List;
+
+import jakarta.ws.rs.core.Response;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.ProviderConfigProperty;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ClickThroughAuthenticator implements Authenticator, AuthenticatorFactory {
+ public static final String PROVIDER_ID = "testsuite-dummy-click-through";
+
+ @Override
+ public void authenticate(AuthenticationFlowContext context) {
+ Response challenge = context.form().createForm("terms.ftl");
+ context.challenge(challenge);
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void action(AuthenticationFlowContext context) {
+ if (context.getHttpRequest().getDecodedFormParameters().containsKey("cancel")) {
+ authenticate(context);
+ return;
+ }
+
+ context.success();
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Testsuite Dummy Click Thru";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public boolean isUserSetupAllowed() {
+ return false;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Testsuite Dummy authenticator. User needs to click through the page to continue.";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public Authenticator create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
new file mode 100755
index 00000000000..8218081a64c
--- /dev/null
+++ b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -0,0 +1 @@
+org.keycloak.testsuite.forms.ClickThroughAuthenticator
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/FlowUtil.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/FlowUtil.java
similarity index 100%
rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/FlowUtil.java
rename to tests/utils-shared/src/main/java/org/keycloak/testsuite/util/FlowUtil.java
diff --git a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractOAuthClient.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractOAuthClient.java
index 8ef242e4641..057343e9a60 100644
--- a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractOAuthClient.java
+++ b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractOAuthClient.java
@@ -150,12 +150,12 @@ public abstract class AbstractOAuthClient {
logoutForm().open();
}
- public LogoutRequest logoutRequest(String refreshToken) {
- return new LogoutRequest(refreshToken, this);
+ public LogoutRequest logoutRequest() {
+ return new LogoutRequest(this);
}
public LogoutResponse doLogout(String refreshToken) {
- return logoutRequest(refreshToken).send();
+ return logoutRequest().refreshToken(refreshToken).send();
}
public BackchannelLogoutRequest backchannelLogoutRequest(String logoutToken) {
diff --git a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LogoutRequest.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LogoutRequest.java
index 17fb0af90f1..f48e75bac81 100644
--- a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LogoutRequest.java
+++ b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LogoutRequest.java
@@ -8,11 +8,21 @@ import org.apache.http.client.methods.CloseableHttpResponse;
public class LogoutRequest extends AbstractHttpPostRequest {
- private final String refreshToken;
+ private String refreshToken;
+ private String idTokenHint;
- LogoutRequest(String refreshToken, AbstractOAuthClient> client) {
+ LogoutRequest(AbstractOAuthClient> client) {
super(client);
+ }
+
+ public LogoutRequest refreshToken(String refreshToken) {
this.refreshToken = refreshToken;
+ return this;
+ }
+
+ public LogoutRequest idTokenHint(String idTokenHint) {
+ this.idTokenHint = idTokenHint;
+ return this;
}
@Override
@@ -21,7 +31,12 @@ public class LogoutRequest extends AbstractHttpPostRequestMichael Gerber
- * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
- */
-public class LoginPageTest extends AbstractI18NTest {
-
- @Page
- protected AppPage appPage;
-
- @Page
- protected LoginPage loginPage;
-
- @Page
- protected ErrorPage errorPage;
-
- @Page
- protected LoginPasswordUpdatePage changePasswordPage;
-
- @Page
- protected OAuthGrantPage grantPage;
-
- @Page
- protected LoginExpiredPage loginExpiredPage;
-
- @Page
- protected TermsAndConditionsPage termsPage;
-
- @Rule
- public AssertEvents events = new AssertEvents(this);
-
- @Before
- public void before() {
- setRealmInternationalization(true);
- }
-
- @Override
- public void configureTestRealm(RealmRepresentation testRealm) {
- testRealm.addIdentityProvider(IdentityProviderBuilder.create()
- .providerId("github")
- .alias("github")
- .build());
- testRealm.addIdentityProvider(IdentityProviderBuilder.create()
- .providerId("saml")
- .alias("mysaml")
- .build());
- testRealm.addIdentityProvider(IdentityProviderBuilder.create()
- .providerId("oidc")
- .alias("myoidc")
- .displayName("MyOIDC")
- .build());
-
- }
-
- @Test
- public void languageDropdown() {
- loginPage.open();
- assertEquals("English", loginPage.getLanguageDropdownText());
-
- switchLanguageToGermanAndBack("Username or email", "Benutzername oder E-Mail", loginPage);
- }
-
- @Test
- public void uiLocalesParameter() {
- oauth.loginForm().open();
- assertEquals("English", loginPage.getLanguageDropdownText());
-
- //test if cookie works
- oauth.loginForm().uiLocales("de").open();
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
-
- driver.manage().deleteAllCookies();
- oauth.loginForm().uiLocales("de").open();
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
-
- driver.manage().deleteAllCookies();
- oauth.loginForm().uiLocales("en de").open();
- assertEquals("English", loginPage.getLanguageDropdownText());
-
- driver.manage().deleteAllCookies();
- oauth.loginForm().uiLocales("fr de").open();
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
- }
-
- @Test
- public void htmlLangAttributeWithInternationalizationEnabled() {
- loginPage.open();
- assertEquals("en", loginPage.getHtmlLanguage());
-
- oauth.loginForm().uiLocales("de").open();
- assertEquals("de", loginPage.getHtmlLanguage());
- }
-
- @Test
- public void htmlLangAttributeWithInternationalizationDisabled() {
- setRealmInternationalization(false);
-
- loginPage.open();
- assertEquals("en", loginPage.getHtmlLanguage());
- }
-
- @Test
- public void acceptLanguageHeader() throws IOException {
- try(CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
- ApacheHttpClient43Engine engine = new ApacheHttpClient43Engine(httpClient);
- ResteasyClient client = ((ResteasyClientBuilder) ResteasyClientBuilder.newBuilder()).httpEngine(engine).build();
-
- loginPage.open();
-
- try(Response responseDe = client.target(driver.getCurrentUrl()).request().acceptLanguage("de").get()) {
- Assert.assertTrue(responseDe.readEntity(String.class).contains("Anmeldung bei test"));
-
- try(Response responseEn = client.target(driver.getCurrentUrl()).request().acceptLanguage("en").get()) {
- Assert.assertTrue(responseEn.readEntity(String.class).contains("Sign in to test"));
- }
- }
-
- client.close();
- }
- }
-
- @Test
- public void testIdentityProviderCapitalization(){
- loginPage.open();
- // contains even name of sub-item - svg element in this case
- assertThat(loginPage.findSocialButton("github").getText(), is("GitHub"));
- assertThat(loginPage.findSocialButton("mysaml").getText(), is("mysaml"));
- assertThat(loginPage.findSocialButton("myoidc").getText(), is("MyOIDC"));
- }
-
-
- // KEYCLOAK-3887
- @Test
- public void languageChangeRequiredActions() {
- UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
- UserRepresentation userRep = user.toRepresentation();
- userRep.setRequiredActions(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
- user.update(userRep);
-
- loginPage.open();
-
- loginPage.login("test-user@localhost", "password");
- changePasswordPage.assertCurrent();
- assertEquals("English", changePasswordPage.getLanguageDropdownText());
-
- // Switch language
- switchLanguageToGermanAndBack("Update password", "Passwort aktualisieren", changePasswordPage);
-
- // Update password
- changePasswordPage.changePassword("password", "password");
-
- assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
- Assert.assertNotNull(oauth.parseLoginResponse().getCode());
- }
-
-
- // KEYCLOAK-3887
- @Test
- public void languageChangeConsentScreen() {
- // Set client, which requires consent
- oauth.client("third-party", "password");
-
- loginPage.open();
-
- loginPage.login("test-user@localhost", "password");
-
- grantPage.assertCurrent();
- assertEquals("English", grantPage.getLanguageDropdownText());
-
- // Switch language
- switchLanguageToGermanAndBack("Do you grant these access privileges?", "Wollen Sie diese Zugriffsrechte", changePasswordPage);
-
- // Confirm grant
- grantPage.accept();
-
- assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
- Assert.assertNotNull(oauth.parseLoginResponse().getCode());
-
- // Revert client
- oauth.client("test-app", "password");
- }
-
- @Test
- public void languageUserUpdates() {
- loginPage.open();
- loginPage.openLanguage("Deutsch");
-
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
-
- Cookie localeCookie = driver.manage().getCookieNamed(CookieType.LOCALE.getName());
- assertEquals("de", localeCookie.getValue());
-
- UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
- String userId = user.toRepresentation().getId();
- loginPage.login("test-user@localhost", "password");
-
- events.expect(EventType.UPDATE_PROFILE)
- .user(userId)
- .client("test-app")
- .detail(Details.PREF_UPDATED + UserModel.LOCALE, "de")
- .assertEvent();
- events.expectLogin()
- .user(userId)
- .client("test-app")
- .assertEvent();
-
- UserRepresentation userRep = user.toRepresentation();
- assertEquals("de", userRep.getAttributes().get("locale").get(0));
-
- String code = oauth.parseLoginResponse().getCode();
- String idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
- appPage.logout(idTokenHint);
-
- loginPage.open();
-
- assertEquals("Deutsch", loginPage.getLanguageDropdownText());
-
- userRep.getAttributes().remove("locale");
- user.update(userRep);
-
- loginPage.open();
- loginPage.login("test-user@localhost", "password");
-
- // User locale should not be updated due to previous cookie
- userRep = user.toRepresentation();
- Assert.assertNull(userRep.getAttributes());
-
- code = oauth.parseLoginResponse().getCode();
- idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
- appPage.logout(idTokenHint);
-
- loginPage.open();
-
- // Cookie should be removed as last user to login didn't have a locale
- localeCookie = driver.manage().getCookieNamed(CookieType.LOCALE.getName());
- Assert.assertNull(localeCookie);
- }
-
-
- // Test for user updating locale on the error page (when authenticationSession is not available)
- @Test
- public void languageUserUpdatesOnErrorPage() {
- // Login page with invalid redirect_uri
- oauth.redirectUri("http://invalid");
- loginPage.open();
-
- errorPage.assertCurrent();
- Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
-
- // Change language should be OK
- errorPage.openLanguage("Deutsch");
- assertEquals("Deutsch", errorPage.getLanguageDropdownText());
- Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
-
- // Refresh browser button should keep german language
- driver.navigate().refresh();
- assertEquals("Deutsch", errorPage.getLanguageDropdownText());
- Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
-
- // Changing to english should work
- errorPage.openLanguage("English");
- assertEquals("English", errorPage.getLanguageDropdownText());
- Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
- }
-
- @Test
- public void languageUserUpdatesOnErrorPageStateCheckerTest() throws URISyntaxException {
- // Login page with invalid redirect_uri
- oauth.redirectUri("http://invalid");
- loginPage.open();
-
- errorPage.assertCurrent();
- Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
-
- errorPage.openLanguage("Deutsch");
- Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
-
- // Add incorrect state checker parameter. Error page should be shown about expired action. Language won't be changed
- String currentUrl = driver.getCurrentUrl();
- String newUrl = KeycloakUriBuilder.fromUri(new URI(currentUrl))
- .replaceQueryParam(LocaleSelectorProvider.KC_LOCALE_PARAM, "en")
- .replaceQueryParam(DetachedInfoStateChecker.STATE_CHECKER_PARAM, "invalid").buildAsString();
- driver.navigate().to(newUrl);
-
- Assert.assertEquals("Die Aktion ist nicht mehr gültig.", errorPage.getError()); // Action expired.
- }
-
- @Test
- public void languageUserUpdatesOnExpiredPage() throws Exception {
- try (UserAttributeUpdater userUpdater = UserAttributeUpdater.forUserByUsername(testRealm(), "test-user@localhost")
- .setRequiredActions(UserModel.RequiredAction.UPDATE_PASSWORD).update()) {
- // login with a failure attempt
- loginPage.open();
- loginPage.login("test-user@localhost", "invalid-password");
- loginPage.assertCurrent();
- assertThat(loginPage.getUsernameInputError(), is("Invalid username or password."));
- loginPage.login("test-user@localhost", "password");
- changePasswordPage.assertCurrent();
-
- // navigate back to the login expired page and change language to german
- UIUtils.navigateBackWithRefresh(driver, loginExpiredPage);
- errorPage.openLanguage("Deutsch");
- assertEquals("Deutsch", errorPage.getLanguageDropdownText());
- assertThat(PageUtils.getPageTitle(driver), is("Diese Seite ist nicht mehr gültig."));
-
- // continue should show password update in german
- loginExpiredPage.clickLoginContinueLink();
- assertEquals("Deutsch", changePasswordPage.getLanguageDropdownText());
- assertThat(PageUtils.getPageTitle(driver), is("Passwort aktualisieren"));
- }
- }
-
- // GH issue 41292
- @Test
- public void languageUserUpdatesOnCustomAuthenticatorPage() {
- configureBrowserFlowWithClickThroughAuthenticator();
-
- loginPage.open();
- Assert.assertTrue(termsPage.isCurrent());
-
- // Change language on the custom page
- switchLanguageToGermanAndBack("Terms and Conditions", "Bedingungen und Konditionen", termsPage);
-
- // Revert dummy flow
- RealmRepresentation rep = testRealm().toRepresentation();
- rep.setBrowserFlow("browser");
- testRealm().update(rep);
- }
-
- @Test
- public void realmLocalizationMessagesAreApplied() {
- String realmLocalizationMessageKey = "loginAccountTitle";
-
- String realmLocalizationMessageValueEn = "Localization Test EN";
- saveLocalizationText(Locale.ENGLISH.toLanguageTag(), realmLocalizationMessageKey,
- realmLocalizationMessageValueEn);
- String realmLocalizationMessageValueDe = "Localization Test DE";
- saveLocalizationText(Locale.GERMAN.toLanguageTag(), realmLocalizationMessageKey,
- realmLocalizationMessageValueDe);
-
- loginPage.open();
- switchLanguageToGermanAndBack(realmLocalizationMessageValueEn, realmLocalizationMessageValueDe, loginPage);
- }
-
- // KEYCLOAK-18590
- @Test
- public void realmLocalizationMessagesAreNotCachedWithinTheTheme() {
- final String locale = Locale.ENGLISH.toLanguageTag();
-
- final String realmLocalizationMessageKey = "loginAccountTitle";
- final String realmLocalizationMessageValue = "Localization Test";
-
- saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
- loginPage.open();
- assertThat(driver.getPageSource(), containsString(realmLocalizationMessageValue));
-
- testRealm().localization().deleteRealmLocalizationText(locale, realmLocalizationMessageKey);
- loginPage.open();
- assertThat(driver.getPageSource(), not(containsString(realmLocalizationMessageValue)));
- }
-
- @Test
- public void realmLocalizationMessagesUsedDuringErrorHandling() {
- final String locale = Locale.ENGLISH.toLanguageTag();
-
- final String realmLocalizationMessageKey = "errorTitle";
- final String realmLocalizationMessageValue = "We are really sorry...";
-
- saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
- String nonExistingUrl = oauth.loginForm().build().split("protocol")[0] + "incorrect-path";
- driver.navigate().to(nonExistingUrl);
-
- assertThat(driver.getPageSource(), containsString(realmLocalizationMessageValue));
- }
-
- private void saveLocalizationText(String locale, String key, String value) {
- testRealm().localization().saveRealmLocalizationText(locale, key, value);
- getCleanup().addLocalization(locale);
- }
-
- private void switchLanguageToGermanAndBack(String expectedEnglishMessage, String expectedGermanMessage, LanguageComboboxAwarePage page) {
- // Switch language to Deutsch
- page.openLanguage("Deutsch");
- assertEquals("Deutsch", page.getLanguageDropdownText());
- String pageSource = driver.getPageSource();
- assertThat(pageSource, not(containsString(expectedEnglishMessage)));
- assertThat(pageSource, containsString(expectedGermanMessage));
-
- // Revert language
- page.openLanguage("English");
- assertEquals("English", page.getLanguageDropdownText());
- pageSource = driver.getPageSource();
- assertThat(pageSource, containsString(expectedEnglishMessage));
- assertThat(pageSource, not(containsString(expectedGermanMessage)));
- }
-
- private void setRealmInternationalization(final boolean enabled) {
- final var realmResource = testRealm();
- RealmRepresentation realm = realmResource.toRepresentation();
- realm.setInternationalizationEnabled(enabled);
- realmResource.update(realm);
- }
-
- private void configureBrowserFlowWithClickThroughAuthenticator() {
- final String newFlowAlias = "browser - rule";
- testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
- testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session)
- .selectFlow(newFlowAlias)
- .inForms(forms -> forms
- .clear()
- // Update the browser forms with a UsernamePasswordForm
- .addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, ClickThroughAuthenticator.PROVIDER_ID)
- )
- .defineAsBrowserFlow()
- );
- }
-}
diff --git a/testsuite/integration-arquillian/tests/base/testsuites/base-suite b/testsuite/integration-arquillian/tests/base/testsuites/base-suite
index 78eb086edda..1c5cfade774 100644
--- a/testsuite/integration-arquillian/tests/base/testsuites/base-suite
+++ b/testsuite/integration-arquillian/tests/base/testsuites/base-suite
@@ -17,7 +17,6 @@ exportimport,4
feature,4
federation,5
forms,5
-i18n,5
login,4
migration,4
model,6
diff --git a/testsuite/integration-arquillian/tests/base/testsuites/login-suite b/testsuite/integration-arquillian/tests/base/testsuites/login-suite
index 0b677864827..589db0046cc 100644
--- a/testsuite/integration-arquillian/tests/base/testsuites/login-suite
+++ b/testsuite/integration-arquillian/tests/base/testsuites/login-suite
@@ -3,4 +3,3 @@ org.keycloak.testsuite.forms
KcOidcBrokerLoginHintTest
KcOidcFirstBrokerLoginTest
KcOidcMultipleTabsBrokerTest
-LoginPageTest