diff --git a/e2e/fixtures/account/account-page.ts b/e2e/fixtures/account/account-page.ts index 17e9698df..14046cc0b 100644 --- a/e2e/fixtures/account/account-page.ts +++ b/e2e/fixtures/account/account-page.ts @@ -4,22 +4,42 @@ import { BasePage } from "../base/base-page" export class AccountPage extends BasePage { container: Locator accountNav: Locator - mobileAccountNav: Locator + overviewLink: Locator profileLink: Locator addressesLink: Locator ordersLink: Locator logoutLink: Locator + mobileAccountNav: Locator + mobileAccountMainLink : Locator + mobileOverviewLink : Locator + mobileProfileLink : Locator + mobileAddressesLink : Locator + mobileOrdersLink : Locator + mobileLogoutLink : Locator + constructor(page: Page) { super(page) this.container = page.getByTestId("account-page") this.accountNav = this.container.getByTestId("account-nav") + this.overviewLink = this.accountNav.getByTestId("overview-link") + this.profileLink = this.accountNav.getByTestId("profile-link") + this.addressesLink = this.accountNav.getByTestId("addresses-link") + this.ordersLink = this.accountNav.getByTestId("orders-link") + this.logoutLink = this.accountNav.getByTestId("logout-button") + this.mobileAccountNav = this.container.getByTestId("mobile-account-nav") - this.overviewLink = this.container.getByTestId("overview-link") - this.profileLink = this.container.getByTestId("profile-link") - this.addressesLink = this.container.getByTestId("addresses-link") - this.ordersLink = this.container.getByTestId("orders-link") - this.logoutLink = this.container.getByTestId("logout-button") + this.mobileAccountMainLink = this.mobileAccountNav.getByTestId("account-main-link") + this.mobileOverviewLink = this.mobileAccountNav.getByTestId("overview-link") + this.mobileProfileLink = this.mobileAccountNav.getByTestId("profile-link") + this.mobileAddressesLink = this.mobileAccountNav.getByTestId("addresses-link") + this.mobileOrdersLink = this.mobileAccountNav.getByTestId("orders-link") + this.mobileLogoutLink = this.mobileAccountNav.getByTestId("logout-button") + } + + async goto() { + await this.navMenu.navAccountLink.click() + await this.container.waitFor({ state: "visible" }) } } diff --git a/e2e/fixtures/account/overview-page.ts b/e2e/fixtures/account/overview-page.ts index aef19bde2..92b6ce293 100644 --- a/e2e/fixtures/account/overview-page.ts +++ b/e2e/fixtures/account/overview-page.ts @@ -9,10 +9,12 @@ export class OverviewPage extends AccountPage { noOrdersMessage: Locator ordersWrapper: Locator orderWrapper: Locator + overviewWrapper: Locator constructor(page: Page) { super(page) - this.welcomeMessage = this.container.getByTestId("welcome-message") // getAttribute("name") + this.overviewWrapper = this.container.getByTestId("overview-page-wrapper") + this.welcomeMessage = this.container.getByTestId("welcome-message") this.customerEmail = this.container.getByTestId("customer-email") this.profileCompletion = this.container.getByTestId( "customer-profile-completion" @@ -36,4 +38,9 @@ export class OverviewPage extends AccountPage { openButton: order.getByTestId("open-order-button"), } } + + async goto() { + await this.navMenu.navAccountLink.click() + await this.container.waitFor({ state: "visible" }) + } } diff --git a/e2e/fixtures/account/profile-page.ts b/e2e/fixtures/account/profile-page.ts index 7d934b51a..3b49e484c 100644 --- a/e2e/fixtures/account/profile-page.ts +++ b/e2e/fixtures/account/profile-page.ts @@ -3,6 +3,7 @@ import { AccountPage } from "./account-page" import { camelCase } from "lodash" export class ProfilePage extends AccountPage { + profileWrapper: Locator accountNameEditor: Locator accountEmailEditor: Locator accountPhoneEditor: Locator @@ -21,6 +22,24 @@ export class ProfilePage extends AccountPage { passwordSaveButton: Locator billingAddressSaveButton: Locator + savedName: Locator + savedEmail: Locator + savedPhone: Locator + savedPassword: Locator + savedBillingAddress: Locator + + nameSuccessMessage: Locator + emailSuccessMessage: Locator + phoneSuccessMessage: Locator + passwordSuccessMessage: Locator + billingAddressSuccessMessage: Locator + + nameErrorMessage: Locator + emailErrorMessage: Locator + phoneErrorMessage: Locator + passwordErrorMessage: Locator + billingAddressErrorMessage: Locator + emailInput: Locator firstNameInput: Locator lastNameInput: Locator @@ -43,6 +62,7 @@ export class ProfilePage extends AccountPage { constructor(page: Page) { super(page) + this.profileWrapper = page.getByTestId("profile-page-wrapper") this.accountNameEditor = this.container.getByTestId("account-name-editor") this.accountEmailEditor = this.container.getByTestId("account-email-editor") this.accountPhoneEditor = this.container.getByTestId("account-phone-editor") @@ -69,6 +89,32 @@ export class ProfilePage extends AccountPage { this.billingAddressSaveButton = this.accountBillingAddressEditor.getByTestId("save-button") + this.savedName = this.accountNameEditor.getByTestId("current-info") + this.savedEmail = this.accountEmailEditor.getByTestId("current-info") + this.savedPhone = this.accountPhoneEditor.getByTestId("current-info") + this.savedPassword = this.accountPasswordEditor.getByTestId("current-info") + this.savedBillingAddress = + this.accountBillingAddressEditor.getByTestId("current-info") + this.nameSuccessMessage = + this.accountNameEditor.getByTestId("success-message") + this.emailSuccessMessage = + this.accountEmailEditor.getByTestId("success-message") + this.phoneSuccessMessage = + this.accountPhoneEditor.getByTestId("success-message") + this.passwordSuccessMessage = + this.accountPasswordEditor.getByTestId("success-message") + this.billingAddressSuccessMessage = + this.accountBillingAddressEditor.getByTestId("success-message") + this.nameErrorMessage = this.accountNameEditor.getByTestId("error-message") + this.emailErrorMessage = + this.accountEmailEditor.getByTestId("error-message") + this.phoneErrorMessage = + this.accountPhoneEditor.getByTestId("error-message") + this.passwordErrorMessage = + this.accountPasswordEditor.getByTestId("error-message") + this.billingAddressErrorMessage = + this.accountBillingAddressEditor.getByTestId("error-message") + this.firstNameInput = page.getByTestId("first-name-input") this.lastNameInput = page.getByTestId("last-name-input") this.emailInput = page.getByTestId("email-input") @@ -111,4 +157,10 @@ export class ProfilePage extends AccountPage { } return o } + + async goto() { + super.goto() + await this.profileLink.click() + await this.profileWrapper.waitFor({ state: "visible" }) + } } diff --git a/e2e/fixtures/base/nav-menu.ts b/e2e/fixtures/base/nav-menu.ts index 239549db7..86c2410de 100644 --- a/e2e/fixtures/base/nav-menu.ts +++ b/e2e/fixtures/base/nav-menu.ts @@ -4,6 +4,7 @@ export class NavMenu { page: Page navMenuButton: Locator navMenu: Locator + navAccountLink: Locator homeLink: Locator storeLink: Locator searchLink: Locator @@ -17,6 +18,7 @@ export class NavMenu { this.page = page this.navMenuButton = page.getByTestId("nav-menu-button") this.navMenu = page.getByTestId("nav-menu-popup") + this.navAccountLink = page.getByTestId("nav-account-link") this.homeLink = this.navMenu.getByTestId("home-link") this.storeLink = this.navMenu.getByTestId("store-link") this.searchLink = this.navMenu.getByTestId("search-link") diff --git a/e2e/tests/authenticated/profile.spec.ts b/e2e/tests/authenticated/profile.spec.ts new file mode 100644 index 000000000..f73b53462 --- /dev/null +++ b/e2e/tests/authenticated/profile.spec.ts @@ -0,0 +1,243 @@ +import { test, expect } from "../../index" + +test.describe("Account profile tests", () => { + test("Profile completed update flow", async ({ + accountOverviewPage: overviewPage, + accountProfilePage: profilePage, + }) => { + await overviewPage.goto() + await expect(overviewPage.profileCompletion).toHaveText("50%") + + await test.step("navigate to the profile page", async () => { + await profilePage.profileLink.click() + await expect(profilePage.profileWrapper).toBeVisible() + }) + + await test.step("update the saved profile phone number", async () => { + await expect(profilePage.savedPhone).toHaveText("null") + await profilePage.phoneEditButton.click() + await profilePage.phoneInput.fill("8888888888") + await profilePage.phoneSaveButton.click() + await expect(profilePage.phoneSuccessMessage).toBeVisible() + await expect(profilePage.savedPhone).toHaveText("8888888888") + }) + + await test.step("verify the profile completion state and go back to the profile page", async () => { + await profilePage.overviewLink.click() + await expect(overviewPage.profileCompletion).toHaveText("75%") + + await profilePage.profileLink.click() + await expect(profilePage.profileWrapper).toBeVisible() + }) + + await test.step("enter in the billing address", async () => { + await expect(profilePage.savedBillingAddress).toContainText( + "No billing address" + ) + await profilePage.billingAddressEditButton.click() + await profilePage.billingFirstNameInput.fill("First") + await profilePage.billingLastNameInput.fill("Last") + await profilePage.billingAddress1Input.fill("123 Fake Street") + await profilePage.billingPostcalCodeInput.fill("11111") + await profilePage.billingCityInput.fill("Springdale") + await profilePage.billingProvinceInput.fill("IL") + await profilePage.billingCountryCodeSelect.selectOption({ + label: "United States", + }) + await profilePage.billingAddressSaveButton.click() + await expect(profilePage.billingAddressSuccessMessage).toBeVisible() + }) + + await test.step("profile completion state", async () => { + await profilePage.overviewLink.click() + await expect(overviewPage.profileCompletion).toHaveText("100%") + + await profilePage.goto() + await expect(profilePage.savedBillingAddress).toContainText("First Last") + await expect(profilePage.savedBillingAddress).toContainText( + "123 Fake Street" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "11111, Springdale" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "United States" + ) + }) + }) + + test("Profile changes persist across page refreshes and logouts", async ({ + page, + loginPage, + accountOverviewPage: overviewPage, + accountProfilePage: profilePage, + }) => { + await overviewPage.goto() + await expect(overviewPage.profileCompletion).toHaveText("50%") + + await test.step("navigate to the profile page", async () => { + await profilePage.profileLink.click() + await expect(profilePage.profileWrapper).toBeVisible() + }) + + await test.step("update the first and last name", async () => { + await profilePage.nameEditButton.click() + await profilePage.firstNameInput.fill("FirstNew") + await profilePage.lastNameInput.fill("LastNew") + await profilePage.nameSaveButton.click() + await profilePage.nameSuccessMessage.waitFor({ state: "visible" }) + }) + + await test.step("update the saved profile phone number", async () => { + await expect(profilePage.savedPhone).toHaveText("null") + await profilePage.phoneEditButton.click() + await profilePage.phoneInput.fill("8888888888") + await profilePage.phoneSaveButton.click() + await expect(profilePage.phoneSuccessMessage).toBeVisible() + await expect(profilePage.savedPhone).toHaveText("8888888888") + }) + + await test.step("enter in the billing address", async () => { + await expect(profilePage.savedBillingAddress).toContainText( + "No billing address" + ) + await profilePage.billingAddressEditButton.click() + await profilePage.billingFirstNameInput.fill("First") + await profilePage.billingLastNameInput.fill("Last") + await profilePage.billingAddress1Input.fill("123 Fake Street") + await profilePage.billingPostcalCodeInput.fill("11111") + await profilePage.billingCityInput.fill("Springdale") + await profilePage.billingProvinceInput.fill("IL") + await profilePage.billingCountryCodeSelect.selectOption({ + label: "United States", + }) + await profilePage.billingAddressSaveButton.click() + await expect(profilePage.billingAddressSuccessMessage).toBeVisible() + }) + + await test.step("Refresh page and verify information saved is still there", async () => { + await page.reload() + await expect(profilePage.savedName).toContainText("FirstNew") + await expect(profilePage.savedName).toContainText("LastNew") + await expect(profilePage.savedPhone).toContainText("8888888888") + + await expect(profilePage.savedBillingAddress).toContainText("First Last") + await expect(profilePage.savedBillingAddress).toContainText( + "123 Fake Street" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "11111, Springdale" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "United States" + ) + }) + + await test.step("Log out and log back in", async () => { + await profilePage.logoutLink.click() + await expect(loginPage.container).toBeVisible() + await loginPage.emailInput.fill("test@example.com") + await loginPage.passwordInput.fill("password") + await loginPage.signInButton.click() + await overviewPage.overviewWrapper.waitFor({ state: "visible" }) + await overviewPage.profileLink.click() + await profilePage.profileWrapper.waitFor({ state: "visible" }) + }) + + await test.step("Verify the saved profile information is correct", async () => { + await expect(profilePage.savedName).toContainText("FirstNew") + await expect(profilePage.savedName).toContainText("LastNew") + await expect(profilePage.savedPhone).toContainText("8888888888") + + await expect(profilePage.savedBillingAddress).toContainText("First Last") + await expect(profilePage.savedBillingAddress).toContainText( + "123 Fake Street" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "11111, Springdale" + ) + await expect(profilePage.savedBillingAddress).toContainText( + "United States" + ) + }) + }) + + test("Verifies password changes work correctly", async ({ + loginPage, + accountProfilePage: profilePage, + accountOverviewPage: overviewPage, + }) => { + await test.step("Navigate to the account Profile page", async () => { + await overviewPage.goto() + await profilePage.profileLink.click() + }) + + await test.step("Update the password", async () => { + await profilePage.passwordEditButton.click() + await profilePage.oldPasswordInput.fill("password") + await profilePage.newPasswordInput.fill("updated-password") + await profilePage.confirmPasswordInput.fill("updated-password") + await profilePage.passwordSaveButton.click() + await expect(profilePage.passwordSuccessMessage).toBeVisible() + }) + + await test.step("logout and log back in", async () => { + await profilePage.logoutLink.click() + await expect(loginPage.container).toBeVisible() + await loginPage.emailInput.fill("test@example.com") + await loginPage.passwordInput.fill("updated-password") + await loginPage.signInButton.click() + await expect(overviewPage.container).toBeVisible() + }) + }) + + test("Check if changing email address updates user correctly", async ({ + loginPage, + accountProfilePage: profilePage, + accountOverviewPage: accountPage, + }) => { + await test.step("Update the user email", async () => { + await accountPage.goto() + await accountPage.welcomeMessage.waitFor({ state: "visible" }) + await accountPage.profileLink.click() + await profilePage.profileWrapper.waitFor({ state: "visible" }) + await profilePage.emailEditButton.click() + await profilePage.emailInput.fill("test-111@example.com") + await profilePage.emailSaveButton.click() + await profilePage.emailSuccessMessage.waitFor({ state: "visible" }) + }) + + await test.step("Try logging in again with the old email", async () => { + await profilePage.logoutLink.click() + await loginPage.container.waitFor({ state: "visible" }) + await loginPage.emailInput.fill("test@example.com") + await loginPage.passwordInput.fill("password") + await loginPage.signInButton.click() + await loginPage.errorMessage.waitFor({ state: "visible" }) + }) + + await test.step("Login with the new email", async () => { + await loginPage.emailInput.fill("test-111@example.com") + await loginPage.signInButton.click() + await accountPage.welcomeMessage.waitFor({ state: "visible" }) + }) + + await test.step("Set the email back to test@example.com", async () => { + await accountPage.profileLink.click() + await profilePage.profileWrapper.waitFor({ state: "visible" }) + await profilePage.emailEditButton.click() + await profilePage.emailInput.fill("test@example.com") + await profilePage.emailSaveButton.click() + await profilePage.emailSuccessMessage.waitFor({ state: "visible" }) + }) + + await test.step("Try logging out and logging in with the first email", async () => { + await profilePage.logoutLink.click() + await loginPage.container.waitFor({ state: "visible" }) + await loginPage.emailInput.fill("test@example.com") + await loginPage.passwordInput.fill("password") + await loginPage.signInButton.click() + await accountPage.welcomeMessage.waitFor({ state: "visible" }) + }) + }) +}) diff --git a/src/modules/account/components/account-info/index.tsx b/src/modules/account/components/account-info/index.tsx index f7f00c21a..f0c4937d8 100644 --- a/src/modules/account/components/account-info/index.tsx +++ b/src/modules/account/components/account-info/index.tsx @@ -98,6 +98,7 @@ const AccountInfo = ({ "max-h-0 opacity-0": !isError, } )} + data-testid="error-message" > {errorMessage} diff --git a/src/modules/account/components/account-nav/index.tsx b/src/modules/account/components/account-nav/index.tsx index f3764f09f..3f4f4c8fd 100644 --- a/src/modules/account/components/account-nav/index.tsx +++ b/src/modules/account/components/account-nav/index.tsx @@ -31,6 +31,7 @@ const AccountNav = ({ <> @@ -170,9 +171,10 @@ type AccountNavLinkProps = { href: string route: string children: React.ReactNode + 'data-testid'?: string } -const AccountNavLink = ({ href, route, children }: AccountNavLinkProps) => { +const AccountNavLink = ({ href, route, children, 'data-testid': dataTestId }: AccountNavLinkProps) => { const { countryCode }: { countryCode: string } = useParams() const active = route.split(countryCode)[1] === href @@ -182,6 +184,7 @@ const AccountNavLink = ({ href, route, children }: AccountNavLinkProps) => { className={clx("text-ui-fg-subtle hover:text-ui-fg-base", { "text-ui-fg-base font-semibold": active, })} + data-testid={dataTestId} > {children} diff --git a/src/modules/account/components/overview/index.tsx b/src/modules/account/components/overview/index.tsx index 183486e30..080dcc9da 100644 --- a/src/modules/account/components/overview/index.tsx +++ b/src/modules/account/components/overview/index.tsx @@ -12,7 +12,7 @@ type OverviewProps = { const Overview = ({ customer, orders }: OverviewProps) => { return ( -
+
Hello {customer?.first_name} diff --git a/src/modules/account/components/profile-billing-address/index.tsx b/src/modules/account/components/profile-billing-address/index.tsx index 868120cf1..a26bc32f9 100644 --- a/src/modules/account/components/profile-billing-address/index.tsx +++ b/src/modules/account/components/profile-billing-address/index.tsx @@ -58,7 +58,7 @@ const ProfileBillingAddress: React.FC = ({ )?.label || customer.billing_address.country_code?.toUpperCase() return ( -
+
{customer.billing_address.first_name}{" "} {customer.billing_address.last_name} @@ -87,6 +87,7 @@ const ProfileBillingAddress: React.FC = ({ isSuccess={successState} isError={!!state.error} clearState={clearState} + data-testid="account-billing-address-editor" >
diff --git a/src/modules/account/components/profile-email/index.tsx b/src/modules/account/components/profile-email/index.tsx index 340531d2b..44124722f 100644 --- a/src/modules/account/components/profile-email/index.tsx +++ b/src/modules/account/components/profile-email/index.tsx @@ -38,6 +38,7 @@ const ProfileEmail: React.FC = ({ customer }) => { isError={!!state.error} errorMessage={state.error} clearState={clearState} + data-testid="account-email-editor" >
= ({ customer }) => { isError={!!state.error} errorMessage={state.error} clearState={clearState} + data-testid="account-password-editor" >
= ({ customer }) => { isError={!!state.error} errorMessage={state.error} clearState={clearState} + data-testid="account-phone-editor" >