KEYCLOAK-7925 Initial tests for the new Account Console

This commit is contained in:
vmuzikar 2018-08-24 18:26:26 +02:00 committed by Pavel Drozd
parent c56e171f3a
commit bd8510f4da
30 changed files with 1791 additions and 115 deletions

View file

@ -16,13 +16,13 @@
*/
package org.keycloak.testsuite.auth.page.account.fragment;
import org.keycloak.testsuite.page.AbstractAlert;
import org.keycloak.testsuite.page.AbstractPatternFlyAlert;
/**
*
* @author tkyjovsk
*/
public class AccountManagementAlert extends AbstractAlert {
public class AccountManagementPatternFlyAlert extends AbstractPatternFlyAlert {
public boolean isError() {
return checkAlertType("error");

View file

@ -0,0 +1,60 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.page.PatternFlyClosableAlert;
import javax.ws.rs.core.UriBuilder;
import java.util.List;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractAccountPage extends AuthRealm {
@Page
private PatternFlyClosableAlert alert;
public AbstractAccountPage() {
setAuthRealm(TEST);
}
/**
* Account Console is based on hash routing, e.g. [server_root]/auth/realms/test/account/#/password.
* All page objects for Account Console need to specify their "hash path" using this method.
*
* @return the hash path
*/
protected abstract List<String> createHashPath();
@Override
public UriBuilder createUriBuilder() {
String fragment = null;
final List<String> hashPath = createHashPath();
if (hashPath != null) {
fragment = "/" + String.join("/", hashPath);
}
return super.createUriBuilder().path("account/").fragment(fragment);
}
public PatternFlyClosableAlert alert() {
return alert;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.VerticalNavBar;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.ArrayList;
import java.util.List;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractLoggedInPage extends AbstractAccountPage {
@Page
protected WelcomeScreen welcomeScreen;
@FindBy(className = "nav-pf-vertical nav-pf-vertical-with-badges")
private VerticalNavBar verticalNavBar;
@FindBy(id = "pageTitle")
protected WebElement pageTitle;
@Override
protected List<String> createHashPath() {
return new ArrayList<>();
}
/**
* This should simulate a user navigating to this page using links in the nav bar. It assume that user is logged in
* and at some Account Console page (not Welcome Screen), i.e. that the nav bar is visible.
*/
public abstract void navigateToUsingNavBar();
public VerticalNavBar verticalNavBar() {
return verticalNavBar;
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ApplicationsPage extends AbstractLoggedInPage {
@Page
private ApplicationsCard applicationsCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("applications");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && applicationsCard.isVisible();
}
public ApplicationsCard applications() {
return applicationsCard;
}
public class ApplicationsCard extends Card {
@FindBy(className = "card-pf-application")
private WebElement cardRoot;
@Override
public boolean isVisible() {
return isElementVisible(cardRoot);
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class AuthenticatorPage extends AbstractLoggedInPage {
@Page
private YourAuthenticatorsCard yourAuthenticatorsCard;
@Page
private AddAuthenticatorCard addAuthenticatorCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("authenticator");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && yourAuthenticators().isVisible() && addAuthenticator().isVisible();
}
public YourAuthenticatorsCard yourAuthenticators() {
return yourAuthenticatorsCard;
}
public AddAuthenticatorCard addAuthenticator() {
return addAuthenticatorCard;
}
public class YourAuthenticatorsCard extends Card {
@FindBy(id = "authenticatorFinishSetUpTitle")
private WebElement yourAuthenticatorsTitle;
@Override
public boolean isVisible() {
return isElementVisible(yourAuthenticatorsTitle);
}
}
public class AddAuthenticatorCard extends Card {
@FindBy(id = "authenticatorSubTitle")
private WebElement addAuthenticatorTitle;
@Override
public boolean isVisible() {
return isElementVisible(addAuthenticatorTitle);
}
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.clickBtnAndWaitForAlert;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ChangePasswordPage extends AbstractLoggedInPage {
@Page
private UpdatePasswordCard updatePasswordCard;
@Page
private PasswordLastUpdateCard passwordLastUpdateCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("password");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && updatePassword().isVisible();
}
public PasswordLastUpdateCard passwordLastUpdate() {
return passwordLastUpdateCard;
}
public UpdatePasswordCard updatePassword() {
return updatePasswordCard;
}
public class PasswordLastUpdateCard extends Card {
@FindBy(id = "passwordLastUpdate")
private WebElement cardRoot;
@Override
public boolean isVisible() {
return isElementVisible(cardRoot);
}
public String getTextDateTime() {
return getTextFromElement(cardRoot.findElement(By.tagName("strong")));
}
public LocalDateTime getDateTime() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d, yyyy, h:m:s a"); // e.g. Aug 31, 2018, 5:41:24 PM
return LocalDateTime.from(formatter.parse(getTextDateTime()));
}
}
public class UpdatePasswordCard extends Card {
@FindBy(id = "updatePasswordSubTitle")
private WebElement updatePasswordSubTitle;
@FindBy(id = "password")
private WebElement currentPassword;
@FindBy(id = "newPassword")
private WebElement newPassword;
@FindBy(id = "confirmation")
private WebElement confirmPassword;
@FindBy(name = "submitAction")
private WebElement submitBtn;
@Override
public boolean isVisible() {
return isElementVisible(updatePasswordSubTitle);
}
public void setCurrentPassword(String value) {
setTextInputValue(currentPassword, value);
}
public void setNewPassword(String value) {
setTextInputValue(newPassword, value);
}
public void setConfirmPassword(String value) {
setTextInputValue(confirmPassword, value);
}
public boolean isSaveDisabled() {
return submitBtn.getAttribute("disabled") != null;
}
public void clickSave() {
clickBtnAndWaitForAlert(submitBtn);
}
public void setPasswords(String currentPassword, String newPassword) {
setCurrentPassword(currentPassword);
setNewPassword(newPassword);
setConfirmPassword(newPassword);
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class DeviceActivityPage extends AbstractLoggedInPage {
@Page
private SignedInDevicesCard signedInDevicesCard;
@Page
private RecentlyUsedDevicesCard recentlyUsedDevicesCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("device-activity");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && signedInDevices().isVisible() && recentlyUsedDevices().isVisible();
}
public SignedInDevicesCard signedInDevices() {
return signedInDevicesCard;
}
public RecentlyUsedDevicesCard recentlyUsedDevices() {
return recentlyUsedDevicesCard;
}
public class SignedInDevicesCard extends Card {
@FindBy(id = "signedInDevicesTitle")
private WebElement signedInDevicesTitle;
@Override
public boolean isVisible() {
return isElementVisible(signedInDevicesTitle);
}
}
public class RecentlyUsedDevicesCard extends Card {
@FindBy(id = "recentlyUsedDevicesTitle")
private WebElement recentlyUsedDevicesTitle;
@Override
public boolean isVisible() {
return isElementVisible(recentlyUsedDevicesTitle);
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LinkedAccountsPage extends AbstractLoggedInPage {
@Page
private AuthorizedProvidersCard authorizedProvidersCard;
@Page
private AvailableProvidersCard availableProvidersCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("linked-accounts");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && authorizedProviders().isVisible() && availableProviders().isVisible();
}
public AuthorizedProvidersCard authorizedProviders() {
return authorizedProvidersCard;
}
public AvailableProvidersCard availableProviders() {
return availableProvidersCard;
}
public class AuthorizedProvidersCard extends Card {
@FindBy(id = "authorizedProvidersSubTitle")
private WebElement authorizedProvidersTitle;
@Override
public boolean isVisible() {
return isElementVisible(authorizedProvidersTitle);
}
}
public class AvailableProvidersCard extends Card {
@FindBy(id = "identityProviderSubTitle")
private WebElement availableProvidersTitle;
@Override
public boolean isVisible() {
return isElementVisible(availableProvidersTitle);
}
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.clickBtnAndWaitForAlert;
import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PersonalInfoPage extends AbstractLoggedInPage {
@Page
private PersonalInfoCard personalInfoCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("account");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && personalInfo().isVisible();
}
public PersonalInfoCard personalInfo() {
return personalInfoCard;
}
public class PersonalInfoCard extends Card {
@FindBy(id = "personalSubTitle")
private WebElement personalSubTitle;
@FindBy(id = "username")
private WebElement username;
@FindBy(id = "email")
private WebElement email;
@FindBy(id = "firstName")
private WebElement firstName;
@FindBy(id = "lastName")
private WebElement lastName;
@FindBy(name = "submitAction")
private WebElement submitBtn;
@Override
public boolean isVisible() {
return isElementVisible(personalSubTitle);
}
public boolean isUsernameDisabled() {
return !username.getTagName().equals("input"); // <div> for disabled
}
public String getUsername() {
return getTextInputValue(username);
}
public void setUsername(String value) {
setTextInputValue(username, value);
}
public String getEmail() {
return getTextInputValue(email);
}
public void setEmail(String value) {
setTextInputValue(email, value);
}
public String getFirstName() {
return getTextInputValue(firstName);
}
public void setFirstName(String value) {
setTextInputValue(firstName, value);
}
public String getLastName() {
return getTextInputValue(lastName);
}
public void setLastName(String value) {
setTextInputValue(lastName, value);
}
public boolean isSaveDisabled() {
return submitBtn.getAttribute("disabled") != null;
}
public void clickSave() {
clickBtnAndWaitForAlert(submitBtn);
}
public void setValues(UserRepresentation user) {
if (!isUsernameDisabled()) {setUsername(user.getUsername());}
setEmail(user.getEmail());
setFirstName(user.getFirstName());
setLastName(user.getLastName());
}
public boolean valuesEqual(UserRepresentation user) {
return user.getUsername().equals(getUsername())
&& user.getEmail().equals(getEmail())
&& user.getFirstName().equals(getFirstName())
&& user.getLastName().equals(getLastName());
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ResourcesPage extends AbstractLoggedInPage {
@Page
private ResourcesListCard resourcesListCard;
@Override
protected List<String> createHashPath() {
List<String> hashPath = super.createHashPath();
hashPath.add("my-resources");
return hashPath;
}
@Override
public void navigateToUsingNavBar() {
// TODO
}
@Override
public boolean isCurrent() {
return super.isCurrent() && resourcesListCard.isVisible();
}
public ResourcesListCard resourcesList() {
return resourcesListCard;
}
public class ResourcesListCard extends Card {
@FindBy(className = "resources-list")
private WebElement cardRoot;
@Override
public boolean isVisible() {
return isElementVisible(cardRoot);
}
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.auth.page.account2.fragment.Card;
import org.keycloak.testsuite.util.URLUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class WelcomeScreen extends AbstractAccountPage {
@Page
private PersonalInfoCard personalInfo;
@Page
private AccountSecurityCard accountSecurityCard;
@Page
private ApplicationsCard applicationsCard;
@Page
private MyResourcesCard myResourcesCard;
@FindBy(id = "welcomeMsg")
private WebElement welcomeMsg;
@FindBy(id = "signInButton")
private WebElement signInBtn;
@Override
protected List<String> createHashPath() {
return null;
}
@Override
public boolean isCurrent() {
return URLUtils.currentUrlEquals(toString() + "#/") && isElementVisible(welcomeMsg); // the hash will be eventually added after the page is loaded
}
public PersonalInfoCard personalInfo() {
return personalInfo;
}
public AccountSecurityCard accountSecurityCard() {
return accountSecurityCard;
}
public ApplicationsCard applicationsCard() {
return applicationsCard;
}
public MyResourcesCard myResourcesCard() {
return myResourcesCard;
}
public void clickLoginBtn() {
clickLink(signInBtn);
}
public boolean isLoginBtnVisible() {
return isElementVisible(signInBtn);
}
public class PersonalInfoCard extends Card {
@FindBy(id = "personalInfoCard")
private WebElement personalInfoCard;
@FindBy(id = "personalInfoLink")
private WebElement personalInfoLink;
@Override
public boolean isVisible() {
return isElementVisible(personalInfoCard);
}
public void clickPersonalInfo() {
clickLink(personalInfoLink);
}
}
public class AccountSecurityCard extends Card {
@FindBy(id = "accountSecurityCard")
private WebElement accountSecurityCard;
@FindBy(id = "changePasswordLink")
private WebElement changePasswordLink;
@FindBy(id = "authenticatorLink")
private WebElement authenticatorLink;
@FindBy(id = "deviceActivityLink")
private WebElement deviceActivityLink;
@FindBy(id = "linkedAccountsLink")
private WebElement linkedAccountsLink;
@Override
public boolean isVisible() {
return isElementVisible(accountSecurityCard);
}
public void clickChangePassword() {
clickLink(changePasswordLink);
}
public void clickAuthenticator() {
clickLink(authenticatorLink);
}
public void clickDeviceActivity() {
clickLink(deviceActivityLink);
}
public void clickLinkedAccounts() {
clickLink(linkedAccountsLink);
}
public boolean isLinkedAccountsVisible() {
return isElementVisible(linkedAccountsLink);
}
}
public class ApplicationsCard extends Card {
@FindBy(id = "applicationsCard")
private WebElement applicationsCard;
@FindBy(id = "applicationsLink")
private WebElement applicationsLink;
@Override
public boolean isVisible() {
return isElementVisible(applicationsCard);
}
public void clickApplicationsLink() {
clickLink(applicationsLink);
}
}
public class MyResourcesCard extends Card {
@FindBy(id = "myResourcesCard")
private WebElement myResourcesCard;
@FindBy(id = "myResourcesLink")
private WebElement myResourcesLink;
@Override
public boolean isVisible() {
return isElementVisible(myResourcesCard);
}
public void clickMyResources() {
clickLink(myResourcesLink);
}
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2.fragment;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.openqa.selenium.WebDriver;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class Card {
@Drone
private WebDriver driver;
public abstract boolean isVisible();
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.auth.page.account2.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
// TODO rewrite this (blocked by KEYCLOAK-8217)
public class VerticalNavBar {
@Root
private WebElement navBarRoot;
public void clickNavLinkByIndex(int i) {
clickLink(getNavLinkByIndex(i));
}
public void clickSubNavLinkByIndex(int i1, int i2) {
clickLink(getNavLinkByIndex(i1));
clickLink(getSubNavLinkByIndex(i1, i2));
}
public boolean isNavLinkActive(int i) {
return isNavLinkActive(getNavLinkByIndex(i));
}
public boolean isSubNavLinkActive(int i1, int i2) {
return isNavLinkActive(getSubNavLinkByIndex(i1, i2));
}
private WebElement getNavLinkByIndex(int i) {
return navBarRoot.findElement(By.xpath(String.format("./ul/li[%d]", i)));
}
private WebElement getSubNavLinkByIndex(int i1, int i2) {
return navBarRoot.findElement(By.xpath(String.format("./ul/li[%d]/div[contains(@class,'nav-pf-secondary-nav')]/ul/li[%d]", i1, i2)));
}
private boolean isNavLinkActive(WebElement navLink) {
return navLink.getAttribute("class").contains("active");
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.page;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.fragment.Root;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
*
* @author tkyjovsk
*/
public abstract class AbstractAlert {
protected final Logger log = Logger.getLogger(this.getClass());
@Root
protected WebElement root;
@Drone
protected WebDriver driver;
public String getText() {
return root.getText();
}
public boolean isSuccess() {
log.debug("Alert.isSuccess()");
return checkAlertType("success");
}
protected boolean checkAlertType(String type) {
WaitUtils.waitForPageToLoad();
try {
(new WebDriverWait(driver, 1)).until(ExpectedConditions.attributeContains(root, "class", "alert-" + type));
}
catch (TimeoutException e) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,99 @@
/*
* 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.page;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.logging.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
import static org.keycloak.testsuite.util.WaitUtils.PAGELOAD_TIMEOUT_MILLIS;
/**
*
* @author tkyjovsk
*/
public abstract class AbstractPatternFlyAlert {
public static final String ALERT_CLASS_NAME = "alert";
protected final Logger log = Logger.getLogger(this.getClass());
@FindBy(className = ALERT_CLASS_NAME)
protected WebElement alertRoot;
@Drone
protected WebDriver driver;
public boolean isDisplayed() {
return isElementVisible(alertRoot);
}
public static void waitUntilDisplayed() {
new WebDriverWait(getCurrentDriver(), PAGELOAD_TIMEOUT_MILLIS / 1000).until(
ExpectedConditions.visibilityOfElementLocated(By.className(ALERT_CLASS_NAME))
);
}
public String getText() {
return getTextFromElement(alertRoot);
}
public boolean isSuccess() {
return checkAlertType("success");
}
public void assertDisplayed() {
assertTrue("Alert should displayed", isDisplayed());
}
public void assertNotDisplayed() {
assertFalse("Alert shouldn't be displayed", isDisplayed());
}
public void assertSuccess() {
assertSuccess(null);
}
public void assertSuccess(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be success", isSuccess());
if (expectedText != null) assertEquals(expectedText, getText());
}
protected boolean checkAlertType(String type) {
try {
new WebDriverWait(driver, 1).until(ExpectedConditions.attributeContains(alertRoot, "class", "alert-" + type));
}
catch (TimeoutException e) {
return false;
}
return true;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -14,20 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
package org.keycloak.testsuite.page;
import org.jboss.arquillian.graphene.fragment.Root;
import org.keycloak.testsuite.page.AbstractAlert;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
*
* @author Petr Mensik
* @author tkyjovsk
*/
public class AdminConsoleAlert extends AbstractAlert {
public class PatternFlyClosableAlert extends AbstractPatternFlyAlert {
@FindBy(xpath = ".//button[@class='close']")
protected WebElement closeButton;
@ -44,6 +45,45 @@ public class AdminConsoleAlert extends AbstractAlert {
return checkAlertType("danger");
}
@Override
public void assertSuccess(String expectedText) {
super.assertSuccess(expectedText);
close();
}
public void assertInfo() {
assertInfo(null);
}
public void assertInfo(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be info", isInfo());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void assertWarning() {
assertWarning(null);
}
public void assertWarning(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be warning", isWarning());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void assertDanger() {
assertDanger(null);
}
public void assertDanger(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be danger", isDanger());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void close() {
closeButton.click();
WaitUtils.pause(500); // Sometimes, when a test is too fast,

View file

@ -1,7 +1,12 @@
package org.keycloak.testsuite.util;
import io.appium.java_client.android.AndroidDriver;
import org.apache.commons.lang3.StringUtils;
import org.keycloak.testsuite.page.AbstractPatternFlyAlert;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
@ -19,6 +24,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
public final class UIUtils {
public static final String VALUE_ATTR_NAME = "value";
public static final short EXPECTED_UI_LAYOUT = Short.parseShort(System.getProperty("testsuite.ui.layout")); // 0 == desktop layout, 1 == smartphone layout, 2 == tablet layout
public static boolean selectContainsOption(Select select, String optionText) {
for (WebElement option : select.getOptions()) {
@ -55,6 +61,18 @@ public final class UIUtils {
performOperationWithPageReload(element::click);
}
/**
* This is as an alternative for {@link #clickLink(WebElement)} and should be used in situations where we can't use
* {@link WaitUtils#waitForPageToLoad()}. This is because {@link WaitUtils#waitForPageToLoad()} would wait until the
* alert would disappeared itself (timeout).
*
* @param button to click on
*/
public static void clickBtnAndWaitForAlert(WebElement button) {
button.click();
AbstractPatternFlyAlert.waitUntilDisplayed();
}
/**
* Navigates to a link directly instead of clicking on it.
* Some browsers are sometimes having problems with clicking on links, so this should be used only in that cases,
@ -93,9 +111,13 @@ public final class UIUtils {
public static void setTextInputValue(WebElement input, String value) {
input.click();
input.clear();
if (value != null) {
if (!StringUtils.isEmpty(value)) { // setting new input
input.sendKeys(value);
}
else { // just clearing the input; input.clear() may not fire all JS events so we need to let the page know that something's changed
input.sendKeys("a");
input.sendKeys(Keys.BACK_SPACE);
}
WebDriver driver = getCurrentDriver();
if (driver instanceof AndroidDriver) {
@ -117,4 +139,21 @@ public final class UIUtils {
}
return text;
}
/**
* Should be used solely with {@link org.jboss.arquillian.graphene.GrapheneElement}, i.e. all elements annotated by
* {@link org.openqa.selenium.support.FindBy}. CANNOT be used with elements found directly using
* {@link WebDriver#findElement(By)} and similar.
*
* @param element
* @return true if element is present and visible
*/
public static boolean isElementVisible(WebElement element) {
try {
return element.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
}

View file

@ -16,21 +16,25 @@
*/
package org.keycloak.testsuite.util;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.arquillian.graphene.wait.ElementBuilder;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
import static org.openqa.selenium.support.ui.ExpectedConditions.*;
import static org.openqa.selenium.support.ui.ExpectedConditions.javaScriptThrowsNoExceptions;
import static org.openqa.selenium.support.ui.ExpectedConditions.not;
import static org.openqa.selenium.support.ui.ExpectedConditions.urlToBe;
/**
*
@ -97,9 +101,11 @@ public final class WaitUtils {
return; // not needed
}
String currentUrl = null;
// Ensure the URL is "stable", i.e. is not changing anymore; if it'd changing, some redirects are probably still in progress
for (int maxRedirects = 2; maxRedirects > 0; maxRedirects--) {
String currentUrl = driver.getCurrentUrl();
for (int maxRedirects = 4; maxRedirects > 0; maxRedirects--) {
currentUrl = driver.getCurrentUrl();
FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(Duration.ofMillis(250));
try {
wait.until(not(urlToBe(currentUrl)));
@ -113,19 +119,34 @@ public final class WaitUtils {
}
WebDriverWait wait = new WebDriverWait(getCurrentDriver(), PAGELOAD_TIMEOUT_MILLIS / 1000);
ExpectedCondition waitCondition = null;
try {
// Checks if the document is ready and asks AngularJS, if present, whether there are any REST API requests
// in progress
wait.until(javaScriptThrowsNoExceptions(
// Different wait strategies for Admin and Account Consoles
if (currentUrl.matches("^[^\\/]+:\\/\\/[^\\/]+\\/auth\\/admin\\/.*$")) { // Admin Console
// Checks if the document is ready and asks AngularJS, if present, whether there are any REST API requests in progress
waitCondition = javaScriptThrowsNoExceptions(
"if (document.readyState !== 'complete' "
+ "|| (typeof angular !== 'undefined' && angular.element(document.body).injector().get('$http').pendingRequests.length !== 0)) {"
+ "throw \"Not ready\";"
+ "}"));
} catch (TimeoutException e) {
// Sometimes, for no obvious reason, the browser/JS doesn't set document.readyState to 'complete' correctly
// but that's no reason to let the test fail; after the timeout the page is surely fully loaded
log.warn("waitForPageToLoad time exceeded!");
+ "}");
}
else if (
currentUrl.matches("^[^\\/]+:\\/\\/[^\\/]+\\/auth\\/realms\\/[^\\/]+\\/account\\/.*$") // check for Account Console URL
&& driver.getPageSource().contains("patternfly-ng") // check for new Account Console (don't use this strategy with the old one)
) {
waitCondition = javaScriptThrowsNoExceptions(
"if (!window.getAngularTestability(document.querySelector('app-root')).isStable()) {" +
"throw 'Not ready';" +
"}"
);
}
if (waitCondition != null) {
try {
wait.until(waitCondition);
} catch (TimeoutException e) {
log.warn("waitForPageToLoad time exceeded!");
}
}
}

View file

@ -137,7 +137,7 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
deleteAllSessionsInRealm(testRealmAccountPage.getAuthRealm());
}
private void resetTestRealmSession() {
protected void resetTestRealmSession() {
resetRealmSession(testRealmAccountPage.getAuthRealm());
}

View file

@ -21,8 +21,7 @@ import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.auth.page.account.AccountManagement;
import org.keycloak.testsuite.auth.page.account.fragment.AccountManagementAlert;
import org.openqa.selenium.support.FindBy;
import org.keycloak.testsuite.auth.page.account.fragment.AccountManagementPatternFlyAlert;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
@ -36,8 +35,8 @@ public abstract class AbstractAccountManagementTest extends AbstractAuthTest {
@Page
protected AccountManagement testRealmAccountManagementPage;
@FindBy(className = "alert")
protected AccountManagementAlert alert;
@Page
protected AccountManagementPatternFlyAlert alert;
@Override
public void setDefaultPageUriParameters() {

View file

@ -17,16 +17,16 @@
package org.keycloak.testsuite.ui;
import org.junit.Before;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractAuthTest;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Set;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
@ -34,14 +34,9 @@ public abstract class AbstractUiTest extends AbstractAuthTest {
public static final String LOCALIZED_THEME = "localized-theme";
public static final String CUSTOM_LOCALE_NAME = "Přísný jazyk";
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation testRealmRep = new RealmRepresentation();
testRealmRep.setId(TEST);
testRealmRep.setRealm(TEST);
testRealmRep.setEnabled(true);
configureInternationalizationForRealm(testRealmRep);
testRealms.add(testRealmRep);
@Before
public void addTestUser() {
createTestUserWithAdminClient(false);
}
protected void configureInternationalizationForRealm(RealmRepresentation realm) {
@ -58,4 +53,12 @@ public abstract class AbstractUiTest extends AbstractAuthTest {
realm.setAccountTheme(LOCALIZED_THEME);
realm.setEmailTheme(LOCALIZED_THEME);
}
protected IdentityProviderRepresentation createIdentityProviderRepresentation(String alias, String providerId) {
IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation();
idpRep.setProviderId(providerId);
idpRep.setAlias(alias);
idpRep.setConfig(new HashMap<>());
return idpRep;
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.common.Profile;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.auth.page.account2.WelcomeScreen;
import org.keycloak.testsuite.ui.AbstractUiTest;
import java.util.List;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractAccountTest extends AbstractUiTest {
public static final String ACCOUNT_THEME_NAME = "keycloak-preview";
@Page
protected WelcomeScreen accountWelcomeScreen;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealmRep = testRealms.get(0);
testRealmRep.setAccountTheme(ACCOUNT_THEME_NAME);
}
@BeforeClass
public static void enabled() {
ProfileAssume.assumeFeatureEnabled(Profile.Feature.ACCOUNT2);
}
@Before
public void beforeAccountTest() {
accountWelcomeScreen.navigateTo();
}
protected void loginToAccount() {
assertCurrentUrlStartsWithLoginUrlOf(accountWelcomeScreen);
loginPage.form().login(testUser);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account2;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class BaseAccountPageTest extends AbstractAccountTest {
protected abstract AbstractLoggedInPage getAccountPage();
@Before
public void beforeBaseAccountPageTest() {
getAccountPage().navigateTo();
loginToAccount();
getAccountPage().assertCurrent();
}
@Test
@Ignore // TODO, blocked by KEYCLOAK-8217
public void navigationTest() {
getAccountPage().navigateToUsingNavBar();
getAccountPage().assertCurrent();
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
import org.keycloak.testsuite.auth.page.account2.ChangePasswordPage;
import java.time.LocalDateTime;
import java.util.List;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.Users.getPasswordOf;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ChangePasswordTest extends BaseAccountPageTest {
@Page
private ChangePasswordPage changePasswordPage;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realm = testRealms.get(0);
realm.setPasswordPolicy("length(3)");
}
@Override
protected AbstractLoggedInPage getAccountPage() {
return changePasswordPage;
}
@Test
public void changePassword() {
final LocalDateTime testStartTime = LocalDateTime.now();
final String oldPwd = getPasswordOf(testUser);
final String newPwd = "nějaké nové heslo s háčky a čárkami";
setPasswordFor(testUser, newPwd);
assertTrue("The current password should be older than the start time of this test",
testStartTime.isAfter(changePasswordPage.passwordLastUpdate().getDateTime()));
changePasswordPage.updatePassword().setPasswords(oldPwd, newPwd);
changePasswordPage.updatePassword().clickSave();
changePasswordPage.alert().assertSuccess();
// try the new password
deleteAllSessionsInTestRealm(); // logout
changePasswordPage.navigateTo();
loginToAccount();
changePasswordPage.assertCurrent();
assertTrue("The new password should be newer than the start time of this test",
testStartTime.isBefore(changePasswordPage.passwordLastUpdate().getDateTime()));
}
@Test
public void formValidationTest() {
assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
changePasswordPage.updatePassword().setPasswords("abc", "def");
assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
// clear current password
changePasswordPage.updatePassword().setCurrentPassword("");
assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
changePasswordPage.updatePassword().setCurrentPassword("abc");
assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
// clear new password
changePasswordPage.updatePassword().setNewPassword("");
assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
changePasswordPage.updatePassword().setNewPassword("def");
assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
// clear confirm password
changePasswordPage.updatePassword().setConfirmPassword("");
assertTrue(changePasswordPage.updatePassword().isSaveDisabled());
changePasswordPage.updatePassword().setConfirmPassword("def");
assertFalse(changePasswordPage.updatePassword().isSaveDisabled());
// invalid current password
changePasswordPage.updatePassword().setPasswords("invalid", "ab");
changePasswordPage.updatePassword().clickSave();
changePasswordPage.alert().assertDanger("Invalid existing password.");
// non-matching passwords
changePasswordPage.updatePassword().setPasswords(getPasswordOf(testUser), "ab");
changePasswordPage.updatePassword().setConfirmPassword("no match");
changePasswordPage.updatePassword().clickSave();
changePasswordPage.alert().assertDanger("Passwords don't match.");
// password policy
changePasswordPage.updatePassword().setPasswords(getPasswordOf(testUser), "ab");
changePasswordPage.updatePassword().clickSave();
changePasswordPage.alert().assertDanger("Invalid password: minimum length 3.");
// check the password is not changed
deleteAllSessionsInTestRealm();
changePasswordPage.navigateTo();
loginToAccount();
changePasswordPage.assertCurrent();
}
// TODO test the last update timestamp when the password was never updated (blocked by KEYCLOAK-8193)
// TODO test internationalization for last update timestamp (blocked by KEYCLOAK-8194)
}

View file

@ -0,0 +1,148 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.auth.page.account2.AbstractLoggedInPage;
import org.keycloak.testsuite.auth.page.account2.PersonalInfoPage;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PersonalInfoTest extends BaseAccountPageTest {
private UserRepresentation testUser2;
@Page
private PersonalInfoPage personalInfoPage;
@Before
public void setTestUser() {
testUser2 = new UserRepresentation();
testUser2.setUsername("vmuzikar");
testUser2.setEmail("vmuzikar@redhat.com");
testUser2.setFirstName("Václav");
testUser2.setLastName("Muzikář");
ApiUtil.removeUserByUsername(testRealmResource(), testUser2.getUsername());
}
@Override
protected AbstractLoggedInPage getAccountPage() {
return personalInfoPage;
}
@Test
public void updateUserInfo() {
setEditUsernameAllowed(true);
assertTrue(personalInfoPage.personalInfo().valuesEqual(testUser));
assertFalse(personalInfoPage.personalInfo().isUsernameDisabled());
assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().setValues(testUser2);
assertTrue(personalInfoPage.personalInfo().valuesEqual(testUser2));
assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().clickSave();
personalInfoPage.alert().assertSuccess();
personalInfoPage.navigateTo();
personalInfoPage.personalInfo().valuesEqual(testUser2);
// change just first and last name
testUser2.setFirstName("Another");
testUser2.setLastName("Name");
personalInfoPage.personalInfo().setValues(testUser2);
personalInfoPage.personalInfo().clickSave();
personalInfoPage.alert().assertSuccess();
personalInfoPage.navigateTo();
personalInfoPage.personalInfo().valuesEqual(testUser2);
}
@Test
public void formValidationTest() {
setEditUsernameAllowed(true);
// clear username
personalInfoPage.personalInfo().setUsername("");
assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().setUsername("abc");
assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
// clear email
personalInfoPage.personalInfo().setEmail("");
assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().setEmail("vmuzikar@redhat.com");
assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
// TODO test email validation (blocked by KEYCLOAK-8098)
// clear first name
personalInfoPage.personalInfo().setFirstName("");
assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().setFirstName("abc");
assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
// clear last name
personalInfoPage.personalInfo().setLastName("");
assertTrue(personalInfoPage.personalInfo().isSaveDisabled());
personalInfoPage.personalInfo().setLastName("abc");
assertFalse(personalInfoPage.personalInfo().isSaveDisabled());
// duplicity tests
ApiUtil.createUserWithAdminClient(testRealmResource(), testUser2);
// duplicate username
personalInfoPage.personalInfo().setUsername(testUser2.getUsername());
personalInfoPage.personalInfo().clickSave();
personalInfoPage.alert().assertDanger("Username already exists.");
personalInfoPage.personalInfo().setUsername(testUser.getUsername());
// duplicate email
personalInfoPage.personalInfo().setEmail(testUser2.getEmail());
personalInfoPage.personalInfo().clickSave();
personalInfoPage.alert().assertDanger("Email already exists.");
// check no changes were saved
personalInfoPage.navigateTo();
personalInfoPage.personalInfo().valuesEqual(testUser);
}
@Test
public void disabledEditUsername() {
setEditUsernameAllowed(false);
assertTrue(personalInfoPage.personalInfo().isUsernameDisabled());
personalInfoPage.personalInfo().setValues(testUser2);
personalInfoPage.personalInfo().clickSave();
personalInfoPage.alert().assertSuccess();
testUser2.setUsername(testUser.getUsername()); // the username should remain the same
personalInfoPage.navigateTo();
personalInfoPage.personalInfo().valuesEqual(testUser2);
}
private void setEditUsernameAllowed(boolean value) {
RealmRepresentation realm = testRealmResource().toRepresentation();
realm.setEditUsernameAllowed(value);
testRealmResource().update(realm);
personalInfoPage.navigateTo();
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account2;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.auth.page.account2.ApplicationsPage;
import org.keycloak.testsuite.auth.page.account2.AuthenticatorPage;
import org.keycloak.testsuite.auth.page.account2.ChangePasswordPage;
import org.keycloak.testsuite.auth.page.account2.DeviceActivityPage;
import org.keycloak.testsuite.auth.page.account2.LinkedAccountsPage;
import org.keycloak.testsuite.auth.page.account2.PersonalInfoPage;
import org.keycloak.testsuite.auth.page.account2.ResourcesPage;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class WelcomeScreenTest extends AbstractAccountTest {
@Page
private PersonalInfoPage personalInfoPage;
@Page
private ChangePasswordPage changePasswordPage;
@Page
private AuthenticatorPage authenticatorPage;
@Page
private DeviceActivityPage deviceActivityPage;
@Page
private LinkedAccountsPage linkedAccountsPage;
@Page
private ApplicationsPage applicationsPage;
@Page
private ResourcesPage resourcesPage;
@Test
public void loginTest() {
accountWelcomeScreen.assertCurrent();
assertTrue(accountWelcomeScreen.isLoginBtnVisible());
// login
accountWelcomeScreen.clickLoginBtn();
loginToAccount();
accountWelcomeScreen.assertCurrent();
assertFalse(accountWelcomeScreen.isLoginBtnVisible());
// TODO logout test (blocked by KEYCLOAK-8084)
}
@Test
public void personalInfoTest() {
assertTrue(accountWelcomeScreen.personalInfo().isVisible());
accountWelcomeScreen.personalInfo().clickPersonalInfo();
loginToAccount();
personalInfoPage.assertCurrent();
}
@Test
public void accountSecurityTest() {
assertTrue(accountWelcomeScreen.accountSecurityCard().isVisible());
// change password link
accountWelcomeScreen.accountSecurityCard().clickChangePassword();
loginToAccount();
changePasswordPage.assertCurrent();
// authenticator link
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.accountSecurityCard().clickAuthenticator();
authenticatorPage.assertCurrent();
// device activity link
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.accountSecurityCard().clickDeviceActivity();
deviceActivityPage.assertCurrent();
// linked accounts link
accountWelcomeScreen.navigateTo();
assertFalse(accountWelcomeScreen.accountSecurityCard().isLinkedAccountsVisible());
// add simple IdP
testRealmResource().identityProviders().create(createIdentityProviderRepresentation("test-idp", "test-provider"));
// test link appeared
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.accountSecurityCard().clickLinkedAccounts();
linkedAccountsPage.assertCurrent();
// no need to remove the IdP
}
@Test
public void applicationsTest() {
assertTrue(accountWelcomeScreen.applicationsCard().isVisible());
accountWelcomeScreen.applicationsCard().clickApplicationsLink();
loginToAccount();
applicationsPage.assertCurrent();
}
@Test
public void resourcesTest() {
assertFalse(accountWelcomeScreen.myResourcesCard().isVisible());
// set user managed access
RealmRepresentation testRealm = testRealmResource().toRepresentation();
testRealm.setUserManagedAccessAllowed(true);
testRealmResource().update(testRealm);
// test my resources appeared
accountWelcomeScreen.navigateTo();
assertTrue(accountWelcomeScreen.myResourcesCard().isVisible());
accountWelcomeScreen.myResourcesCard().clickMyResources();
loginToAccount();
resourcesPage.assertCurrent();
// no need to disable user managed access
}
}

View file

@ -17,9 +17,11 @@
package org.keycloak.testsuite.ui.login;
import org.junit.Before;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.AbstractUiTest;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
@ -29,9 +31,11 @@ import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractLoginTest extends AbstractUiTest {
@Before
public void addTestUser() {
createTestUserWithAdminClient(false);
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealmRep = testRealms.get(0);
configureInternationalizationForRealm(testRealmRep);
}
protected void assertLoginFailed(String message) {

View file

@ -26,8 +26,8 @@ import org.keycloak.testsuite.console.page.AdminConsole;
import org.keycloak.testsuite.console.page.AdminConsoleRealm;
import org.keycloak.testsuite.console.page.AdminConsoleRealm.ConfigureMenu;
import org.keycloak.testsuite.console.page.AdminConsoleRealm.ManageMenu;
import org.keycloak.testsuite.console.page.fragment.AdminConsoleAlert;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.PatternFlyClosableAlert;
import org.openqa.selenium.support.FindBy;
import static org.junit.Assert.assertTrue;
@ -53,8 +53,8 @@ public abstract class AbstractConsoleTest extends AbstractAuthTest {
@FindBy(xpath = "//div[@class='modal-dialog']")
protected ModalDialog modalDialog;
@FindBy(className = "alert")
protected AdminConsoleAlert alert;
@Page
protected PatternFlyClosableAlert alert;
protected boolean adminLoggedIn = false;
@ -111,13 +111,11 @@ public abstract class AbstractConsoleTest extends AbstractAuthTest {
}
public void assertAlertSuccess() {
assertTrue("Alert is not success", alert.isSuccess());
alert.close();
alert.assertSuccess();
}
public void assertAlertDanger() {
assertTrue("Alert is not danger", alert.isDanger());
alert.close();
alert.assertDanger();
}
public ConfigureMenu configure() {

View file

@ -167,6 +167,7 @@
<backends.console.output>true</backends.console.output>
<testsuite.constants>${project.build.directory}/dependency/test-constants.properties</testsuite.constants>
<testsuite.ui.layout>0</testsuite.ui.layout> <!-- this is to tell the UI tests what type of layout to expect (doesn't setup the layout, just what to expect); 0 == desktop layout, 1 == smartphone layout, 2 == tablet layout -->
<skip.add.user.json>false</skip.add.user.json>
<skip.clean.second.cache>true</skip.clean.second.cache>
@ -461,6 +462,7 @@
<kie.maven.settings>${kie.maven.settings}</kie.maven.settings>
<testsuite.constants>${testsuite.constants}</testsuite.constants>
<testsuite.ui.layout>${testsuite.ui.layout}</testsuite.ui.layout>
<cli.log.output>${cli.log.output}</cli.log.output>
<test.intermittent>${test.intermittent}</test.intermittent>

View file

@ -194,7 +194,7 @@
<h2>${msg("accountSecurityTitle")}</h2>
<p class="card-pf-content-intro">${msg("accountSecurityIntroMessage")}</p>
<h3 id="changePasswordLink"><a href="${baseUrl}/#/password">${msg("changePasswordHtmlTitle")}</a></h3>
<h3 id="authenticatiorLink"><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
<h3 id="authenticatorLink"><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
<h3 id="deviceActivityLink"><a href="${baseUrl}/#/device-activity">${msg("deviceActivityHtmlTitle")}</a></h3>
<h3 id="linkedAccountsLink"><a href="${baseUrl}/#/linked-accounts">${msg("linkedAccountsHtmlTitle")}</a></h3>
</div>