Run tests with keycloak.v2 login theme

The fixes (mostly selectors) are needed for tests.

In the future, to switch the keycloak.v2 to the default theme, do
the following:

- Update `ThemeSelectorProvider`: Uncomment relevant lines
- Update `testsuite/integration-arquillian/tests/pom.xml`: Revert the change in `<login.theme.default>` property
- Update `ThemeSelectorTest` per comment

Signed-off-by: Hynek Mlnarik <hmlnarik@redhat.com>
This commit is contained in:
Hynek Mlnarik 2024-07-17 10:17:25 +02:00 committed by Hynek Mlnařík
parent c3019fb2d3
commit 183cd6c957
43 changed files with 289 additions and 400 deletions

View file

@ -35,7 +35,7 @@ public class DeleteAccountActionConfirmPage extends RequiredActions {
} }
public boolean isErrorMessageDisplayed() { public boolean isErrorMessageDisplayed() {
return driver.findElements(By.cssSelector(".alert-error")).size() == 1; return driver.findElements(By.cssSelector(".pf-v5-c-alert")).size() == 1;
} }
public String getErrorMessageText() { public String getErrorMessageText() {

View file

@ -33,12 +33,19 @@ import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
*/ */
public class FeedbackMessage { public class FeedbackMessage {
private final String SUCCESS = "success"; private final static String SUCCESS = "success";
private final String WARNING = "warning"; private final static String WARNING = "warning";
private final String ERROR = "error"; private final static String ERROR = "danger";
private final String INFO = "info"; private final static String INFO = "info";
@FindBy(css = "div[class^='alert']") private static final Pattern ALERT_TYPE_CLASS_PATTERN = Pattern.compile("(pf-m|alert)-("
+ SUCCESS + "|"
+ WARNING + "|"
+ ERROR + "|"
+ INFO
+ ")");
@FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert']")
private WebElement alertRoot; private WebElement alertRoot;
@FindBy(css = "span[id^='input-error']") @FindBy(css = "span[id^='input-error']")
@ -71,7 +78,7 @@ public class FeedbackMessage {
public String getType() { public String getType() {
try { try {
String cssClass = alertRoot.getAttribute("class"); String cssClass = alertRoot.getAttribute("class");
Matcher classMatcher = Pattern.compile("alert-(.+)").matcher(cssClass); Matcher classMatcher = ALERT_TYPE_CLASS_PATTERN.matcher(cssClass);
if (!classMatcher.find()) { if (!classMatcher.find()) {
throw new RuntimeException("Failed to identify feedback message type"); throw new RuntimeException("Failed to identify feedback message type");
} }

View file

@ -34,7 +34,7 @@ public class OneTimeCode extends Authenticate {
@FindBy(xpath = ".//label[@for='otp']") @FindBy(xpath = ".//label[@for='otp']")
private WebElement otpInputLabel; private WebElement otpInputLabel;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(id = "input-error-otp-code") @FindBy(id = "input-error-otp-code")

View file

@ -43,7 +43,7 @@ public class LoginPasswordUpdatePage {
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
public void changePassword(String newPassword, String passwordConfirm) { public void changePassword(String newPassword, String passwordConfirm) {

View file

@ -31,7 +31,7 @@ public class IdpConfirmLinkPage extends LanguageComboboxAwarePage {
@FindBy(id = "linkAccount") @FindBy(id = "linkAccount")
private WebElement linkAccountButton; private WebElement linkAccountButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement message; private WebElement message;
@Override @Override

View file

@ -25,7 +25,7 @@ public class IdpConfirmOverrideLinkPage extends LanguageComboboxAwarePage {
@FindBy(id = "confirmOverride") @FindBy(id = "confirmOverride")
private WebElement confirmOverrideButton; private WebElement confirmOverrideButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement message; private WebElement message;
@Override @Override

View file

@ -44,7 +44,7 @@ public class InstalledAppRedirectPage extends AbstractPage {
@FindBy(id = "kc-page-title") @FindBy(id = "kc-page-title")
private WebElement pageTitle; private WebElement pageTitle;
@FindBy(className = "alert-error") @FindBy(className = "pf-v5-c-alert")
private WebElement errorBox; private WebElement errorBox;
@Override @Override

View file

@ -20,6 +20,10 @@ package org.keycloak.testsuite.pages;
import org.junit.Assert; import org.junit.Assert;
import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.testsuite.util.WaitUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
@ -33,12 +37,18 @@ import org.openqa.selenium.support.FindBy;
*/ */
public abstract class LanguageComboboxAwarePage extends AbstractPage { public abstract class LanguageComboboxAwarePage extends AbstractPage {
@FindBy(id = "kc-current-locale-link") @FindBy(xpath = "//select[@aria-label='languages']/option[@selected]")
private WebElement languageText; private WebElement languageText;
@FindBy(id = "kc-locale-dropdown") @FindBy(xpath = "//select[@aria-label='languages']")
private WebElement localeDropdown; private WebElement localeDropdown;
@FindBy(id = "kc-current-locale-link")
private WebElement languageTextBase; // base theme
@FindBy(id = "kc-locale-dropdown")
private WebElement localeDropdownBase; // base theme
@FindBy(id = "try-another-way") @FindBy(id = "try-another-way")
private WebElement tryAnotherWayLink; private WebElement tryAnotherWayLink;
@ -52,13 +62,26 @@ public abstract class LanguageComboboxAwarePage extends AbstractPage {
private WebElement accountLink; private WebElement accountLink;
public String getLanguageDropdownText() { public String getLanguageDropdownText() {
return languageText.getText(); try {
final String text = languageText.getText();
return text == null ? text : text.trim();
} catch (NoSuchElementException ex) {
return languageTextBase.getText();
}
} }
public void openLanguage(String language){ public void openLanguage(String language){
WebElement langLink = localeDropdown.findElement(By.xpath("//a[text()[contains(.,'" + language + "')]]")); try {
String url = langLink.getAttribute("href"); WebElement langLink = localeDropdown.findElement(By.xpath("//option[text()[contains(.,'" + language + "')]]"));
DroneUtils.getCurrentDriver().navigate().to(url); String url = langLink.getAttribute("value");
DroneUtils.getCurrentDriver().navigate().to(new URI(DroneUtils.getCurrentDriver().getCurrentUrl()).resolve(url).toString());
} catch (NoSuchElementException ex) {
WebElement langLink = localeDropdownBase.findElement(By.xpath("//a[text()[contains(.,'" + language + "')]]"));
String url = langLink.getAttribute("href");
DroneUtils.getCurrentDriver().navigate().to(url);
} catch (URISyntaxException ex) {
Assert.fail(ex.getMessage());
}
WaitUtils.waitForPageToLoad(); WaitUtils.waitForPageToLoad();
} }

View file

@ -48,7 +48,7 @@ public class LoginConfigTotpPage extends LogoutSessionsPage {
@FindBy(id = "mode-manual") @FindBy(id = "mode-manual")
private WebElement manualLink; private WebElement manualLink;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginAlertErrorMessage; private WebElement loginAlertErrorMessage;
@FindBy(id = "input-error-otp-code") @FindBy(id = "input-error-otp-code")

View file

@ -65,13 +65,13 @@ public class LoginPage extends LanguageComboboxAwarePage {
@FindBy(linkText = "Forgot Password?") @FindBy(linkText = "Forgot Password?")
private WebElement resetPasswordLink; private WebElement resetPasswordLink;
@FindBy(className = "alert-error") @FindBy(className = "pf-m-danger")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(className = "alert-success") @FindBy(className = "pf-m-success")
private WebElement loginSuccessMessage; private WebElement loginSuccessMessage;
@FindBy(className = "alert-info") @FindBy(className = "pf-m-info")
private WebElement loginInfoMessage; private WebElement loginInfoMessage;
@FindBy(className = "instruction") @FindBy(className = "instruction")

View file

@ -35,10 +35,10 @@ public class LoginPasswordResetPage extends LanguageComboboxAwarePage {
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-success") @FindBy(className = "pf-v5-c-success")
private WebElement emailSuccessMessage; private WebElement emailSuccessMessage;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement emailErrorMessage; private WebElement emailErrorMessage;
@FindBy(partialLinkText = "Back to Login") @FindBy(partialLinkText = "Back to Login")

View file

@ -35,7 +35,7 @@ public class LoginPasswordUpdatePage extends LogoutSessionsPage {
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(className = "kc-feedback-text") @FindBy(className = "kc-feedback-text")

View file

@ -41,7 +41,7 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(id = "input-error-otp-code") @FindBy(id = "input-error-otp-code")

View file

@ -40,16 +40,16 @@ public class LoginUpdateProfilePage extends AbstractPage {
@Page @Page
private UpdateProfileErrors errorsPage; private UpdateProfileErrors errorsPage;
@FindBy(id = "firstName") @FindBy(name = "firstName")
private WebElement firstNameInput; private WebElement firstNameInput;
@FindBy(id = "lastName") @FindBy(name = "lastName")
private WebElement lastNameInput; private WebElement lastNameInput;
@FindBy(id = "email") @FindBy(name = "email")
private WebElement emailInput; private WebElement emailInput;
@FindBy(id = "department") @FindBy(name = "department")
private WebElement departmentInput; private WebElement departmentInput;
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
@ -58,7 +58,7 @@ public class LoginUpdateProfilePage extends AbstractPage {
@FindBy(name = "cancel-aia") @FindBy(name = "cancel-aia")
private WebElement cancelAIAButton; private WebElement cancelAIAButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginAlertErrorMessage; private WebElement loginAlertErrorMessage;
public void update(String firstName, String lastName) { public void update(String firstName, String lastName) {

View file

@ -34,7 +34,7 @@ public class OAuth2DeviceVerificationPage extends LanguageComboboxAwarePage {
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(className = "pf-v5-c-alert")
private WebElement verifyErrorMessage; private WebElement verifyErrorMessage;
public void submit(String userCode) { public void submit(String userCode) {
@ -108,7 +108,7 @@ public class OAuth2DeviceVerificationPage extends LanguageComboboxAwarePage {
try { try {
driver.findElement(By.id("device-user-code")); driver.findElement(By.id("device-user-code"));
return driver.findElement(By.id("kc-page-title")).getText().equals("Device Login") return driver.findElement(By.id("kc-page-title")).getText().equals("Device Login")
&& driver.findElement(By.className("kc-feedback-text")).getText().equals("Invalid code, please try again."); && driver.findElement(By.className("pf-m-danger")).getText().equals("Invalid code, please try again.");
} catch (Throwable t) { } catch (Throwable t) {
} }
} }
@ -120,7 +120,7 @@ public class OAuth2DeviceVerificationPage extends LanguageComboboxAwarePage {
try { try {
driver.findElement(By.id("device-user-code")); driver.findElement(By.id("device-user-code"));
return driver.findElement(By.id("kc-page-title")).getText().equals("Device Login") return driver.findElement(By.id("kc-page-title")).getText().equals("Device Login")
&& driver.findElement(By.className("kc-feedback-text")).getText().equals("The code has expired. Please go back to your device and try connecting again."); && driver.findElement(By.className("pf-m-danger")).getText().equals("The code has expired. Please go back to your device and try connecting again.");
} catch (Throwable t) { } catch (Throwable t) {
} }
} }

View file

@ -27,7 +27,7 @@ public class PasswordPage extends LanguageComboboxAwarePage {
@FindBy(name = "login") @FindBy(name = "login")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(linkText = "Forgot Password?") @FindBy(linkText = "Forgot Password?")

View file

@ -44,34 +44,34 @@ public class RegisterPage extends AbstractPage {
@Page @Page
private PasswordFields.PasswordErrors passwordErrors; private PasswordFields.PasswordErrors passwordErrors;
@FindBy(id = "firstName") @FindBy(name = "firstName")
private WebElement firstNameInput; private WebElement firstNameInput;
@FindBy(id = "lastName") @FindBy(name = "lastName")
private WebElement lastNameInput; private WebElement lastNameInput;
@FindBy(id = "email") @FindBy(name = "email")
private WebElement emailInput; private WebElement emailInput;
@FindBy(id = "username") @FindBy(name = "username")
private WebElement usernameInput; private WebElement usernameInput;
@FindBy(id = "password") @FindBy(name = "password")
private WebElement passwordInput; private WebElement passwordInput;
@FindBy(id = "password-confirm") @FindBy(name = "password-confirm")
private WebElement passwordConfirmInput; private WebElement passwordConfirmInput;
@FindBy(id = "department") @FindBy(name = "department")
private WebElement departmentInput; private WebElement departmentInput;
@FindBy(id = "termsAccepted") @FindBy(name = "termsAccepted")
private WebElement termsAcceptedInput; private WebElement termsAcceptedInput;
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginAlertErrorMessage; private WebElement loginAlertErrorMessage;
@FindBy(className = "instruction") @FindBy(className = "instruction")
@ -136,7 +136,7 @@ public class RegisterPage extends AbstractPage {
if (attributes != null) { if (attributes != null) {
for (Entry<String, String> attribute : attributes.entrySet()) { for (Entry<String, String> attribute : attributes.entrySet()) {
driver.findElement(By.id(Constants.USER_ATTRIBUTES_PREFIX + attribute.getKey())).sendKeys(attribute.getValue()); driver.findElement(By.name(Constants.USER_ATTRIBUTES_PREFIX + attribute.getKey())).sendKeys(attribute.getValue());
} }
} }
@ -238,7 +238,7 @@ public class RegisterPage extends AbstractPage {
public boolean isDepartmentPresent() { public boolean isDepartmentPresent() {
try { try {
return driver.findElement(By.id("department")).isDisplayed(); return driver.findElement(By.name("department")).isDisplayed();
} catch (NoSuchElementException nse) { } catch (NoSuchElementException nse) {
return false; return false;
} }

View file

@ -9,19 +9,19 @@ import static org.keycloak.testsuite.util.UIUtils.clickLink;
public class UpdateAccountInformationPage extends LanguageComboboxAwarePage { public class UpdateAccountInformationPage extends LanguageComboboxAwarePage {
@FindBy(id = "username") @FindBy(name = "username")
private WebElement usernameInput; private WebElement usernameInput;
@FindBy(id = "email") @FindBy(name = "email")
private WebElement emailInput; private WebElement emailInput;
@FindBy(id = "firstName") @FindBy(name = "firstName")
private WebElement firstNameInput; private WebElement firstNameInput;
@FindBy(id = "lastName") @FindBy(name = "lastName")
private WebElement lastNameInput; private WebElement lastNameInput;
@FindBy(id = "department") @FindBy(name = "department")
private WebElement departmentInput; private WebElement departmentInput;
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
@ -106,7 +106,7 @@ public class UpdateAccountInformationPage extends LanguageComboboxAwarePage {
public boolean isDepartmentPresent() { public boolean isDepartmentPresent() {
try { try {
return driver.findElement(By.id("department")).isDisplayed(); return driver.findElement(By.name("department")).isDisplayed();
} catch (NoSuchElementException nse) { } catch (NoSuchElementException nse) {
return false; return false;
} }

View file

@ -33,23 +33,23 @@ public class VerifyProfilePage extends AbstractPage {
@Page @Page
private AccountFields.AccountErrors accountErrors; private AccountFields.AccountErrors accountErrors;
@FindBy(id = "firstName") @FindBy(name = "firstName")
private WebElement firstNameInput; private WebElement firstNameInput;
@FindBy(id = "lastName") @FindBy(name = "lastName")
private WebElement lastNameInput; private WebElement lastNameInput;
@FindBy(id = "email") @FindBy(name = "email")
private WebElement emailInput; private WebElement emailInput;
@FindBy(id = "department") @FindBy(name = "department")
private WebElement departmentInput; private WebElement departmentInput;
@FindBy(css = "input[type=\"submit\"]") @FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton; private WebElement submitButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginAlertErrorMessage; private WebElement loginAlertErrorMessage;

View file

@ -44,10 +44,10 @@ public class X509IdentityConfirmationPage extends LanguageComboboxAwarePage {
@FindBy(name = "cancel") @FindBy(name = "cancel")
private WebElement ignoreButton; private WebElement ignoreButton;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage; private WebElement loginErrorMessage;
@FindBy(className = "alert-warning") @FindBy(className = "pf-v5-c-warning")
private WebElement loginWarningMessage; private WebElement loginWarningMessage;
@FindBy(className = "alert-success") @FindBy(className = "alert-success")

View file

@ -345,19 +345,19 @@ public class OAuthClient {
public void linkUsers(String username, String password) { public void linkUsers(String username, String password) {
WaitUtils.waitForPageToLoad(); WaitUtils.waitForPageToLoad();
WebElement linkAccountButton = driver.findElement(By.id("linkAccount")); WebElement linkAccountButton = driver.findElement(By.name("linkAccount"));
waitUntilElement(linkAccountButton).is().clickable(); waitUntilElement(linkAccountButton).is().clickable();
linkAccountButton.click(); linkAccountButton.click();
WaitUtils.waitForPageToLoad(); WaitUtils.waitForPageToLoad();
WebElement usernameInput = driver.findElement(By.id("username")); WebElement usernameInput = driver.findElement(By.name("username"));
usernameInput.clear(); usernameInput.clear();
usernameInput.sendKeys(username); usernameInput.sendKeys(username);
WebElement passwordInput = driver.findElement(By.id("password")); WebElement passwordInput = driver.findElement(By.name("password"));
passwordInput.clear(); passwordInput.clear();
passwordInput.sendKeys(password); passwordInput.sendKeys(password);
WebElement loginButton = driver.findElement(By.id("kc-login")); WebElement loginButton = driver.findElement(By.name("kc-login"));
waitUntilElement(loginButton).is().clickable(); waitUntilElement(loginButton).is().clickable();
loginButton.click(); loginButton.click();
} }
@ -382,8 +382,8 @@ public class OAuthClient {
WaitUtils.waitForPageToLoad(); WaitUtils.waitForPageToLoad();
String src = driver.getPageSource(); String src = driver.getPageSource();
try { try {
driver.findElement(By.id("username")).sendKeys(username); driver.findElement(By.name("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password); driver.findElement(By.name("password")).sendKeys(password);
if (rememberMe) { if (rememberMe) {
driver.findElement(By.id("rememberMe")).click(); driver.findElement(By.id("rememberMe")).click();
} }
@ -396,19 +396,19 @@ public class OAuthClient {
private void updateAccountInformation(String username, String email, String firstName, String lastName) { private void updateAccountInformation(String username, String email, String firstName, String lastName) {
WebElement usernameInput = driver.findElement(By.id("username")); WebElement usernameInput = driver.findElement(By.name("username"));
usernameInput.clear(); usernameInput.clear();
usernameInput.sendKeys(username); usernameInput.sendKeys(username);
WebElement emailInput = driver.findElement(By.id("email")); WebElement emailInput = driver.findElement(By.name("email"));
emailInput.clear(); emailInput.clear();
emailInput.sendKeys(email); emailInput.sendKeys(email);
WebElement firstNameInput = driver.findElement(By.id("firstName")); WebElement firstNameInput = driver.findElement(By.name("firstName"));
firstNameInput.clear(); firstNameInput.clear();
firstNameInput.sendKeys(firstName); firstNameInput.sendKeys(firstName);
WebElement lastNameInput = driver.findElement(By.id("lastName")); WebElement lastNameInput = driver.findElement(By.name("lastName"));
lastNameInput.clear(); lastNameInput.clear();
lastNameInput.sendKeys(lastName); lastNameInput.sendKeys(lastName);

View file

@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.openqa.selenium.chrome.ChromeDriver;
import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver; import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
import static org.keycloak.testsuite.util.WaitUtils.log; import static org.keycloak.testsuite.util.WaitUtils.log;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad; import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
@ -230,7 +231,7 @@ public final class UIUtils {
} }
public static String getRawPageSource(WebDriver driver) { public static String getRawPageSource(WebDriver driver) {
if (driver instanceof FirefoxDriver) { if ((driver instanceof FirefoxDriver) || (driver instanceof ChromeDriver)) {
// firefox has some weird "bug" it wraps xml in html // firefox has some weird "bug" it wraps xml in html
return driver.findElement(By.tagName("body")).getText(); return driver.findElement(By.tagName("body")).getText();
} }

View file

@ -26,7 +26,7 @@ public class WebAuthnErrorPage extends LanguageComboboxAwarePage {
@FindBy(id = "cancelWebAuthnAIA") @FindBy(id = "cancelWebAuthnAIA")
private WebElement cancelRegistrationAIA; private WebElement cancelRegistrationAIA;
@FindBy(className = "alert-error") @FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement errorMessage; private WebElement errorMessage;
public void clickTryAgain() { public void clickTryAgain() {

View file

@ -159,7 +159,9 @@ public abstract class AbstractKeycloakTest {
private boolean resetTimeOffset; private boolean resetTimeOffset;
private static String PREFERRED_DEFAULT_LOGIN_THEME = System.getProperty("login.theme.default"); public static final String PROPERTY_LOGIN_THEME_DEFAULT = "login.theme.default";
public static final String PREFERRED_DEFAULT_LOGIN_THEME = System.getProperty(PROPERTY_LOGIN_THEME_DEFAULT);
@Before @Before
public void beforeAbstractKeycloakTest() throws Exception { public void beforeAbstractKeycloakTest() throws Exception {

View file

@ -173,7 +173,7 @@ public class AppInitiatedActionUpdateEmailWithVerificationTest extends AbstractA
assertEquals(String.format("The account email has been successfully updated to %s.", "new@localhost"), infoPage.getInfo()); assertEquals(String.format("The account email has been successfully updated to %s.", "new@localhost"), infoPage.getInfo());
//Issue #15136 //Issue #15136
final WebElement backToApplicationLink = driver.findElement(By.linkText("« Back to Application")); final WebElement backToApplicationLink = driver.findElement(By.linkText("« Back to Application"));
assertThat(backToApplicationLink.toString(), Matchers.containsString("/auth/realms/master/app/auth")); assertThat(backToApplicationLink.getDomAttribute("href"), Matchers.containsString("/auth/realms/master/app/auth"));
events.expect(EventType.UPDATE_EMAIL) events.expect(EventType.UPDATE_EMAIL)
.detail(Details.PREVIOUS_EMAIL, "test-user@localhost") .detail(Details.PREVIOUS_EMAIL, "test-user@localhost")

View file

@ -16,10 +16,8 @@
*/ */
package org.keycloak.testsuite.actions; package org.keycloak.testsuite.actions;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN; import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER; import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
@ -60,7 +58,10 @@ import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
import org.keycloak.testsuite.util.UserBuilder; import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.userprofile.UserProfileContext; import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.utils.StringUtil; import org.keycloak.utils.StringUtil;
import java.util.HashSet;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.htmlunit.HtmlUnitDriver; import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import static org.hamcrest.MatcherAssert.assertThat;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -457,6 +458,7 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
UPConfig testUpConfig = configuration.clone(); UPConfig testUpConfig = configuration.clone();
List<String> attributes = List.of("foo", "bar", "zar"); List<String> attributes = List.of("foo", "bar", "zar");
List<String> values = IntStream.range(0, 5).mapToObj(Integer::toString).collect(Collectors.toList()); List<String> values = IntStream.range(0, 5).mapToObj(Integer::toString).collect(Collectors.toList());
Set<String> valuesSet = new HashSet<>(values);
for (String attribute : attributes) { for (String attribute : attributes) {
testUpConfig.addOrReplaceAttribute( testUpConfig.addOrReplaceAttribute(
@ -487,17 +489,24 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name())); userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
testRealm().users().get(userRep.getId()).update(userRep); testRealm().users().get(userRep.getId()).update(userRep);
loginPage.open(); loginPage.open();
for (String value : values) { assertThat(IntStream.range(0, 5).mapToObj(value -> updateProfilePage.getAttribute(attribute + "-" + value)).collect(Collectors.toSet()), Matchers.equalTo(valuesSet));
assertEquals(value, updateProfilePage.getAttribute(attribute + "-" + value));
} final String lastValue = values.get(values.size() - 1);
// remove multiple values, only the last value should be kept as you can't remove the last one // remove multiple values, only the last value should be kept as you can't remove the last one
for (String value : values) { for (String value : values) {
updateProfilePage.clickRemoveAttributeValue(attribute + "-0"); try {
updateProfilePage.clickRemoveAttributeValue(attribute + "-0");
} catch (ElementClickInterceptedException e) {
if (! lastValue.equals(value)) { // Ignore that the last value cannot be clicked / removed - this is by design
throw e;
}
}
} }
updateProfilePage.update("f", "l", "e@keycloak.org"); updateProfilePage.update("f", "l", "e@keycloak.org");
userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost"); userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
assertThat(userRep.getAttributes().get(attribute), Matchers.containsInAnyOrder(values.get(values.size() - 1))); assertThat(userRep.getAttributes().get(attribute), Matchers.hasSize(1));
assertThat(userRep.getAttributes().get(attribute).get(0), Matchers.in(values));
// make sure adding/removing within the same context works // make sure adding/removing within the same context works
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name())); userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
@ -508,19 +517,26 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
updateProfilePage.setAttribute(elementId, value); updateProfilePage.setAttribute(elementId, value);
updateProfilePage.clickAddAttributeValue(elementId); updateProfilePage.clickAddAttributeValue(elementId);
} }
assertThat(IntStream.range(0, 5).mapToObj(value -> updateProfilePage.getAttribute(attribute + "-" + value)).collect(Collectors.toSet()), Matchers.equalTo(valuesSet));
for (String value : values) { for (String value : values) {
assertEquals(value, updateProfilePage.getAttribute(attribute + "-" + value)); try {
} updateProfilePage.clickRemoveAttributeValue(attribute + "-0");
for (String value : values) { } catch (ElementClickInterceptedException e) {
updateProfilePage.clickRemoveAttributeValue(attribute + "-0"); if (! lastValue.equals(value)) { // Ignore that the last value cannot be clicked / removed - this is by design
throw e;
}
}
} }
// make sure the last attribute is set with a value // make sure the last attribute is set with a value
if (StringUtil.isBlank(updateProfilePage.getAttribute(attribute + "-0"))) { if (StringUtil.isBlank(updateProfilePage.getAttribute(attribute + "-0"))) {
updateProfilePage.setAttribute(attribute + "-0", values.get(values.size() - 1)); updateProfilePage.setAttribute(attribute + "-0", lastValue);
} }
updateProfilePage.update("f", "l", "e@keycloak.org"); updateProfilePage.update("f", "l", "e@keycloak.org");
userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost"); userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
assertThat(userRep.getAttributes().get(attribute), Matchers.containsInAnyOrder(values.get(values.size() - 1))); assertThat(userRep.getAttributes().get(attribute), Matchers.hasSize(1));
assertThat(userRep.getAttributes().get(attribute).get(0), Matchers.in(values));
// at the end the attribute is set with multiple values // at the end the attribute is set with multiple values
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name())); userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));

View file

@ -33,6 +33,7 @@ import static org.keycloak.testsuite.forms.VerifyProfileTest.CONFIGURATION_FOR_U
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
@ -61,6 +62,7 @@ import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.testsuite.util.KeycloakModelUtils; import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.UserBuilder; import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
/** /**
* Test update-profile required action with custom user profile configurations * Test update-profile required action with custom user profile configurations
@ -107,7 +109,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
@Before @Before
public void beforeTest() { public void beforeTest() {
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null); VerifyProfileTest.setUserProfileConfiguration(testRealm(), null);
ApiUtil.removeUserByUsername(testRealm(), "test-user@localhost"); ApiUtil.removeUserByUsername(testRealm(), "test-user@localhost");
UserRepresentation user = UserBuilder.create().enabled(true) UserRepresentation user = UserBuilder.create().enabled(true)
@ -146,11 +148,11 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
//assert field names //assert field names
// i18n replaced // i18n replaced
Assert.assertEquals("First name",updateProfilePage.getLabelForField("firstName")); Assert.assertEquals("First name", updateProfilePage.getLabelForField("firstName"));
// attribute name used if no display name set // attribute name used if no display name set
Assert.assertEquals("lastName",updateProfilePage.getLabelForField("lastName")); Assert.assertEquals("lastName", updateProfilePage.getLabelForField("lastName"));
// direct value in display name // direct value in display name
Assert.assertEquals("Department",updateProfilePage.getLabelForField("department")); Assert.assertEquals("Department", updateProfilePage.getLabelForField("department"));
} }
@ -172,49 +174,22 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
loginPage.login(USERNAME1, PASSWORD); loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent(); updateProfilePage.assertCurrent();
String htmlFormId="kc-update-profile-form";
//assert fields and groups location in form, attributes without a group appear first //assert fields and groups location in form, attributes without a group appear first
Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-update-profile-form label"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "username", "firstName", "header-company", "description-company", "department", "header-contact", "email"};
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < element.size(); i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id;
Assert.assertTrue( if (webElement.getAttribute("for") != null) {
driver.findElement( id = webElement.getAttribute("for");
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") // see that the label has an element it belongs to
).isDisplayed() assertThat("Label with id: " + id + " should have component it belongs to", driver.findElement(By.id(id)).isDisplayed(), is(true));
); } else {
Assert.assertTrue( id = webElement.getAttribute("id");
driver.findElement( }
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") assertThat("Label at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
).isDisplayed() }
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@ -235,31 +210,14 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
updateProfilePage.assertCurrent(); updateProfilePage.assertCurrent();
//assert fields location in form //assert fields location in form
Assert.assertTrue( //assert fields and groups location in form, attributes without a group appear first
driver.findElement( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-update-profile-form input"));
By.cssSelector("form#kc-update-profile-form > div:nth-child(1) > div:nth-child(2) > input#lastName") String[] labelOrder = new String[]{"lastName", "department", "username", "firstName", "email"};
).isDisplayed() for (int i = 0; i < labelOrder.length; i++) {
); WebElement webElement = element.get(i);
Assert.assertTrue( String id = webElement.getAttribute("id");
driver.findElement( assertThat("Field at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
By.cssSelector("form#kc-update-profile-form > div:nth-child(2) > div:nth-child(2) > input#department") }
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(4) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(5) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test
@ -343,7 +301,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL +","+VALIDATIONS_LENGTH + "}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "," + VALIDATIONS_LENGTH + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ADMIN_ONLY + "}" + "{\"name\": \"department\"," + PERMISSIONS_ADMIN_ONLY + "}"
+ "]}"); + "]}");
@ -436,7 +394,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
updateProfilePage.assertCurrent(); updateProfilePage.assertCurrent();
Assert.assertEquals("Brady", updateProfilePage.getLastName()); Assert.assertEquals("Brady", updateProfilePage.getLastName());
Assert.assertFalse("'department' field is visible" , updateProfilePage.isDepartmentPresent()); Assert.assertFalse("'department' field is visible", updateProfilePage.isDepartmentPresent());
//update of the other attributes must be successful in this case //update of the other attributes must be successful in this case
updateProfilePage.prepareUpdate().username(USERNAME1).firstName("First").lastName("Last").email(USERNAME1).submit(); updateProfilePage.prepareUpdate().username(USERNAME1).firstName("First").lastName("Last").email(USERNAME1).submit();
@ -494,7 +452,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}" + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\"" + SCOPE_DEPARTMENT + "\"]}}"
+ "]}"); + "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm(); oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
@ -534,7 +492,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}" + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\"" + SCOPE_DEPARTMENT + "\"]}}"
+ "]}"); + "]}");
oauth.clientId(client_scope_default.getClientId()).openLoginForm(); oauth.clientId(client_scope_default.getClientId()).openLoginForm();
@ -568,7 +526,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}" + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"" + SCOPE_DEPARTMENT + "\"]}}"
+ "]}"); + "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm(); oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
@ -602,7 +560,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}" + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\"" + SCOPE_DEPARTMENT + "\"]}}"
+ "]}"); + "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm(); oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
@ -631,7 +589,7 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTest
setUserProfileConfiguration("{\"attributes\": [" setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}}," + "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}" + "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"" + SCOPE_DEPARTMENT + "\"]}}"
+ "]}"); + "]}");
oauth.clientId(client_scope_optional.getClientId()).openLoginForm(); oauth.clientId(client_scope_optional.getClientId()).openLoginForm();

View file

@ -1897,7 +1897,7 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
waitForPageToLoad(); waitForPageToLoad();
assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage); // we are still in login assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage); // we are still in login
Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.",
UIUtils.getTextFromElement(driver.findElement(By.className("alert-error")))); UIUtils.getTextFromElement(driver.findElement(By.cssSelector("div[class^='pf-v5-c-alert'], div[class^='alert-error']"))));
// login successfully in tab2 after the error // login successfully in tab2 after the error
loginPage.form().login(bburkeUser); loginPage.form().login(bburkeUser);

View file

@ -30,6 +30,7 @@ import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder; import org.keycloak.testsuite.util.UserBuilder;
import static org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID;
public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest { public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
@ -52,6 +53,10 @@ public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
@Before @Before
public void createHttpClient() throws Exception { public void createHttpClient() throws Exception {
client = HttpClientBuilder.create().build(); client = HttpClientBuilder.create().build();
getCleanup().addCleanup(ClientAttributeUpdater.forClient(adminClient, "master", ADMIN_CLI_CLIENT_ID).setAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR, "true").update());
getCleanup().addCleanup(ClientAttributeUpdater.forClient(adminClient, REALM_I18N_OFF, ADMIN_CLI_CLIENT_ID).setAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR, "true").update());
getCleanup().addCleanup(ClientAttributeUpdater.forClient(adminClient, REALM_I18N_ON, ADMIN_CLI_CLIENT_ID).setAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR, "true").update());
} }
@After @After
@ -101,17 +106,7 @@ public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
} }
private OAuthClient.AccessTokenResponse accessToken(String realmName, String username, String password) throws Exception { private OAuthClient.AccessTokenResponse accessToken(String realmName, String username, String password) throws Exception {
String codeVerifier = PkceUtils.generateCodeVerifier(); return oauth.doGrantAccessTokenRequest(realmName, username, password, null, ADMIN_CLI_CLIENT_ID, null);
oauth.realm(realmName)
.codeVerifier(codeVerifier)
.codeChallenge(PkceUtils.generateS256CodeChallenge(codeVerifier))
.codeChallengeMethod(OAuth2Constants.PKCE_METHOD_S256)
.clientId(Constants.ADMIN_CONSOLE_CLIENT_ID)
.redirectUri(adminConsole.createUriBuilder().build(realmName).toASCIIString());
oauth.doLogin(username, password);
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, null);
return response;
} }
private String whoAmiUrl(String realmName) { private String whoAmiUrl(String realmName) {
@ -139,7 +134,7 @@ public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
@Test @Test
public void testLocaleRealmI18nDisabledUserWithoutLocale() throws Exception { public void testLocaleRealmI18nDisabledUserWithoutLocale() throws Exception {
OAuthClient.AccessTokenResponse response = accessToken(REALM_I18N_OFF, USER_WITHOUT_LOCALE, PASSWORD); OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(REALM_I18N_OFF, USER_WITHOUT_LOCALE, PASSWORD, null, ADMIN_CLI_CLIENT_ID, null);
JsonNode whoAmI = SimpleHttpDefault JsonNode whoAmI = SimpleHttpDefault
.doGet(whoAmiUrl(REALM_I18N_OFF), client) .doGet(whoAmiUrl(REALM_I18N_OFF), client)
.header("Accept", "application/json") .header("Accept", "application/json")
@ -276,7 +271,8 @@ public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
@Test @Test
public void testLocaleRealmTokenForOtherClient() throws Exception { public void testLocaleRealmTokenForOtherClient() throws Exception {
try (Keycloak adminCliClient = AdminClientUtil.createAdminClient(true, REALM_I18N_ON, try (var c = ClientAttributeUpdater.forClient(adminClient, REALM_I18N_ON, ADMIN_CLI_CLIENT_ID).setAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR, null).update();
Keycloak adminCliClient = AdminClientUtil.createAdminClient(true, REALM_I18N_ON,
USER_WITH_LOCALE, PASSWORD, Constants.ADMIN_CLI_CLIENT_ID, null)) { USER_WITH_LOCALE, PASSWORD, Constants.ADMIN_CLI_CLIENT_ID, null)) {
AccessTokenResponse accessToken = adminCliClient.tokenManager().getAccessToken(); AccessTokenResponse accessToken = adminCliClient.tokenManager().getAccessToken();
Assert.assertNotNull(accessToken); Assert.assertNotNull(accessToken);

View file

@ -120,7 +120,7 @@ public abstract class AbstractDefaultIdpTest extends AbstractInitializedBaseBrok
waitForPage(driver, "sign in to", true); waitForPage(driver, "sign in to", true);
WebElement errorElement = driver.findElement(By.className("alert-error")); WebElement errorElement = driver.findElement(By.className("pf-v5-c-alert"));
assertNotNull("Page should show an error message but it's missing", errorElement); assertNotNull("Page should show an error message but it's missing", errorElement);
// Login to IDP failed due consent denied. Error message is displayed on the username/password screen of the consumer realm // Login to IDP failed due consent denied. Error message is displayed on the username/password screen of the consumer realm

View file

@ -1,6 +1,7 @@
package org.keycloak.testsuite.broker; package org.keycloak.testsuite.broker;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hamcrest.CoreMatchers;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.IdentityProviderResource;
@ -27,6 +28,7 @@ import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -391,7 +393,7 @@ public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
updateAccountInformationPage.assertCurrent(); updateAccountInformationPage.assertCurrent();
assertEquals("Please specify username.", loginUpdateProfilePage.getInputErrors().getUsernameError()); assertEquals("Please specify username.", loginUpdateProfilePage.getInputErrors().getUsernameError());
updateAccountInformationPage.updateAccountInformation("new-username", "no-first-name@localhost.com", "First Name", "Last Name"); updateAccountInformationPage.updateAccountInformation("new-username", "no-first-name@localhost.com", "First Name", "Last Name");
UserRepresentation userRepresentation = AccountHelper.getUserRepresentation(adminClient.realm(bc.consumerRealmName()), "new-username"); UserRepresentation userRepresentation = AccountHelper.getUserRepresentation(adminClient.realm(bc.consumerRealmName()), "new-username");
@ -498,46 +500,20 @@ public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
String htmlFormId = "kc-idp-review-profile-form"; String htmlFormId = "kc-idp-review-profile-form";
//assert fields and groups location in form, attributes without a group appear first //assert fields and groups location in form, attributes without a group appear first
org.junit.Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-idp-review-profile-form label"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "username", "firstName", "header-company", "description-company", "department", "header-contact", "email"};
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < element.size(); i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id;
org.junit.Assert.assertTrue( if (webElement.getAttribute("for") != null) {
driver.findElement( id = webElement.getAttribute("for");
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") // see that the label has an element it belongs to
).isDisplayed() assertThat("Label with id: " + id + " should have component it belongs to", driver.findElement(By.id(id)).isDisplayed(), is(true));
); } else {
org.junit.Assert.assertTrue( id = webElement.getAttribute("id");
driver.findElement( }
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") assertThat("Label at index: " + i + " with id: " + id + " was not in found in the same order in the dom", labelOrder[i], is(id));
).isDisplayed() }
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test
@ -562,32 +538,13 @@ public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
updateAccountInformationPage.assertCurrent(); updateAccountInformationPage.assertCurrent();
//assert fields location in form //assert fields location in form
String htmlFormId = "kc-idp-review-profile-form"; List<WebElement> element = driver.findElements(By.cssSelector("form#kc-idp-review-profile-form input"));
org.junit.Assert.assertTrue( String[] labelOrder = new String[]{"lastName", "department", "username", "firstName", "email"};
driver.findElement( for (int i = 0; i < labelOrder.length; i++) {
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") WebElement webElement = element.get(i);
).isDisplayed() String id = webElement.getAttribute("id");
); assertThat("Field at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
org.junit.Assert.assertTrue( }
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
org.junit.Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test

View file

@ -36,7 +36,9 @@ import org.keycloak.testsuite.util.BrowserTabUtil;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.keycloak.testsuite.AssertEvents.DEFAULT_REDIRECT_URI; import static org.keycloak.testsuite.AssertEvents.DEFAULT_REDIRECT_URI;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
@ -62,6 +64,7 @@ public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBroker
// Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved
@Test @Test
public void testAuthenticationExpiredWithMoreBrowserTabs_clickIdpLoginInTab1AfterExpiration() { public void testAuthenticationExpiredWithMoreBrowserTabs_clickIdpLoginInTab1AfterExpiration() {
assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver);
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
oauth.clientId("broker-app"); oauth.clientId("broker-app");
loginPage.open(bc.consumerRealmName()); loginPage.open(bc.consumerRealmName());
@ -100,6 +103,7 @@ public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBroker
// Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved
@Test @Test
public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() { public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() {
assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver);
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
// Open login page in tab1 and click "login with IDP" // Open login page in tab1 and click "login with IDP"
oauth.clientId("broker-app"); oauth.clientId("broker-app");
@ -166,6 +170,7 @@ public class KcOidcMultipleTabsBrokerTest extends AbstractInitializedBaseBroker
@Test @Test
public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception { public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception {
assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver);
// Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP. // Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP.
// So need to increase authSession timeout on "consumer" // So need to increase authSession timeout on "consumer"
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver); try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver);

View file

@ -32,7 +32,9 @@ import org.keycloak.testsuite.util.BrowserTabUtil;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
/** /**
@ -58,6 +60,7 @@ public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerT
// Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved // Similar to MultipleTabsLoginTest.multipleTabsParallelLogin but with IDP brokering test involved
@Test @Test
public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() { public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInBothConsumerAndProvider() {
assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver);
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) { try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver)) {
// Open login page in tab1 and click "login with IDP" // Open login page in tab1 and click "login with IDP"
oauth.clientId("broker-app"); oauth.clientId("broker-app");
@ -120,6 +123,7 @@ public class KcSamlMultipleTabsBrokerTest extends AbstractInitializedBaseBrokerT
@Test @Test
public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception { public void testAuthenticationExpiredWithMoreBrowserTabs_loginExpiredInProvider() throws Exception {
assumeTrue("Since the JS engine in real browser does check the expiration regularly in all tabs, this test only works with HtmlUnit", driver instanceof HtmlUnitDriver);
// Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP. // Testing the scenario when authenticationSession expired only in "provider" realm and "consumer" is able to handle it at IDP.
// So need to increase authSession timeout on "consumer" // So need to increase authSession timeout on "consumer"
try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver); try (BrowserTabUtil tabUtil = BrowserTabUtil.getInstanceAndSetEnv(driver);

View file

@ -151,7 +151,7 @@ public abstract class AbstractFailoverClusterTest extends AbstractClusterTest {
protected Cookie verifyLoggedIn(Cookie sessionCookieForVerification) { protected Cookie verifyLoggedIn(Cookie sessionCookieForVerification) {
// verify on realm path // verify on realm path
URLUtils.navigateToUri(AUTH_SERVER_ROOT + "/realms/test"); URLUtils.navigateToUri(AUTH_SERVER_ROOT + "/realms/test/");
Cookie sessionCookieOnRealmPath = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE); Cookie sessionCookieOnRealmPath = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNotNull(sessionCookieOnRealmPath); assertNotNull(sessionCookieOnRealmPath);
assertEquals(sessionCookieOnRealmPath.getValue(), sessionCookieForVerification.getValue()); assertEquals(sessionCookieOnRealmPath.getValue(), sessionCookieForVerification.getValue());

View file

@ -223,6 +223,8 @@ public class UncaughtErrorPageTest extends AbstractKeycloakTest {
try { try {
checkPageNotFound("/auth/realms/master/nosuch"); checkPageNotFound("/auth/realms/master/nosuch");
// Once keycloak.v2 would be the default login theme, uncomment the following line and remove the one below:
// String url = driver.findElement(By.xpath("//option[text()[contains(.,'Deutsch')]]")).getAttribute("value");
String url = driver.findElement(By.xpath("//a[text()[contains(.,'Deutsch')]]")).getAttribute("href"); String url = driver.findElement(By.xpath("//a[text()[contains(.,'Deutsch')]]")).getAttribute("href");
driver.navigate().to(url); driver.navigate().to(url);
errorPage.assertCurrent(); errorPage.assertCurrent();

View file

@ -55,6 +55,7 @@ import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.KeycloakModelUtils; import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
/** /**
* Test user registration with customized user-profile configurations * Test user registration with customized user-profile configurations
@ -288,41 +289,13 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
registerPage.assertCurrent(); registerPage.assertCurrent();
//assert fields location in form //assert fields location in form
Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-register-form input"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "department", "username", "password", "password-confirm", "firstName", "email"};
By.cssSelector("form#kc-register-form > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < labelOrder.length; i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id = webElement.getAttribute("id");
Assert.assertTrue( assertThat("Field at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
driver.findElement( }
By.cssSelector("form#kc-register-form > div:nth-child(2) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("#password")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("#password-confirm")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(6) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-register-form > div:nth-child(7) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
public static final String UP_CONFIG_PART_INPUT_TYPES = "{\"name\": \"defaultType\"," + VerifyProfileTest.PERMISSIONS_ALL + "}," public static final String UP_CONFIG_PART_INPUT_TYPES = "{\"name\": \"defaultType\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
@ -440,50 +413,22 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
loginPage.clickRegister(); loginPage.clickRegister();
registerPage.assertCurrent(); registerPage.assertCurrent();
String htmlFormId="kc-register-form";
//assert fields and groups location in form, attributes without a group appear first //assert fields and groups location in form, attributes without a group appear first
Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-register-form label"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "username", "password", "password-confirm", "firstName", "header-company", "description-company", "department", "header-contact", "email"};
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < element.size(); i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id;
Assert.assertTrue( if (webElement.getAttribute("for") != null) {
driver.findElement( id = webElement.getAttribute("for");
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") // see that the label has an element it belongs to
).isDisplayed() assertThat("Label with id: " + id + " should have component it belongs to", driver.findElement(By.id(id)).isDisplayed(), is(true));
); } else {
// password and password confirmation fields appear after the username field, in positions 3 and 4 id = webElement.getAttribute("id");
Assert.assertTrue( }
driver.findElement( assertThat("Label at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#firstName") }
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > label#description-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(8) > div:nth-child(1) > label#header-contact")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(9) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test
@ -702,4 +647,4 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
private UserRepresentation getUser(String userId) { private UserRepresentation getUser(String userId) {
return testRealm().users().get(userId).toRepresentation(); return testRealm().users().get(userId).toRepresentation();
} }
} }

View file

@ -7,10 +7,15 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.theme.ThemeSelectorProvider;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest { public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
// At this moment, the default login theme as defined in ThemeSelectorProvider is "keycloak".
// Once the ThemeSelectorProvider.DEFAULT_V2 becomes default, this needs to be reflected in this constant.
private static final String SYSTEM_DEFAULT_LOGIN_THEME = ThemeSelectorProvider.DEFAULT;
@Page @Page
protected LoginPage loginPage; protected LoginPage loginPage;
@ -21,7 +26,7 @@ public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
@Test @Test
public void clientOverride() { public void clientOverride() {
loginPage.open(); loginPage.open();
assertEquals("keycloak", detectTheme()); assertEquals(System.getProperty(PROPERTY_LOGIN_THEME_DEFAULT, SYSTEM_DEFAULT_LOGIN_THEME), detectTheme());
ClientRepresentation rep = testRealm().clients().findByClientId("test-app").get(0); ClientRepresentation rep = testRealm().clients().findByClientId("test-app").get(0);
@ -37,7 +42,7 @@ public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
testRealm().clients().get(rep.getId()).update(rep); testRealm().clients().get(rep.getId()).update(rep);
loginPage.open(); loginPage.open();
assertEquals("keycloak", detectTheme()); assertEquals(SYSTEM_DEFAULT_LOGIN_THEME, detectTheme());
} finally { } finally {
rep.getAttributes().put("login_theme", ""); rep.getAttributes().put("login_theme", "");
testRealm().clients().get(rep.getId()).update(rep); testRealm().clients().get(rep.getId()).update(rep);
@ -48,6 +53,8 @@ public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
// for the purpose of the test does not matter which profile is used (product or community) // for the purpose of the test does not matter which profile is used (product or community)
if(driver.getPageSource().contains("/login/keycloak/css/login.css") || driver.getPageSource().contains("/login/rh-sso/css/login.css")) { if(driver.getPageSource().contains("/login/keycloak/css/login.css") || driver.getPageSource().contains("/login/rh-sso/css/login.css")) {
return "keycloak"; return "keycloak";
} else if (driver.getPageSource().contains("/login/keycloak.v2/css/styles.css") || driver.getPageSource().contains("/login/rh-sso/css/styles.css")) {
return "keycloak.v2";
} else { } else {
return "base"; return "base";
} }

View file

@ -73,6 +73,7 @@ import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.userprofile.UserProfileContext; import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
/** /**
* @author Vlastimil Elias <velias@redhat.com> * @author Vlastimil Elias <velias@redhat.com>
@ -218,49 +219,22 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
loginPage.login("login-test5", "password"); loginPage.login("login-test5", "password");
verifyProfilePage.assertCurrent(); verifyProfilePage.assertCurrent();
String htmlFormId="kc-update-profile-form";
//assert fields and groups location in form, attributes without a group appear first //assert fields and groups location in form, attributes without a group appear first
Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-update-profile-form label"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "username", "firstName", "header-company", "description-company", "department", "header-contact", "email"};
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < element.size(); i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id;
Assert.assertTrue( if (webElement.getAttribute("for") != null) {
driver.findElement( id = webElement.getAttribute("for");
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") // see that the label has an element it belongs to
).isDisplayed() assertThat("Label with id: " + id + " should have component it belongs to", driver.findElement(By.id(id)).isDisplayed(), is(true));
); } else {
Assert.assertTrue( id = webElement.getAttribute("id");
driver.findElement( }
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") assertThat("Label at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
).isDisplayed() }
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test
@ -287,31 +261,13 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
verifyProfilePage.assertCurrent(); verifyProfilePage.assertCurrent();
//assert fields location in form //assert fields location in form
Assert.assertTrue( List<WebElement> element = driver.findElements(By.cssSelector("form#kc-update-profile-form input"));
driver.findElement( String[] labelOrder = new String[]{"lastName", "department", "username", "firstName", "email"};
By.cssSelector("form#kc-update-profile-form > div:nth-child(1) > div:nth-child(2) > input#lastName") for (int i = 0; i < labelOrder.length; i++) {
).isDisplayed() WebElement webElement = element.get(i);
); String id = webElement.getAttribute("id");
Assert.assertTrue( assertThat("Field at index: " + i + " with id: " + id + " was not in found in the same order in the dom", id, is(labelOrder[i]));
driver.findElement( }
By.cssSelector("form#kc-update-profile-form > div:nth-child(2) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(4) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(5) > div:nth-child(2) > input#email")
).isDisplayed()
);
} }
@Test @Test

View file

@ -167,7 +167,7 @@ public class LoginPageTest extends AbstractI18NTest {
@Test @Test
public void testIdentityProviderCapitalization(){ public void testIdentityProviderCapitalization(){
loginPage.open(); loginPage.open();
assertEquals("GitHub", loginPage.findSocialButton("github").getText()); assertEquals("", loginPage.findSocialButton("github").getText()); // github is only displayed as an icon in keycloak.v2 theme
assertEquals("mysaml", loginPage.findSocialButton("mysaml").getText()); assertEquals("mysaml", loginPage.findSocialButton("mysaml").getText());
assertEquals("MyOIDC", loginPage.findSocialButton("myoidc").getText()); assertEquals("MyOIDC", loginPage.findSocialButton("myoidc").getText());
} }

View file

@ -118,6 +118,7 @@ import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS; import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
import static org.keycloak.models.AccountRoles.VIEW_GROUPS; import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
import static org.keycloak.testsuite.AbstractKeycloakTest.PREFERRED_DEFAULT_LOGIN_THEME;
import static org.keycloak.testsuite.Assert.assertNames; import static org.keycloak.testsuite.Assert.assertNames;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY; import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY;
@ -174,7 +175,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
protected void testRhssoThemes(RealmResource realm) { protected void testRhssoThemes(RealmResource realm) {
// check themes are removed // check themes are removed
RealmRepresentation rep = realm.toRepresentation(); RealmRepresentation rep = realm.toRepresentation();
Assert.assertNull("Login theme was not modified", rep.getLoginTheme()); assertThat("Login theme modified for test purposes", rep.getLoginTheme(), anyOf(nullValue(), equalTo(PREFERRED_DEFAULT_LOGIN_THEME)));
Assert.assertNull("Email theme was not modified", rep.getEmailTheme()); Assert.assertNull("Email theme was not modified", rep.getEmailTheme());
// there should be either new default or left null if not set // there should be either new default or left null if not set
assertThat("Account theme was not modified", rep.getAccountTheme(), anyOf(equalTo("keycloak.v2"), nullValue())); assertThat("Account theme was not modified", rep.getAccountTheme(), anyOf(equalTo("keycloak.v2"), nullValue()));

View file

@ -460,8 +460,9 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
oauth.fillLoginForm("alice", "alice"); oauth.fillLoginForm("alice", "alice");
String aliceCode = oauth.getCurrentQuery().get(OAuth2Constants.CODE); String aliceCode = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
WebClient webClient = DroneHtmlUnitDriver.class.cast(driver).getWebClient(); // WebClient webClient = DroneHtmlUnitDriver.class.cast(driver).getWebClient();
webClient.getCookieManager().clearCookies(); // webClient.getCookieManager().clearCookies();
driver.manage().deleteAllCookies();
oauth.openLoginForm(); oauth.openLoginForm();
driver.manage().addCookie(authSessionCookie); driver.manage().addCookie(authSessionCookie);
oauth.fillLoginForm("bob", "bob"); oauth.fillLoginForm("bob", "bob");

View file

@ -50,6 +50,7 @@ import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.updaters.IdentityProviderCreator; import org.keycloak.testsuite.updaters.IdentityProviderCreator;
import org.keycloak.testsuite.util.IdentityProviderBuilder; import org.keycloak.testsuite.util.IdentityProviderBuilder;
import static org.hamcrest.Matchers.anyOf;
import static org.keycloak.testsuite.util.Matchers.bodyHC; import static org.keycloak.testsuite.util.Matchers.bodyHC;
import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC; import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
@ -197,9 +198,12 @@ public class IdpInitiatedLoginTest extends AbstractSamlTest {
.execute(r -> { .execute(r -> {
assertThat(r, statusCodeIsHC(Response.Status.OK)); assertThat(r, statusCodeIsHC(Response.Status.OK));
assertThat(r, bodyHC(allOf( assertThat(r, bodyHC(allOf(
containsString("Redirecting, please wait."), anyOf(
containsString("Redirecting, please wait."),
containsString("Authentication Redirect")
),
containsString("<input type=\"hidden\" name=\"SAMLResponse\""), containsString("<input type=\"hidden\" name=\"SAMLResponse\""),
containsString("<h1 id=\"kc-page-title\">") containsString(" id=\"kc-page-title\"")
))); )));
}); });
} }
@ -219,9 +223,13 @@ public class IdpInitiatedLoginTest extends AbstractSamlTest {
.execute(r -> { .execute(r -> {
assertThat(r, statusCodeIsHC(Response.Status.OK)); assertThat(r, statusCodeIsHC(Response.Status.OK));
assertThat(r, bodyHC(allOf( assertThat(r, bodyHC(allOf(
anyOf(
containsString("Redirecting, please wait."),
containsString("Authentication Redirect")
),
containsString("Redirecting, please wait."), containsString("Redirecting, please wait."),
containsString("<input type=\"hidden\" name=\"SAMLRequest\""), containsString("<input type=\"hidden\" name=\"SAMLRequest\""),
containsString("<h1 id=\"kc-page-title\">") containsString(" id=\"kc-page-title\"")
))); )));
}); });
} }

View file

@ -210,7 +210,7 @@
<browser.strict.cookies>false</browser.strict.cookies> <browser.strict.cookies>false</browser.strict.cookies>
<webdriverDownloadBinaries>true</webdriverDownloadBinaries> <webdriverDownloadBinaries>true</webdriverDownloadBinaries>
<droneInstantiationTimeoutInSeconds>60</droneInstantiationTimeoutInSeconds> <droneInstantiationTimeoutInSeconds>60</droneInstantiationTimeoutInSeconds>
<login.theme.default/> <login.theme.default>keycloak.v2</login.theme.default>
<github.username/> <github.username/>
<github.secretToken/> <github.secretToken/>
<ieDriverArch>Win32</ieDriverArch> <ieDriverArch>Win32</ieDriverArch>