diff --git a/e2e/scripts/pageHelpers.js b/e2e/scripts/pageHelpers.js index b67b4a6e6c..f0ac3aac2e 100644 --- a/e2e/scripts/pageHelpers.js +++ b/e2e/scripts/pageHelpers.js @@ -1,138 +1,166 @@ +import { default as AxeBuilder } from "@axe-core/playwright"; const { expect } = require("@playwright/test"); const config = require("../config"); -const { getCreditCardExpiry } = require("../scripts/utils.js") +const { + getCreditCardExpiry, + createViolationFingerprints, +} = require("../scripts/utils.js"); /** - * Navigates to the `Cotton Turtleneck Sweater` PDP (Product Detail Page) on mobile + * Navigates to the `Cotton Turtleneck Sweater` PDP (Product Detail Page) on mobile * with the black variant selected - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright */ -export const navigateToPDPMobile = async ({page}) => { - // Home page - await page.goto(config.RETAIL_APP_HOME); - - await page.getByLabel("Menu", { exact: true }).click(); - - // SSR nav loads top level categories as direct links so we wait till all sub-categories load in the accordion - const categoryAccordion = page.locator( - "#category-nav .chakra-accordion__button svg+:text('Womens')" - ); - await categoryAccordion.waitFor(); - - await page.getByRole("button", { name: "Womens" }).click(); - - const clothingNav = page.getByRole("button", { name: "Clothing" }); - - await clothingNav.waitFor(); - - await clothingNav.click(); - - const topsLink = page.getByLabel('Womens').getByRole("link", { name: "Tops" }); - await topsLink.click(); - // Wait for the nav menu to close first - await topsLink.waitFor({state: 'hidden'}) - - await expect(page.getByRole("heading", { name: "Tops" })).toBeVisible(); - - // PLP - const productTile = page.getByRole("link", { - name: /Cotton Turtleneck Sweater/i, - }); - await productTile.scrollIntoViewIfNeeded() - // selecting swatch - const productTileImg = productTile.locator("img"); - await productTileImg.waitFor({state: 'visible'}) - const initialSrc = await productTileImg.getAttribute("src"); - await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); - - await productTile.getByLabel(/Black/, { exact: true }).click(); - // Make sure the image src has changed - await expect(async () => { - const newSrc = await productTileImg.getAttribute("src") - expect(newSrc).not.toBe(initialSrc) - }).toPass() - await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); - await productTile.click(); -} +export const navigateToPDPMobile = async ({ page }) => { + // Home page + await page.goto(config.RETAIL_APP_HOME); + + await page.getByLabel("Menu", { exact: true }).click(); + + // SSR nav loads top level categories as direct links so we wait till all sub-categories load in the accordion + const categoryAccordion = page.locator( + "#category-nav .chakra-accordion__button svg+:text('Womens')" + ); + await categoryAccordion.waitFor(); + + await page.getByRole("button", { name: "Womens" }).click(); + + const clothingNav = page.getByRole("button", { name: "Clothing" }); + + await clothingNav.waitFor(); + + await clothingNav.click(); + + const topsLink = page + .getByLabel("Womens") + .getByRole("link", { name: "Tops" }); + await topsLink.click(); + // Wait for the nav menu to close first + await topsLink.waitFor({ state: "hidden" }); + + await expect(page.getByRole("heading", { name: "Tops" })).toBeVisible(); + + // PLP + const productTile = page.getByRole("link", { + name: /Cotton Turtleneck Sweater/i, + }); + await productTile.scrollIntoViewIfNeeded(); + // selecting swatch + const productTileImg = productTile.locator("img"); + await productTileImg.waitFor({ state: "visible" }); + const initialSrc = await productTileImg.getAttribute("src"); + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); + + await productTile.getByLabel(/Black/, { exact: true }).click(); + // Make sure the image src has changed + await expect(async () => { + const newSrc = await productTileImg.getAttribute("src"); + expect(newSrc).not.toBe(initialSrc); + }).toPass(); + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["PLP-mobile-a11y-snapshot"] + ); + await productTile.click(); +}; /** - * Navigates to the `Cotton Turtleneck Sweater` PDP (Product Detail Page) on Desktop + * Navigates to the `Cotton Turtleneck Sweater` PDP (Product Detail Page) on Desktop * with the black variant selected. - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright */ -export const navigateToPDPDesktop = async ({page}) => { - await page.goto(config.RETAIL_APP_HOME); - - await page.getByRole("link", { name: "Womens" }).hover(); - const topsNav = await page.getByRole("link", { name: "Tops", exact: true }); - await expect(topsNav).toBeVisible(); - - await topsNav.click(); - - // PLP - const productTile = page.getByRole("link", { - name: /Cotton Turtleneck Sweater/i, - }); - // selecting swatch - const productTileImg = productTile.locator("img"); - await productTileImg.waitFor({state: 'visible'}) - const initialSrc = await productTileImg.getAttribute("src"); - await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); - - await productTile.getByLabel(/Black/, { exact: true }).hover(); - // Make sure the image src has changed - await expect(async () => { - const newSrc = await productTileImg.getAttribute("src") - expect(newSrc).not.toBe(initialSrc) - }).toPass() - await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); - await productTile.click(); -} +export const navigateToPDPDesktop = async ({ page }) => { + await page.goto(config.RETAIL_APP_HOME); + + await page.getByRole("link", { name: "Womens" }).hover(); + const topsNav = await page.getByRole("link", { name: "Tops", exact: true }); + await expect(topsNav).toBeVisible(); + + await topsNav.click(); + + // PLP + const productTile = page.getByRole("link", { + name: /Cotton Turtleneck Sweater/i, + }); + // selecting swatch + const productTileImg = productTile.locator("img"); + await productTileImg.waitFor({ state: "visible" }); + const initialSrc = await productTileImg.getAttribute("src"); + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); + + await productTile.getByLabel(/Black/, { exact: true }).hover(); + // Make sure the image src has changed + await expect(async () => { + const newSrc = await productTileImg.getAttribute("src"); + expect(newSrc).not.toBe(initialSrc); + }).toPass(); + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["PLP-desktop-a11y-snapshot"] + ); + await productTile.click(); +}; /** * Adds the `Cotton Turtleneck Sweater` product to the cart with the variant: * Color: Black * Size: L - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright * @param {Boolean} options.isMobile - Flag to indicate if device type is mobile or not, defaulted to false */ -export const addProductToCart = async ({page, isMobile = false}) => { - // Navigate to Cotton Turtleneck Sweater with Black color variant selected - if(isMobile) { - await navigateToPDPMobile({page}) - } else { - await navigateToPDPDesktop({page}) - } - - // PDP - await expect( - page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) - ).toBeVisible(); - await page.getByRole("radio", { name: "L", exact: true }).click(); - - await page.locator("button[data-testid='quantity-increment']").click(); - - // Selected Size and Color texts are broken into multiple elements on the page. - // So we need to look at the page URL to verify selected variants - const updatedPageURL = await page.url(); - const params = updatedPageURL.split("?")[1]; - expect(params).toMatch(/size=9LG/i); - expect(params).toMatch(/color=JJ169XX/i); - await page.getByRole("button", { name: /Add to Cart/i }).click(); - - const addedToCartModal = page.getByText(/2 items added to cart/i); - - await addedToCartModal.waitFor(); - - await page.getByLabel("Close").click(); -} +export const addProductToCart = async ({ page, isMobile = false }) => { + // Navigate to Cotton Turtleneck Sweater with Black color variant selected + if (isMobile) { + await navigateToPDPMobile({ page }); + } else { + await navigateToPDPDesktop({ page }); + } + + // PDP + await expect( + page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) + ).toBeVisible(); + + await page.getByRole("radio", { name: "L", exact: true }).click(); + + await page.locator("button[data-testid='quantity-increment']").click(); + + // Selected Size and Color texts are broken into multiple elements on the page. + // So we need to look at the page URL to verify selected variants + const updatedPageURL = await page.url(); + const params = updatedPageURL.split("?")[1]; + expect(params).toMatch(/size=9LG/i); + expect(params).toMatch(/color=JJ169XX/i); + await page.getByRole("button", { name: /Add to Cart/i }).click(); + + const addedToCartModal = page.getByText(/2 items added to cart/i); + + await addedToCartModal.waitFor(); + + await page.getByLabel("Close").click(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["PDP-a11y-snapshot"] + ); +}; /** * Registers a shopper with provided user credentials - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright * @param {Object} options.userCredentials - Object containing user credentials with the following properties: * - firstName @@ -141,143 +169,139 @@ export const addProductToCart = async ({page, isMobile = false}) => { * - password * @param {Boolean} options.isMobile - flag to indicate if device type is mobile or not, defaulted to false */ -export const registerShopper = async ({page, userCredentials, isMobile = false}) => { - // Create Account and Sign In - await page.goto(config.RETAIL_APP_HOME + "/registration"); +export const registerShopper = async ({ + page, + userCredentials, + isMobile = false, +}) => { + // Create Account and Sign In + await page.goto(config.RETAIL_APP_HOME + "/registration"); - await page.waitForLoadState(); + await page.waitForLoadState(); - const registrationFormHeading = page.getByText(/Let's get started!/i); - await registrationFormHeading.waitFor(); + const registrationFormHeading = page.getByText(/Let's get started!/i); + await registrationFormHeading.waitFor(); - await page - .locator("input#firstName") - .fill(userCredentials.firstName); - await page - .locator("input#lastName") - .fill(userCredentials.lastName); - await page.locator("input#email").fill(userCredentials.email); - await page - .locator("input#password") - .fill(userCredentials.password); + await page.locator("input#firstName").fill(userCredentials.firstName); + await page.locator("input#lastName").fill(userCredentials.lastName); + await page.locator("input#email").fill(userCredentials.email); + await page.locator("input#password").fill(userCredentials.password); - await page.getByRole("button", { name: /Create Account/i }).click(); + await page.getByRole("button", { name: /Create Account/i }).click(); - await page.waitForLoadState(); + await page.waitForLoadState(); + + await expect( + page.getByRole("heading", { name: /Account Details/i }) + ).toBeVisible(); + if (!isMobile) { await expect( - page.getByRole("heading", { name: /Account Details/i }) + page.getByRole("heading", { name: /My Account/i }) ).toBeVisible(); + } - if(!isMobile) { - await expect( - page.getByRole("heading", { name: /My Account/i }) - ).toBeVisible(); - } - - await expect(page.getByText(/Email/i)).toBeVisible(); - await expect(page.getByText(userCredentials.email)).toBeVisible(); -} + await expect(page.getByText(/Email/i)).toBeVisible(); + await expect(page.getByText(userCredentials.email)).toBeVisible(); +}; /** * Validates that the `Cotton Turtleneck Sweater` product appears in the Order History page - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright */ -export const validateOrderHistory = async ({page}) => { - await page.goto(config.RETAIL_APP_HOME + "/account/orders"); - await expect( - page.getByRole("heading", { name: /Order History/i }) - ).toBeVisible(); - - await page.getByRole('link', { name: 'View details' }).click(); - - await expect( - page.getByRole("heading", { name: /Order Details/i }) - ).toBeVisible(); - await expect( - page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) - ).toBeVisible(); - await expect(page.getByText(/Color: Black/i)).toBeVisible(); - await expect(page.getByText(/Size: L/i)).toBeVisible(); -} +export const validateOrderHistory = async ({ page }) => { + await page.goto(config.RETAIL_APP_HOME + "/account/orders"); + await expect( + page.getByRole("heading", { name: /Order History/i }) + ).toBeVisible(); + + await page.getByRole("link", { name: "View details" }).click(); + + await expect( + page.getByRole("heading", { name: /Order Details/i }) + ).toBeVisible(); + await expect( + page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) + ).toBeVisible(); + await expect(page.getByText(/Color: Black/i)).toBeVisible(); + await expect(page.getByText(/Size: L/i)).toBeVisible(); +}; /** * Validates that the `Cotton Turtleneck Sweater` product appears in the Wishlist page - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright */ -export const validateWishlist = async ({page}) => { - await page.goto(config.RETAIL_APP_HOME + "/account/wishlist"); +export const validateWishlist = async ({ page }) => { + await page.goto(config.RETAIL_APP_HOME + "/account/wishlist"); - await expect( - page.getByRole("heading", { name: /Wishlist/i }) - ).toBeVisible(); - - await expect( - page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) - ).toBeVisible(); - await expect(page.getByText(/Color: Black/i)).toBeVisible() - await expect(page.getByText(/Size: L/i)).toBeVisible() -} + await expect(page.getByRole("heading", { name: /Wishlist/i })).toBeVisible(); + + await expect( + page.getByRole("heading", { name: /Cotton Turtleneck Sweater/i }) + ).toBeVisible(); + await expect(page.getByText(/Color: Black/i)).toBeVisible(); + await expect(page.getByText(/Size: L/i)).toBeVisible(); +}; /** * Attempts to log in a shopper with provided user credentials. - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright * @param {Object} options.userCredentials - Object containing user credentials with the following properties: * - firstName * - lastName * - email * - password - * + * * @return {Boolean} - denotes whether or not login was successful */ -export const loginShopper = async ({page, userCredentials}) => { - try { - await page.goto(config.RETAIL_APP_HOME + "/login"); - await page.locator("input#email").fill(userCredentials.email); - await page - .locator("input#password") - .fill(userCredentials.password); - await page.getByRole("button", { name: /Sign In/i }).click(); - - await page.waitForLoadState(); - - // redirected to Account Details page after logging in - await expect( - page.getByRole("heading", { name: /Account Details/i }) - ).toBeVisible({ timeout: 2000 }); - return true; - } catch { - return false; - } -} +export const loginShopper = async ({ page, userCredentials }) => { + try { + await page.goto(config.RETAIL_APP_HOME + "/login"); + await page.locator("input#email").fill(userCredentials.email); + await page.locator("input#password").fill(userCredentials.password); + await page.getByRole("button", { name: /Sign In/i }).click(); + + await page.waitForLoadState(); + + // redirected to Account Details page after logging in + await expect( + page.getByRole("heading", { name: /Account Details/i }) + ).toBeVisible({ timeout: 2000 }); + return true; + } catch { + return false; + } +}; /** * Search for products by query string that takes you to the PLP - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright * @param {String} options.query - Product name other product related descriptors to search for * @param {Object} options.isMobile - Flag to indicate if device type is mobile or not, defaulted to false */ -export const searchProduct = async ({page, query, isMobile = false}) => { - await page.goto(config.RETAIL_APP_HOME); +export const searchProduct = async ({ page, query, isMobile = false }) => { + await page.goto(config.RETAIL_APP_HOME); - // For accessibility reasons, we have two search bars - // one for desktop and one for mobile depending on your device type - const searchInputs = page.locator('input[aria-label="Search for products..."]'); + // For accessibility reasons, we have two search bars + // one for desktop and one for mobile depending on your device type + const searchInputs = page.locator( + 'input[aria-label="Search for products..."]' + ); - let searchInput = isMobile ? searchInputs.nth(1) : searchInputs.nth(0); - await searchInput.fill(query); - await searchInput.press('Enter'); + let searchInput = isMobile ? searchInputs.nth(1) : searchInputs.nth(0); + await searchInput.fill(query); + await searchInput.press("Enter"); - await page.waitForLoadState(); -} + await page.waitForLoadState(); +}; /** * Checkout products that are in the cart - * + * * @param {Object} options.page - Object that represents a tab/window in the browser provided by playwright * @param {Object} options.userCredentials - Object containing user credentials with the following properties: * - firstName @@ -286,82 +310,91 @@ export const searchProduct = async ({page, query, isMobile = false}) => { * - password */ export const checkoutProduct = async ({ page, userCredentials }) => { - await page.getByRole("link", { name: "Proceed to Checkout" }).click(); - - await expect( - page.getByRole("heading", { name: /Contact Info/i }) - ).toBeVisible(); + await page.getByRole("link", { name: "Proceed to Checkout" }).click(); - await page.locator("input#email").fill("test@gmail.com"); + await expect( + page.getByRole("heading", { name: /Contact Info/i }) + ).toBeVisible(); - await page.getByRole("button", { name: /Checkout as guest/i }).click(); + await page.locator("input#email").fill("test@gmail.com"); - // Confirm the email input toggles to show edit button on clicking "Checkout as guest" - const step0Card = page.locator("div[data-testid='sf-toggle-card-step-0']"); + await page.getByRole("button", { name: /Checkout as guest/i }).click(); - await expect(step0Card.getByRole("button", { name: /Edit/i })).toBeVisible(); + // Confirm the email input toggles to show edit button on clicking "Checkout as guest" + const step0Card = page.locator("div[data-testid='sf-toggle-card-step-0']"); - await expect( - page.getByRole("heading", { name: /Shipping Address/i }) - ).toBeVisible(); - - await page.locator("input#firstName").fill(userCredentials.firstName); - await page.locator("input#lastName").fill(userCredentials.lastName); - await page.locator("input#phone").fill(userCredentials.phone); - await page - .locator("input#address1") - .fill(userCredentials.address.street); - await page.locator("input#city").fill(userCredentials.address.city); - await page - .locator("select#stateCode") - .selectOption(userCredentials.address.state); - await page - .locator("input#postalCode") - .fill(userCredentials.address.zipcode); - - await page - .getByRole("button", { name: /Continue to Shipping Method/i }) - .click(); - - // Confirm the shipping details form toggles to show edit button on clicking "Checkout as guest" - const step1Card = page.locator("div[data-testid='sf-toggle-card-step-1']"); - - await expect(step1Card.getByRole("button", { name: /Edit/i })).toBeVisible(); - - await expect( - page.getByRole("heading", { name: /Shipping & Gift Options/i }) - ).toBeVisible(); + await expect(step0Card.getByRole("button", { name: /Edit/i })).toBeVisible(); - try { - // sometimes the shipping & gifts section gets skipped - // so there is no 'Continue to payment' button available - const continueToPayment = page.getByRole("button", { - name: /Continue to Payment/i - }); - await expect(continueToPayment).toBeVisible({ timeout: 2000 }); - await continueToPayment.click(); - } catch { + await expect( + page.getByRole("heading", { name: /Shipping Address/i }) + ).toBeVisible(); - } + await page.locator("input#firstName").fill(userCredentials.firstName); + await page.locator("input#lastName").fill(userCredentials.lastName); + await page.locator("input#phone").fill(userCredentials.phone); + await page.locator("input#address1").fill(userCredentials.address.street); + await page.locator("input#city").fill(userCredentials.address.city); + await page + .locator("select#stateCode") + .selectOption(userCredentials.address.state); + await page.locator("input#postalCode").fill(userCredentials.address.zipcode); - await expect(page.getByRole("heading", { name: /Payment/i })).toBeVisible(); - const creditCardExpiry = getCreditCardExpiry(); + await page + .getByRole("button", { name: /Continue to Shipping Method/i }) + .click(); - await page.locator("input#number").fill("4111111111111111"); - await page.locator("input#holder").fill("John Doe"); - await page.locator("input#expiry").fill(creditCardExpiry); - await page.locator("input#securityCode").fill("213"); + // Confirm the shipping details form toggles to show edit button on clicking "Checkout as guest" + const step1Card = page.locator("div[data-testid='sf-toggle-card-step-1']"); - await page.getByRole("button", { name: /Review Order/i }).click(); + await expect(step1Card.getByRole("button", { name: /Edit/i })).toBeVisible(); - page - .getByRole("button", { name: /Place Order/i }) - .first() - .click(); + await expect( + page.getByRole("heading", { name: /Shipping & Gift Options/i }) + ).toBeVisible(); - // order confirmation - const orderConfirmationHeading = page.getByRole("heading", { - name: /Thank you for your order!/i, + try { + // sometimes the shipping & gifts section gets skipped + // so there is no 'Continue to payment' button available + const continueToPayment = page.getByRole("button", { + name: /Continue to Payment/i, }); - await orderConfirmationHeading.waitFor(); -} + await expect(continueToPayment).toBeVisible({ timeout: 2000 }); + await continueToPayment.click(); + } catch {} + + await expect(page.getByRole("heading", { name: /Payment/i })).toBeVisible(); + const creditCardExpiry = getCreditCardExpiry(); + + await page.locator("input#number").fill("4111111111111111"); + await page.locator("input#holder").fill("John Doe"); + await page.locator("input#expiry").fill(creditCardExpiry); + await page.locator("input#securityCode").fill("213"); + + await page.getByRole("button", { name: /Review Order/i }).click(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["Checkout-a11y-snapshots", "review-order"] + ); + + page + .getByRole("button", { name: /Place Order/i }) + .first() + .click(); + + // order confirmation + const orderConfirmationHeading = page.getByRole("heading", { + name: /Thank you for your order!/i, + }); + await orderConfirmationHeading.waitFor(); + const orderConfirmA11yScan = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(orderConfirmA11yScan)).toMatchSnapshot([ + "Checkout-a11y-snapshots", + "order-confirmation", + ]); +}; diff --git a/e2e/scripts/utils.js b/e2e/scripts/utils.js index 42dfe801e9..90015619f8 100644 --- a/e2e/scripts/utils.js +++ b/e2e/scripts/utils.js @@ -97,7 +97,22 @@ const generateUserCredentials = function () { return user; }; +// https://playwright.dev/docs/accessibility-testing#using-snapshots-to-allow-specific-known-issues +function createViolationFingerprints(accessibilityScanResults) { + const violationFingerprints = accessibilityScanResults.violations.map( + (violation) => ({ + rule: violation.id, + // These are CSS selectors which uniquely identify each element with + // a violation of the rule in question. + targets: violation.nodes.map((node) => node.target), + }) + ); + + return JSON.stringify(violationFingerprints, null, 2); +} + module.exports = { + createViolationFingerprints, isPrompt, mkdirIfNotExists, diffArrays, diff --git a/e2e/tests/desktop/guest-shopper.spec.js b/e2e/tests/desktop/guest-shopper.spec.js index 29dcf7c325..a7eca9413e 100644 --- a/e2e/tests/desktop/guest-shopper.spec.js +++ b/e2e/tests/desktop/guest-shopper.spec.js @@ -8,17 +8,43 @@ const { test, expect } = require("@playwright/test"); const { generateUserCredentials, + createViolationFingerprints, } = require("../../scripts/utils.js"); -const { addProductToCart, searchProduct, checkoutProduct } = require("../../scripts/pageHelpers.js") +const { + addProductToCart, + searchProduct, + checkoutProduct, +} = require("../../scripts/pageHelpers.js"); +const config = require("../../config"); +const AxeBuilder = require("@axe-core/playwright").default; // 1 const GUEST_USER_CREDENTIALS = generateUserCredentials(); +test.describe("HomePage accessibility", () => { + test("should not have any automatically detectable accessibility issues except the known issues", async ({ + page, + }) => { + await page.goto(config.RETAIL_APP_HOME); + // wait til product tiles are fully load before analyzing + await expect( + page.getByRole("link", { name: /Denim slim skirt/i }) + ).toBeVisible(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect( + createViolationFingerprints(accessibilityScanResults) + ).toMatchSnapshot("HomePage-a11y-snapshot"); + }); +}); + /** * Test that guest shoppers can add a product to cart and go through the entire checkout process, * validating that shopper is able to get to the order summary section */ test("Guest shopper can checkout items as guest", async ({ page }) => { - await addProductToCart({page}) + await addProductToCart({ page }); // cart await page.getByLabel(/My cart/i).click(); @@ -27,7 +53,15 @@ test("Guest shopper can checkout items as guest", async ({ page }) => { page.getByRole("link", { name: /Cotton Turtleneck Sweater/i }) ).toBeVisible(); - await checkoutProduct({page, userCredentials: GUEST_USER_CREDENTIALS }); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["Guest-cart-a11y-snapshots", "landing"] + ); + + await checkoutProduct({ page, userCredentials: GUEST_USER_CREDENTIALS }); await expect( page.getByRole("heading", { name: /Order Summary/i }) @@ -42,7 +76,7 @@ test("Guest shopper can checkout items as guest", async ({ page }) => { * Test that guest shoppers can use the product edit modal on cart page */ test("Guest shopper can edit product item in cart", async ({ page }) => { - await addProductToCart({page}); + await addProductToCart({ page }); // cart await page.getByLabel(/My cart/i).click(); @@ -65,8 +99,8 @@ test("Guest shopper can edit product item in cart", async ({ page }) => { await page.waitForLoadState(); // Product edit modal should be open - await expect(page.getByTestId('product-view')).toBeVisible(); - + await expect(page.getByTestId("product-view")).toBeVisible(); + await page.getByRole("radio", { name: "S", exact: true }).click(); await page.getByRole("radio", { name: "Meadow Violet", exact: true }).click(); await page.getByRole("button", { name: /Update/i }).click(); @@ -74,17 +108,26 @@ test("Guest shopper can edit product item in cart", async ({ page }) => { await page.waitForLoadState(); await expect(page.getByText(/Color: Meadow Violet/i)).toBeVisible(); await expect(page.getByText(/Size: S/i)).toBeVisible(); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["Guest-cart-a11y-snapshots", "editing-item"] + ); }); /** * Test that guest shoppers can add product bundle to cart and successfully checkout */ test("Guest shopper can checkout product bundle", async ({ page }) => { - await searchProduct({page, query: 'bundle'}); + await searchProduct({ page, query: "bundle" }); - await page.getByRole("link", { - name: /Turquoise Jewelry Bundle/i, - }).click(); + await page + .getByRole("link", { + name: /Turquoise Jewelry Bundle/i, + }) + .click(); await page.waitForLoadState(); @@ -108,15 +151,23 @@ test("Guest shopper can checkout product bundle", async ({ page }) => { // bundle child selections with all color gold await expect(page.getByText(/Turquoise and Gold Bracelet/i)).toBeVisible(); await expect(page.getByText(/Turquoise and Gold Necklace/i)).toBeVisible(); - await expect(page.getByText(/Turquoise and Gold Hoop Earring/i)).toBeVisible(); + await expect( + page.getByText(/Turquoise and Gold Hoop Earring/i) + ).toBeVisible(); const qtyText = page.locator('text="Qty: 1"'); const colorGoldText = page.locator('text="Color: Gold"'); await expect(colorGoldText).toHaveCount(3); await expect(qtyText).toHaveCount(3); - await checkoutProduct({page, userCredentials: GUEST_USER_CREDENTIALS }); + await checkoutProduct({ page, userCredentials: GUEST_USER_CREDENTIALS }); + const accessibilityScanResults = await new AxeBuilder({ + page, + }).analyze(); + expect(createViolationFingerprints(accessibilityScanResults)).toMatchSnapshot( + ["Checkout-a11y-snapshots", "bundle-checkout"] + ); await expect( page.getByRole("heading", { name: /Order Summary/i }) ).toBeVisible(); @@ -126,5 +177,7 @@ test("Guest shopper can checkout product bundle", async ({ page }) => { ).toBeVisible(); await expect(page.getByText(/Turquoise and Gold Bracelet/i)).toBeVisible(); await expect(page.getByText(/Turquoise and Gold Necklace/i)).toBeVisible(); - await expect(page.getByText(/Turquoise and Gold Hoop Earring/i)).toBeVisible(); + await expect( + page.getByText(/Turquoise and Gold Hoop Earring/i) + ).toBeVisible(); }); diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/bundle-checkout-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/bundle-checkout-chromium-darwin new file mode 100644 index 0000000000..0a56893f89 --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/bundle-checkout-chromium-darwin @@ -0,0 +1,40 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:r3g\\:" + ] + ] + }, + { + "rule": "color-contrast", + "targets": [ + [ + ".css-a4jxtg" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:r3g\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-chromium-darwin new file mode 100644 index 0000000000..0a56893f89 --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-chromium-darwin @@ -0,0 +1,40 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:r3g\\:" + ] + ] + }, + { + "rule": "color-contrast", + "targets": [ + [ + ".css-a4jxtg" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:r3g\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-chromium-darwin new file mode 100644 index 0000000000..be62aebda8 --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-chromium-darwin @@ -0,0 +1,40 @@ +[ + { + "rule": "aria-prohibited-attr", + "targets": [ + [ + "div[aria-label=\"7.99\"]" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "page-has-heading-one", + "targets": [ + [ + "html" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + ".css-1k2aozt" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/editing-item-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/editing-item-chromium-darwin new file mode 100644 index 0000000000..039da92a7c --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/editing-item-chromium-darwin @@ -0,0 +1,32 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/landing-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/landing-chromium-darwin new file mode 100644 index 0000000000..039da92a7c --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/Guest-cart-a11y-snapshots/landing-chromium-darwin @@ -0,0 +1,32 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/HomePage-a11y-snapshot-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/HomePage-a11y-snapshot-chromium-darwin new file mode 100644 index 0000000000..2b735d115c --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/HomePage-a11y-snapshot-chromium-darwin @@ -0,0 +1,32 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin new file mode 100644 index 0000000000..039da92a7c --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin @@ -0,0 +1,32 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin new file mode 100644 index 0000000000..3dfda960ce --- /dev/null +++ b/e2e/tests/desktop/guest-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin @@ -0,0 +1,43 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + }, + { + "rule": "select-name", + "targets": [ + [ + "#page_sort" + ], + [ + "#pagination" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin b/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin new file mode 100644 index 0000000000..039da92a7c --- /dev/null +++ b/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-chromium-darwin @@ -0,0 +1,32 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin b/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin new file mode 100644 index 0000000000..3dfda960ce --- /dev/null +++ b/e2e/tests/desktop/registered-shopper.spec.js-snapshots/PLP-desktop-a11y-snapshot-chromium-darwin @@ -0,0 +1,43 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\:" + ] + ] + }, + { + "rule": "listitem", + "targets": [ + [ + ".css-427wse:nth-child(1)" + ], + [ + ".css-427wse:nth-child(2)" + ], + [ + ".css-427wse:nth-child(3)" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:Rd95aqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + }, + { + "rule": "select-name", + "targets": [ + [ + "#page_sort" + ], + [ + "#pagination" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/order-confirmation-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/order-confirmation-Mobile-Chrome-darwin new file mode 100644 index 0000000000..1aaaa6562d --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/order-confirmation-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:r3m\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:r3m\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/review-order-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/review-order-Mobile-Chrome-darwin new file mode 100644 index 0000000000..b777a119b6 --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshot/review-order-Mobile-Chrome-darwin @@ -0,0 +1,26 @@ +[ + { + "rule": "aria-prohibited-attr", + "targets": [ + [ + "div[aria-label=\"7.99\"]" + ] + ] + }, + { + "rule": "page-has-heading-one", + "targets": [ + [ + "html" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + ".css-1k2aozt" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-Mobile-Chrome-darwin new file mode 100644 index 0000000000..1aaaa6562d --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/order-confirmation-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:r3m\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:r3m\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-Mobile-Chrome-darwin new file mode 100644 index 0000000000..b777a119b6 --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Checkout-a11y-snapshots/review-order-Mobile-Chrome-darwin @@ -0,0 +1,26 @@ +[ + { + "rule": "aria-prohibited-attr", + "targets": [ + [ + "div[aria-label=\"7.99\"]" + ] + ] + }, + { + "rule": "page-has-heading-one", + "targets": [ + [ + "html" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + ".css-1k2aozt" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-checkout-items-as-guest-1-Mobile-Chrome-darwin.txt b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-checkout-items-as-guest-1-Mobile-Chrome-darwin.txt new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-checkout-items-as-guest-1-Mobile-Chrome-darwin.txt @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-edit-product-item-in-cart-1-Mobile-Chrome-darwin.txt b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-edit-product-item-in-cart-1-Mobile-Chrome-darwin.txt new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/Guest-shopper-can-edit-product-item-in-cart-1-Mobile-Chrome-darwin.txt @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin new file mode 100644 index 0000000000..47d835c55d --- /dev/null +++ b/e2e/tests/mobile/guest-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin @@ -0,0 +1,26 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + }, + { + "rule": "select-name", + "targets": [ + [ + "#pagination" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin new file mode 100644 index 0000000000..df2a99395f --- /dev/null +++ b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PDP-a11y-snapshot-Mobile-Chrome-darwin @@ -0,0 +1,18 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin new file mode 100644 index 0000000000..47d835c55d --- /dev/null +++ b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/PLP-mobile-a11y-snapshot-Mobile-Chrome-darwin @@ -0,0 +1,26 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[autocomplete=\"off\"][type=\"search\"]" + ] + ] + }, + { + "rule": "select-name", + "targets": [ + [ + "#pagination" + ] + ] + } +] \ No newline at end of file diff --git a/e2e/tests/mobile/registered-shopper.spec.js-snapshots/Registered-shopper-can-checkout-items-1-Mobile-Chrome-darwin.txt b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/Registered-shopper-can-checkout-items-1-Mobile-Chrome-darwin.txt new file mode 100644 index 0000000000..e5d8f3eb6e --- /dev/null +++ b/e2e/tests/mobile/registered-shopper.spec.js-snapshots/Registered-shopper-can-checkout-items-1-Mobile-Chrome-darwin.txt @@ -0,0 +1,35 @@ +[ + { + "rule": "aria-allowed-attr", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\:" + ] + ] + }, + { + "rule": "color-contrast", + "targets": [ + [ + ".css-1nz5pv0 > .css-q0shp6.chakra-stack > .css-ahyunb.chakra-skeleton:nth-child(2) > .css-1xix1js.chakra-heading" + ], + [ + ".css-1nz5pv0 > .css-q0shp6.chakra-stack > .css-ahyunb.chakra-skeleton:nth-child(3) > b" + ], + [ + ".css-1nz5pv0 > .css-q0shp6.chakra-stack > .css-ahyunb.chakra-skeleton:nth-child(3) > s" + ], + [ + ".css-14fylt8 > .css-1mfmpuh[type=\"button\"]" + ] + ] + }, + { + "rule": "region", + "targets": [ + [ + "#popover-trigger-\\:RdalaqeqlbpH1\\: > .css-1igwmid.chakra-stack > .chakra-input__group.css-1y0e7gb[data-group=\"true\"] > .css-va76oz[type=\"search\"][aria-label=\"Search for products...\"]" + ] + ] + } +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 51d688d9a0..984ffb15b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "node-fetch": "^2.6.9" }, "devDependencies": { + "@axe-core/playwright": "^4.9.0", "@playwright/test": "^1.36.0", "commander": "^9.5.0", "eslint-plugin-header": "^3.1.1", @@ -27,6 +28,18 @@ "npm": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@axe-core/playwright": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.0.tgz", + "integrity": "sha512-kEr3JPEVUSnKIYp/egV2jvFj+chIjCjPp3K3zlpJMza/CB3TFw8UZNbI9agEC2uMz4YbgAOyzlbUy0QS+OofFA==", + "dev": true, + "dependencies": { + "axe-core": "~4.10.0" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -2325,6 +2338,15 @@ "node": ">= 4.0.0" } }, + "node_modules/axe-core": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.1.tgz", + "integrity": "sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", diff --git a/package.json b/package.json index 6534aced54..cac2b737f1 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "@playwright/test": "^1.36.0", + "@axe-core/playwright": "^4.9.0", "commander": "^9.5.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-no-relative-import-paths": "^1.5.3",