diff --git a/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/Login.java b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/Login.java index 229136977..6eaba2a70 100644 --- a/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/Login.java +++ b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/Login.java @@ -25,6 +25,7 @@ import org.junit.Assert; import org.openqa.selenium.WebDriver; import pages.LoginPage; +import pages.Page; import pages.ProfilePage; import pages.RootPage; @@ -34,6 +35,7 @@ public class Login extends CucumberRoot { protected RootPage rootPage = new RootPage(driver); protected LoginPage loginPage = new LoginPage(driver); protected ProfilePage profilePage = new ProfilePage(driver); + protected Page page = new Page(driver); @Given("Mary navigates to the Embedded Widget View") public void maryNavigatesToTheEmbeddedWidgetView() { @@ -58,12 +60,35 @@ public void sheFillsInHerCorrectPassword() { loginPage.passwordInput.sendKeys(PASSWORD); } + @And("she fills in her account password") + public void she_fills_in_her_account_password() { + Assert.assertTrue(loginPage.passwordInput.isDisplayed()); + loginPage.passwordInput.click(); + loginPage.passwordInput.sendKeys("Abcd1234"); + } + + @And("^she fills in her incorrect password$") + public void enter_incorrect_password() { + loginPage.passwordInput.click(); + loginPage.passwordInput.sendKeys("invalid123"); + } + @And("she submits the Login form") public void sheSubmitsTheLoginForm() { Assert.assertTrue(loginPage.submitButton.isDisplayed()); loginPage.submitButton.click(); } + @And("she submits the Login form and locks the account") + public void sheSubmitsTheLoginFormAndLocksTheAccount() throws InterruptedException { + for(int i=0; i < 10; i++) { + loginPage.waitForWebElementDisplayed(loginPage.submitButton); + Assert.assertTrue(loginPage.submitButton.isDisplayed()); + loginPage.submitButton.click(); + page.waitForOneSec(); + } + } + @Then("she is redirected to the Root View") public void sheIsRedirectedToTheRootView() { rootPage.waitForWebElementDisplayed(rootPage.profileLink); diff --git a/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServiceAccountUnlock.java b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServiceAccountUnlock.java new file mode 100644 index 000000000..dd28ff0f2 --- /dev/null +++ b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServiceAccountUnlock.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021-Present, Okta, Inc. + * + * 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 info.seleniumcucumber.userStepDefinitions; + + +import env.CucumberRoot; +import env.DriverUtil; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.openqa.selenium.WebDriver; +import pages.*; + +import java.util.ArrayList; + +public class SelfServiceAccountUnlock extends CucumberRoot { + + protected WebDriver driver = DriverUtil.getDefaultDriver(); + protected RootPage rootPage = new RootPage(driver); + protected LoginPage loginPage = new LoginPage(driver); + protected ProfilePage profilePage = new ProfilePage(driver); + protected Page page = new Page(driver); + protected PasswordResetPage passwordResetPage = new PasswordResetPage(driver); + protected AccountUnlockPage accountUnlockPage = new AccountUnlockPage(driver); + + @When("^she sees a link to unlock her account$") + public void she_sees_a_link_to_unlock_her_account() { + page.waitForOneSec(); + accountUnlockPage.waitForWebElementDisplayed(accountUnlockPage.unlockAccountLink); + Assert.assertTrue(accountUnlockPage.unlockAccountLink.isDisplayed()); + } + + @And("^she clicks the link to unlock her account$") + public void she_clicks_the_link_to_unlock_her_account() { + page.waitForOneSec(); + accountUnlockPage.waitForWebElementDisplayed(accountUnlockPage.unlockAccountLink); + accountUnlockPage.unlockAccountLink.click(); + } + + @Then("^she sees a page to input her username and select Email or Phone to unlock her account$") + public void she_sees_a_page_to_input_her_username_and_select_email_or_phone_to_unlock_her_account() { + accountUnlockPage.waitForWebElementDisplayed(accountUnlockPage.unlockAccountPage); + Assert.assertTrue(accountUnlockPage.unlockAccountPage.isDisplayed()); + } + + @Then("^she selects Email from the available options$") + public void she_selects_email_from_the_available_options() { + Assert.assertTrue(accountUnlockPage.emailSelectButton.isDisplayed()); + accountUnlockPage.emailSelectButton.click(); + } + + @When("^she opens the magic link from her email inbox$") + public void she_opens_the_magic_link_from_her_email_inbox() { + String mailBody="email-activation-button"; + String emailContent = page.fetchSpecificEmailContent(mailBody); + String magicLink = page.fetchUnlockMagicLinkFromEmail(emailContent); + driver.get(magicLink); + } + + @And("^she submits the verify form$") + public void she_submits_the_form() { + Assert.assertTrue(accountUnlockPage.verifyFormSubmitButton.isDisplayed()); + accountUnlockPage.verifyFormSubmitButton.click(); + } + + @Then("^she sees a page that says \"Account Successfully Unlocked!\" and to enter the password for verification$") + public void she_sees_a_page_that_says_account_successfully_unlocked_and_to_enter_the_password_for_verification() { + accountUnlockPage.waitForWebElementDisplayed(accountUnlockPage.accountUnlockSuccessMessage); + Assert.assertTrue(accountUnlockPage.accountUnlockSuccessMessage.isDisplayed()); + Assert.assertTrue(accountUnlockPage.enterPasswordBox.isDisplayed()); + } + @Then("^she selects \"Phone\" from the available options$") + public void she_selects_phone_from_the_available_options() { + Assert.assertTrue(accountUnlockPage.phoneSelectButton.isDisplayed()); + accountUnlockPage.phoneSelectButton.click(); + } + + @And("^she sees a page saying \"Verify with your phone\"$") + public void she_sees_a_page_saying_verify_with_your_phone() { + accountUnlockPage.waitForWebElementDisplayed(accountUnlockPage.receiveSMSButton); + Assert.assertTrue(accountUnlockPage.receiveSMSButton.isDisplayed()); + } + + @Then("^she clicks the button saying \"Receive a code via SMS\"$") + public void she_clicks_the_button_saying_receive_a_code_via_sms() { + accountUnlockPage.receiveSMSButton.click(); + } + + @When("^she fills in the correct code from SMS$") + public void she_fills_in_the_correct_code_from_sms() { + String code = page.fetchCodeFromSMS(); + Assert.assertTrue(StringUtils.isNotBlank(code)); + passwordResetPage.enterCodeBox.click(); + passwordResetPage.enterCodeBox.sendKeys(code); + } +} diff --git a/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServicePasswordReset.java b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServicePasswordReset.java index f904046fa..3205849d3 100644 --- a/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServicePasswordReset.java +++ b/samples/embedded-sign-in-widget/src/test/java/info/seleniumcucumber/userStepDefinitions/SelfServicePasswordReset.java @@ -40,10 +40,10 @@ public void she_clicks_on_the_forgot_password_link() { passwordResetPage.forgotPasswordLink.click(); } - @Then("^she sees the page to input the email address$") - public void she_sees_the_page_to_input_the_email_address() { - loginPage.waitForWebElementDisplayed(passwordResetPage.enterEmailBox); - Assert.assertTrue(passwordResetPage.enterEmailBox.isDisplayed()); + @Then("^she sees the password reset page$") + public void she_sees_the_password_reset_page() { + loginPage.waitForWebElementDisplayed(passwordResetPage.resetPasswordPage); + Assert.assertTrue(passwordResetPage.resetPasswordPage.isDisplayed()); } @And("^she submits the recovery form$") @@ -105,7 +105,6 @@ public void she_submits_the_password_reset_form() { passwordResetPage.resetPasswordSubmitButton.click(); } - @When("she inputs her correct email address") public void she_inputs_her_correct_email_address() { Assert.assertNotNull(Page.getA18NProfile()); diff --git a/samples/embedded-sign-in-widget/src/test/java/pages/AccountUnlockPage.java b/samples/embedded-sign-in-widget/src/test/java/pages/AccountUnlockPage.java new file mode 100644 index 000000000..8021d696e --- /dev/null +++ b/samples/embedded-sign-in-widget/src/test/java/pages/AccountUnlockPage.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021-Present, Okta, Inc. + * + * 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 pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +public class AccountUnlockPage extends Page { + + public AccountUnlockPage(WebDriver driver) { + super(driver); + } + + @FindBy(css = "a[data-se = 'unlock']") + public static WebElement unlockAccountLink; + + @FindBy(xpath = "//h2[text()= 'Unlock account?']") + public static WebElement unlockAccountPage; + + @FindBy(css = "div[data-se = 'okta_email']") + public static WebElement emailSelectButton; + + @FindBy(css = "input[type='submit'][value='Verify'][data-type='save']") + public static WebElement verifyFormSubmitButton; + + @FindBy(className = "ion-messages-container") + public static WebElement accountUnlockSuccessMessage; + + @FindBy(css = "input[type='password'][name='credentials.passcode']") + public static WebElement enterPasswordBox; + + @FindBy(className = "otp-value") + public static WebElement otpValue; + + @FindBy(css = "div[data-se = 'phone_number']") + public static WebElement phoneSelectButton; + + @FindBy(css = "input[type='submit'][value='Receive a code via SMS']") + public static WebElement receiveSMSButton; +} + diff --git a/samples/embedded-sign-in-widget/src/test/java/pages/Page.java b/samples/embedded-sign-in-widget/src/test/java/pages/Page.java index 19a0c074f..aa08c61e1 100644 --- a/samples/embedded-sign-in-widget/src/test/java/pages/Page.java +++ b/samples/embedded-sign-in-widget/src/test/java/pages/Page.java @@ -125,6 +125,23 @@ public String fetchEmailContent() { return email; } + public String fetchSpecificEmailContent(String mailBody) { + String email = null; + int totalRetryCount = getRetryCountDuringVerificationCodeFetching(); + int tryCounter = 0; + while(tryCounter < totalRetryCount && !(email != null && email.contains(mailBody))) { + waitForNextTry(); + email = Page.getA18NClient().getLatestEmailContent(Page.getA18NProfile()); + if(email != null && email.contains(mailBody)) { + logger.info("Verification email successfully received."); + } else { + logger.warn("Attempt {} of {} email fetching failed.", tryCounter, totalRetryCount); + } + tryCounter++; + } + return email; + } + public String fetchCodeFromRegistrationEmail(String emailContent) { Pattern pattern = Pattern.compile("To verify manually, enter this code: (\\d{6})"); Matcher matcher = pattern.matcher(emailContent); @@ -151,6 +168,12 @@ public String fetchMagicLinkFromEmail(String emailContent) { return matcher.find() ? matcher.group(1) : null; } + public String fetchUnlockMagicLinkFromEmail(String emailContent) { + Pattern pattern = Pattern.compile("\\\"unlock-account-link\\\" href=\\\"(.*?)\\\""); + Matcher matcher = pattern.matcher(emailContent); + return matcher.find() ? matcher.group(1) : null; + } + public String fetchActivationLinkFromEmail(String emailContent) { Pattern pattern = Pattern.compile("\\\"push-verify-activation-link\\\" href=\\\"(.*?)\\\""); Matcher matcher = pattern.matcher(emailContent); @@ -200,4 +223,11 @@ int getSleepDurationDuringVerificationCodeFetching() { return value; } + public void waitForOneSec() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("Exception occurred", e); + } + } } diff --git a/samples/embedded-sign-in-widget/src/test/java/pages/PasswordResetPage.java b/samples/embedded-sign-in-widget/src/test/java/pages/PasswordResetPage.java index fe310d496..c209984ac 100644 --- a/samples/embedded-sign-in-widget/src/test/java/pages/PasswordResetPage.java +++ b/samples/embedded-sign-in-widget/src/test/java/pages/PasswordResetPage.java @@ -55,4 +55,7 @@ public PasswordResetPage(WebDriver driver) { @FindBy(className = "okta-form-subtitle") public WebElement emailVerificationWaitingScreen; + @FindBy(xpath = "//h2[text()= 'Reset your password']") + public static WebElement resetPasswordPage; + } diff --git a/samples/embedded-sign-in-widget/src/test/resources/features/self_service_account_unlock.feature b/samples/embedded-sign-in-widget/src/test/resources/features/self_service_account_unlock.feature new file mode 100644 index 000000000..c757d4a91 --- /dev/null +++ b/samples/embedded-sign-in-widget/src/test/resources/features/self_service_account_unlock.feature @@ -0,0 +1,79 @@ +Feature: 8.3 Self service account unlock with with Single factor (Email, Phone, Okta Verify Push) + + @requireA18NProfile + @requireExistingUser + @requireMFAGroupsForUser + Scenario: 8.3.1 Mary recovers from a locked account with Email Magic Link from a different Browser + Given Mary navigates to the Embedded Widget View + When she inputs her correct email address + And she fills in her incorrect password + And she submits the Login form and locks the account + When she sees a link to unlock her account + And she clicks the link to unlock her account + Then she sees a page to input her username and select Email or Phone to unlock her account + When she inputs her correct email address + Then she selects Email from the available options + And she sees a page saying "Verify with your email" + Then she clicks on "Send me an email" + And the page changes to waiting screen message for email verification + And she clicks on "Enter a code from the email instead" + Then she sees a page to input her code + When she opens the magic link from her email inbox + Then she sees a page that says "Account Successfully Unlocked!" and to enter the password for verification + Given Mary navigates to the Embedded Widget View + When she inputs her correct email address + And she fills in her account password + And she submits the Login form + Then she is redirected to the Root View + And she sees a table with her profile info + + @requireA18NProfile + @requireExistingUser + @requireMFAGroupsForUser + Scenario: 8.3.2 Mary recovers from a locked account with Email OTP + Given Mary navigates to the Embedded Widget View + When she inputs her correct email address + And she fills in her incorrect password + And she submits the Login form and locks the account + When she sees a link to unlock her account + And she clicks the link to unlock her account + Then she sees a page to input her username and select Email or Phone to unlock her account + When she inputs her correct email address + Then she selects Email from the available options + And she sees a page saying "Verify with your email" + Then she clicks on "Send me an email" + And the page changes to waiting screen message for email verification + And she clicks on "Enter a code from the email instead" + Then she sees a page to input her code + When she fills in the correct code + And she submits the verify form + Then she sees a page that says "Account Successfully Unlocked!" and to enter the password for verification + And she fills in her account password + And she submits the verify form + Then she is redirected to the Root View + And she sees a table with her profile info + + @requireA18NProfile + @requireExistingUser + @requireEnrolledPhone + @requireMFAGroupsForUser + Scenario: 8.3.3 Mary recovers from a locked account with SMS OTP + Given Mary navigates to the Embedded Widget View + When she inputs her correct email address + And she fills in her incorrect password + And she submits the Login form and locks the account + When she sees a link to unlock her account + And she clicks the link to unlock her account + Then she sees a page to input her username and select Email or Phone to unlock her account + When she inputs her correct email address + Then she selects "Phone" from the available options + And she sees a page saying "Verify with your phone" + Then she clicks the button saying "Receive a code via SMS" + Then she sees a page to input her code + When she fills in the correct code from SMS + And she submits the verify form + Then she sees a page that says "Account Successfully Unlocked!" and to enter the password for verification + And she fills in her account password + And she submits the verify form + Then she is redirected to the Root View + And she sees a table with her profile info diff --git a/samples/embedded-sign-in-widget/src/test/resources/features/self_service_password_reset.feature b/samples/embedded-sign-in-widget/src/test/resources/features/self_service_password_reset.feature index 0f6cf45ac..c74d21851 100644 --- a/samples/embedded-sign-in-widget/src/test/resources/features/self_service_password_reset.feature +++ b/samples/embedded-sign-in-widget/src/test/resources/features/self_service_password_reset.feature @@ -6,7 +6,7 @@ Feature: 8.2: Password Recovery with Embedded Sign In Widget Scenario: 8.2.1 Mary resets the Password through forgot password link Given Mary navigates to the Embedded Widget View When she clicks on the Forgot Password link - Then she sees the page to input the email address + Then she sees the password reset page When she inputs her correct email address And she submits the recovery form And she sees a page saying "Verify with your email"