diff --git a/.cursor/rules/cursor-rule.mdc b/.cursor/rules/cursor-rule.mdc deleted file mode 100644 index 774d83ff2c..0000000000 --- a/.cursor/rules/cursor-rule.mdc +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: How to add or edit Cursor rules in our project -globs: -alwaysApply: false ---- -# Cursor Rules Location - -How to add new cursor rules to the project - -1. Always place rule files in PROJECT_ROOT/.cursor/rules/: - ``` - .cursor/rules/ - ├── your-rule-name.mdc - ├── another-rule.mdc - └── ... - ``` - -2. Follow the naming convention: - - Use kebab-case for filenames - - Always use .mdc extension - - Make names descriptive of the rule's purpose - -3. Directory structure: - ``` - PROJECT_ROOT/ - ├── .cursor/ - │ └── rules/ - │ ├── your-rule-name.mdc - │ └── ... - └── ... - ``` - -4. Never place rule files: - - In the project root - - In subdirectories outside .cursor/rules - - In any other location - -5. Cursor rules have the following structure: - -```` ---- -description: Short description of the rule's purpose -globs: optional/path/pattern/**/* -alwaysApply: false ---- -# Rule Title - -Main content explaining the rule with markdown formatting. - -1. Step-by-step instructions -2. Code examples -3. Guidelines - -Example: -```typescript -// Good example -function goodExample() { - // Implementation following guidelines -} - -// Bad example -function badExample() { - // Implementation not following guidelines -} -``` -```` \ No newline at end of file diff --git a/.cursor/rules/testing/unit-tests-generic.mdc b/.cursor/rules/testing/unit-tests-generic.mdc index 63970b79d5..83a395aad9 100644 --- a/.cursor/rules/testing/unit-tests-generic.mdc +++ b/.cursor/rules/testing/unit-tests-generic.mdc @@ -7,6 +7,22 @@ USE WHEN writing unit tests for components in template packages # 🧪 Generic Component Test Rules +# CRITICAL: AI Attribution Requirements +* **IMPORTANT** All individual test methods generated or modified by Cursor MUST include an AI attribution comment directly above the test stating the following: +"DO NOT REMOVE THIS COMMENT! This test was generated by Cursor". The comment should go on the test method and not the test class. Failure to add an AI Attribution Comment will be considered a failure of test generation. +* The AI attribution comment MUST include a comment declaring the LLM model that was used in writing the test on its own line. + +*Sample AI Attribution Comment* +``` +/* + * DO NOT REMOVE THIS COMMENT! This test was generated by Cursor + * This test was generated with the following model: Claude 3.5 Sonnet + */ +test('renders component correctly', () => { + // test implementation +}) +``` + ## Structure & Best Practices - Use `describe` blocks to group tests, `test` for individual cases - Use `beforeEach` for setup, clear mocks after each test diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml deleted file mode 100644 index 710b73e62c..0000000000 --- a/.github/workflows/e2e-pr.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: SalesforceCommerceCloud/pwa-kit/e2e-pr -on: - workflow_dispatch: - pull_request: # Default: opened, reopened, synchronize (head branch updated) - merge_group: # Trigger GA workflow when a pull request is added to a merge queue. - push: - branches: - - develop - - 'release-*' - -jobs: - test_e2e_private: - runs-on: ubuntu-latest - steps: - # Skipping the entire workflow for now until all steps are implemented. - - name: Skip Check - run: | - echo "SKIP_WORKFLOW=true" >> "$GITHUB_ENV" - - - name: Checkout - uses: actions/checkout@v4 - - - name: Check PWA Kit Version - run: |- - version=`jq -r ".version" package.json` - echo "pwa_kit_version=$version" >> "$GITHUB_ENV" - - # TODO: Skip the entire workflow since we don't have e2e tests for PWA Kit v2.x - - name: Skip if PWA Kit version older than v3.x - if: ${{ env.SKIP_WORKFLOW != 'true' }} - run: | - major_version=$(echo "${{ env.pwa_kit_version }}" | cut -d. -f1) - if [ "$major_version" -lt 3 ]; then - echo "PWA Kit version is older than v3.x, skipping workflow." - echo "SKIP_WORKFLOW=true" >> "$GITHUB_ENV" - fi - - # Only test for latest Node version supported by MRT - - name: Setup Node - if: ${{ env.SKIP_WORKFLOW != 'true' }} - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - - # Check central resource allocation on AWS and get a lock on an available environment from the pool. - # Returns the MRT target ID if lock is acquired, otherwise returns an error state. - - name: Get MRT Target lock - if: ${{ env.SKIP_WORKFLOW != 'true' }} - id: get_mrt_target_lock - run: | - echo "TODO: Implement .github/actions/get_mrt_target_lock" - - - name: Create MRT target - id: create_mrt_target - if: ${{ env.SKIP_WORKFLOW != 'true' && steps.get_mrt_target_lock.outputs.status == 'ERR_NO_AVAILABLE_TARGETS' }} - run: | - echo "TODO: Call .github/actions/create_mrt_target with correct inputs" - - - name: Get Template Version - if: ${{ env.SKIP_WORKFLOW != 'true' }} - run: |- - version=`jq -r ".version" packages/template-retail-react-app/package.json` - echo "retail_app_template_version=$version" >> "$GITHUB_ENV" - - - name: Generate Retail App Private Client - if: ${{ env.SKIP_WORKFLOW != 'true' }} - uses: ./.github/actions/e2e_generate_app - with: - PROJECT_KEY: 'retail-app-private-client' - TEMPLATE_VERSION: ${{ env.retail_app_template_version }} - - - name: Validate Retail App Without Extensibility - if: ${{ env.SKIP_WORKFLOW != 'true' }} - uses: ./.github/actions/e2e_validate_generated_app - with: - PROJECT_KEY: 'retail-app-no-ext' - TEMPLATE_VERSION: ${{ env.retail_app_template_version }} - - # TODO: Revisit the next 2 steps to see if we can use the existing .github/actions/deploy_app action. - - name: Create MRT credentials file - if: ${{ env.SKIP_WORKFLOW != 'true' }} - uses: './.github/actions/create_mrt' - with: - mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} - mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} - - - name: Push Bundle to MRT (E2E Test PWA Kit) - if: ${{ env.SKIP_WORKFLOW != 'true' }} - uses: './.github/actions/push_to_mrt' - with: - CWD: '../generated-projects/retail-app-no-ext' - # TODO: Use the MRT target ID from the target lock step above. - TARGET: e2e-tests-pwa-kit - FLAGS: --wait - - - name: Install Playwright Browsers - if: ${{ env.SKIP_WORKFLOW != 'true' }} - run: npx playwright install --with-deps - - - name: Run Playwright tests - if: ${{ env.SKIP_WORKFLOW != 'true' }} - run: npm run test:e2e - - - name: Run Playwright a11y tests - if: ${{ env.SKIP_WORKFLOW != 'true' }} - run: npm run test:e2e:a11y - - - name: Release MRT Target Lock - if: always() # Always release the target lock back to the pool even if the tests fail. - run: | - echo "TODO: Implement .github/actions/release_mrt_target_lock" \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e5e88f7967..4e2c98fe90 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -411,25 +411,3 @@ jobs: } env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - - test-extra-features: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - - name: Install Monorepo Dependencies - run: node ./scripts/gtime.js monorepo_install npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Set PWA Kit E2E Test User - run: export PWA_E2E_USER_EMAIL=e2e.pwa.kit@gmail.com PWA_E2E_USER_PASSWORD=hpv_pek-JZK_xkz0wzf - - name: Run extra features tests - env: - PWA_E2E_USER_EMAIL: e2e.pwa.kit@gmail.com - PWA_E2E_USER_PASSWORD: hpv_pek-JZK_xkz0wzf - run: npm run test:e2e:extra_features diff --git a/.github/workflows/sync_extra_features_e2e.yml b/.github/workflows/sync_extra_features_e2e.yml deleted file mode 100644 index 5bfff89a23..0000000000 --- a/.github/workflows/sync_extra_features_e2e.yml +++ /dev/null @@ -1,170 +0,0 @@ -name: Sync Extra Features E2E Branch with Develop - -on: - # Trigger when develop branch is updated - push: - branches: - - develop - # Run daily at 11 PM PST (7 AM UTC) to catch any missed syncs - schedule: - - cron: '0 7 * * *' - workflow_dispatch: - -permissions: - contents: write - issues: write - pull-requests: read - -jobs: - sync-branch: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Configure git - run: | - git config --global user.name ${{ secrets.GIT_CONFIG_USERNAME }} - git config --global user.email ${{ secrets.GIT_CONFIG_EMAIL }} - - - name: Sync extra-features-e2e-branch with develop - id: sync - continue-on-error: true - run: | - set -e - - # Fetch all branches - git fetch origin - - # Check if the target branch exists - if ! git show-ref --verify --quiet refs/remotes/origin/extra-features-e2e-branch; then - echo "Branch extra-features-e2e-branch does not exist. Creating it from develop..." - git checkout -b extra-features-e2e-branch origin/develop - git push origin extra-features-e2e-branch - echo "✅ Created extra-features-e2e-branch branch from develop" - echo "status=created" >> $GITHUB_OUTPUT - exit 0 - fi - - # Switch to the target branch - git checkout extra-features-e2e-branch - git reset --hard origin/extra-features-e2e-branch - - # Check if we're already up to date - if git merge-base --is-ancestor origin/develop HEAD; then - echo "✅ extra-features-e2e-branch is already up to date with develop" - echo "status=up-to-date" >> $GITHUB_OUTPUT - exit 0 - fi - - if git merge origin/develop --no-edit; then - echo "✅ Successfully merged develop into extra-features-e2e-branch" - git push origin extra-features-e2e-branch - echo "status=merged" >> $GITHUB_OUTPUT - else - echo "❌ Merge conflicts detected!" - echo "📋 Files with conflicts:" - git diff --name-only --diff-filter=U || true - git merge --abort - echo "status=conflict" >> $GITHUB_OUTPUT - exit 1 - fi - - - name: Create conflict resolution issue - if: steps.sync.outputs.status == 'conflict' - uses: actions/github-script@v7 - with: - script: | - const conflictFiles = `${{ steps.sync.outputs.conflict_files || 'Unknown files' }}`; - const issueBody = ` - ## 🚨 Automatic Sync Failed - Merge Conflicts Detected - - Some features in PWA kit are defaulted to be off in \`develop\` branch (e.g. if they only work with private client). - Hence there is another site that has these features enabled that E2E tests of these extra features are run against. - The \`extra-features-e2e-branch\` is used for this site. - - A job is run nightly to sync \`develop\` to \`extra-features-e2e-branch\` branch, but today this automatic sync from \`develop\` to \`extra-features-e2e-branch\` failed due to merge conflicts. - - ### Conflicting Files: - \`\`\` - ${conflictFiles} - \`\`\` - - - ### Manual Resolution: - \`\`\`bash - git checkout extra-features-e2e-branch - git pull origin extra-features-e2e-branch - git merge develop - # Resolve conflicts manually - git add . - git commit -m "Resolve merge conflicts from develop" - git push origin extra-features-e2e-branch - \`\`\` - - ### After resolving the conflicts, close this issue - `; - - // Check if issue already exists - const existingIssues = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - labels: 'sync-conflict' - }); - - if (existingIssues.data.length === 0) { - await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: '🚨 Extra Features E2E Branch Sync Conflict - Manual Resolution Required', - body: issueBody, - labels: ['sync-conflict', 'automation'] - }); - } - - deploy: - needs: sync-branch - if: needs.sync-branch.outputs.status != 'conflict' && needs.sync-branch.outputs.status != 'up-to-date' - runs-on: ubuntu-latest - environment: extra-features-e2e - - steps: - - name: Checkout extra-features-e2e-branch branch - uses: actions/checkout@v4 - with: - ref: extra-features-e2e-branch - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'npm' - - - name: Install Monorepo Dependencies - run: | - # Install node dependencies - node ./scripts/gtime.js monorepo_install npm ci - - - name: Build project - run: | - cd packages/template-retail-react-app - npm run build - - - name: Create MRT credentials file - uses: "./.github/actions/create_mrt" - with: - mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} - mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} - - - name: Deploy to MRT - uses: "./.github/actions/push_to_mrt" - with: - CWD: "./packages/template-retail-react-app" - TARGET: extra-features-e2e - PROJECT: scaffold-pwa - MESSAGE: "Auto-sync from develop - build ${{ github.run_id }} (${{ github.sha }})" - FLAGS: --wait \ No newline at end of file diff --git a/e2e/config.js b/e2e/config.js index 4d2e315bb6..d610da3487 100644 --- a/e2e/config.js +++ b/e2e/config.js @@ -9,7 +9,6 @@ module.exports = { RETAIL_APP_HOME: process.env.RETAIL_APP_HOME || "https://scaffold-pwa-e2e-tests-pwa-kit.mobify-storefront.com", - RETAIL_APP_HOME_SITE: "RefArchGlobal", GENERATED_PROJECTS_DIR: "../generated-projects", GENERATE_PROJECTS: ["retail-app-demo", "retail-app-ext", "retail-app-no-ext"], GENERATOR_CMD: @@ -163,6 +162,5 @@ module.exports = { }, PWA_E2E_USER_EMAIL: process.env.PWA_E2E_USER_EMAIL, PWA_E2E_USER_PASSWORD: process.env.PWA_E2E_USER_PASSWORD, - EXTRA_FEATURES_E2E_RETAIL_APP_HOME: "https://scaffold-pwa-extra-features-e2e.mobify-storefront.com", - EXTRA_FEATURES_E2E_RETAIL_APP_HOME_SITE: "RefArchGlobal" + SOCIAL_LOGIN_RETAIL_APP_HOME: "https://wasatch-mrt-feature-public.mrt-storefront-staging.com" }; diff --git a/e2e/scripts/pageHelpers.js b/e2e/scripts/pageHelpers.js index 6b3f0e1589..4152b20ea8 100644 --- a/e2e/scripts/pageHelpers.js +++ b/e2e/scripts/pageHelpers.js @@ -61,8 +61,8 @@ export const answerConsentTrackingForm = async (page, dnt = false) => { } /** - * Navigates to the `Belted Ribbed Boat Neck Sweater` PDP (Product Detail Page) on mobile - * with the Black variant selected + * 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 */ @@ -96,29 +96,28 @@ export const navigateToPDPMobile = async ({page}) => { // PLP const productTile = page.getByRole('link', { - name: /Belted Ribbed Boat Neck Sweater/i + 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 \£50\.56/i)).toBeVisible() + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible() - await productTile.getByLabel(/Black/, {exact: true}).hover() + 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 \£50\.56/i)).toBeVisible() - + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible() await productTile.click() } /** - * Navigates to the `Belted Ribbed Boat Neck Sweater` PDP (Product Detail Page) on Desktop - * with the Black variant selected. + * 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 */ @@ -134,13 +133,13 @@ export const navigateToPDPDesktop = async ({page}) => { // PLP const productTile = page.getByRole('link', { - name: /Belted Ribbed Boat Neck Sweater/i + 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 \£50\.56/i)).toBeVisible() + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible() await productTile.getByLabel(/Black/, {exact: true}).hover() // Make sure the image src has changed @@ -148,14 +147,14 @@ export const navigateToPDPDesktop = async ({page}) => { const newSrc = await productTileImg.getAttribute('src') expect(newSrc).not.toBe(initialSrc) }).toPass() - await expect(productTile.getByText(/From \£50\.56/i)).toBeVisible() + await expect(productTile.getByText(/From \$39\.99/i)).toBeVisible() await productTile.click() } /** - * Navigates to the `Belted Ribbed Boat Neck Sweater` PDP (Product Detail Page) on Desktop - * with the Black variant selected. + * 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 */ @@ -165,7 +164,7 @@ export const navigateToPDPDesktopSocial = async ({ productColor, productPrice }) => { - await page.goto(config.EXTRA_FEATURES_E2E_RETAIL_APP_HOME) + await page.goto(config.SOCIAL_LOGIN_RETAIL_APP_HOME) await answerConsentTrackingForm(page) await page.getByRole('link', {name: 'Womens'}).hover() @@ -188,15 +187,15 @@ export const navigateToPDPDesktopSocial = async ({ } /** - * Adds the `Belted Ribbed Boat Neck Sweater` product to the cart with the variant: - * Colour: Black - * Size: M + * 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 Belted Ribbed Boat Neck Sweater with Black color variant selected + // Navigate to Cotton Turtleneck Sweater with Black color variant selected if (isMobile) { await navigateToPDPMobile({page}) } else { @@ -204,8 +203,8 @@ export const addProductToCart = async ({page, isMobile = false}) => { } // PDP - await expect(page.getByRole('heading', {name: /Belted Ribbed Boat Neck Sweater/i})).toBeVisible() - await page.getByRole('radio', {name: 'M', exact: true}).click() + 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() @@ -213,8 +212,8 @@ export const addProductToCart = async ({page, isMobile = false}) => { // 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=9MD/i) - expect(params).toMatch(/color=JJ3WCXX&/i) + 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) @@ -280,7 +279,7 @@ export const registerShopper = async ({page, userCredentials, isMobile = false}) } /** - * Validates that the `Belted Ribbed Boat Neck Sweater` product appears in the Order History page + * 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 */ @@ -294,15 +293,16 @@ export const validateOrderHistory = async ({page, a11y = {}}) => { await page.getByRole('link', {name: 'View details'}).click() await expect(page.getByRole('heading', {name: /Order Details/i})).toBeVisible() - await expect(page.getByRole('heading', {name: /Belted Ribbed Boat Neck Sweater/i})).toBeVisible() - await expect(page.getByText(/Size: M/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() if (checkA11y) { await runAccessibilityTest(page, [snapShotName, 'order-history-a11y-violations.json']) } } /** - * Validates that the `Belted Ribbed Boat Neck Sweater` product appears in the Wishlist page + * 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 */ @@ -314,9 +314,9 @@ export const validateWishlist = async ({page, a11y = {}}) => { await expect(page.getByRole('heading', {name: /Wishlist/i})).toBeVisible() - await expect(page.getByRole('heading', {name: /Belted Ribbed Boat Neck Sweater/i})).toBeVisible() - await expect(page.getByText(/Colour: Black/i)).toBeVisible() - await expect(page.getByText(/Size: M/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() if (checkA11y) { await runAccessibilityTest(page, [snapShotName, 'wishlist-violations.json']) } @@ -373,7 +373,7 @@ export const loginShopper = async ({page, userCredentials}) => { */ export const socialLoginShopper = async ({page}) => { try { - await page.goto(config.EXTRA_FEATURES_E2E_RETAIL_APP_HOME + '/login') + await page.goto(config.SOCIAL_LOGIN_RETAIL_APP_HOME + '/login') await page.getByText(/Google/i).click() await expect(page.getByText(/Sign in with Google/i)).toBeVisible({timeout: 10000}) @@ -543,7 +543,7 @@ export const registeredUserHappyPath = async ({page, registeredUserCredentials, // cart await page.getByLabel(/My cart/i).click() - await expect(page.getByRole('link', {name: 'Belted Ribbed Boat Neck Sweater'})).toBeVisible() + await expect(page.getByRole('link', {name: /Cotton Turtleneck Sweater/i})).toBeVisible() await page.getByRole('link', {name: 'Proceed to Checkout'}).click() @@ -630,7 +630,7 @@ export const registeredUserHappyPath = async ({page, registeredUserCredentials, await expect(page.getByRole('heading', {name: /Order Summary/i})).toBeVisible() await expect(page.getByText(/2 Items/i)).toBeVisible() - await expect(page.getByRole('link', {name: /Belted Ribbed Boat Neck Sweater/i})).toBeVisible() + await expect(page.getByRole('link', {name: /Cotton Turtleneck Sweater/i})).toBeVisible() if (checkA11y) { await runAccessibilityTest(page, [ 'registered', @@ -688,86 +688,11 @@ export const wishlistFlow = async ({page, registeredUserCredentials, a11y = {}}) await navigateToPDPDesktop({page}) // add product to wishlist - await expect(page.getByRole('heading', {name: /Belted Ribbed Boat Neck Sweater/i})).toBeVisible() + await expect(page.getByRole('heading', {name: /Cotton Turtleneck Sweater/i})).toBeVisible() - await page.getByRole('radio', {name: 'M', exact: true}).click() + await page.getByRole('radio', {name: 'L', exact: true}).click() await page.getByRole('button', {name: /Add to Wishlist/i}).click() // wishlist await validateWishlist({page, a11y}) } - -/** - * Navigates to a PLP and opens the store inventory filter to select a store. - * - * This helper function demonstrates the store inventory filtering functionality by: - * 1. Navigating to the Womens > Tops category PLP - * 2. Opening the store locator modal - * 3. Searching for stores by postal code - * 4. Returning the available store selection options - * - * This is useful for testing store inventory features and BOPIS (Buy Online, Pick Up In Store) functionality. - * - * @param {Object} options.page - Playwright page object representing a browser tab/window - */ -export const selectStoreFromPLP = async ({page}) => { - // Navigate to a product category (Womens > Tops) - await page.getByRole('link', {name: 'Womens'}).hover() - const topsNav = await page.getByRole('link', {name: 'Tops', exact: true}) - await expect(topsNav).toBeVisible() - await topsNav.click() - - // Verify we're on the PLP - await expect(page.getByRole('heading', {name: 'Tops'})).toBeVisible() - const productTile = page.getByRole('link', { - name: /Belted Ribbed Boat Neck Sweater/i - }) - const productTileImg = productTile.locator('img') - await productTileImg.waitFor({state: 'visible'}) - - // Look for the store inventory filter component - const storeInventoryFilter = page.getByTestId('sf-store-inventory-filter') - await expect(storeInventoryFilter).toBeVisible() - - // Verify the filter shows "Select Store" initially - await expect(page.getByText('Select Store')).toBeVisible() - await expect(page.getByText('Shop by Availability')).toBeVisible() - - // Click on the store inventory filter checkbox to open store locator - const inventoryCheckbox = page.getByTestId('sf-store-inventory-filter-checkbox') - await inventoryCheckbox.click() - - // Verify store locator modal opens and select a store - await expect(page.getByText('Find a Store')).toBeVisible() - await page.locator('select[name="countryCode"]').selectOption({label: 'United States'}) - await page.locator('input[name="postalCode"]').fill('01803') - const findButton = page.getByRole('button', {name: 'Find'}) - await expect(findButton).toBeVisible() - await findButton.click() - - // Wait for stores to load in the modal - await page.waitForLoadState() - - // Select the first available store (if any stores are available) - await expect(page.getByText(/Burlington Retail Store/i)).toBeVisible() - - // Find and click the first available store label - const storeRadioLabels = page.locator('label.chakra-radio:has(input[aria-describedby^="store-info-"])') - const storeCount = await storeRadioLabels.count() - - if (storeCount > 0) { - // Select the first store - await storeRadioLabels.first().click() - - // Close the store locator modal - await page.locator('button[aria-label="Close"]').click() - await page.waitForLoadState() - await expect(page.getByText('Find a Store')).not.toBeVisible() - } else { - // If no stores are available, verify the appropriate message is shown - await expect(page.getByText('Sorry, there are no locations in this area.')).toBeVisible() - - // Close the modal - await page.getByRole('button', {name: 'Close'}).click() - } -} diff --git a/e2e/tests/a11y/desktop/__snapshots__/guest/cart-a11y-violations.json b/e2e/tests/a11y/desktop/__snapshots__/guest/cart-a11y-violations.json index 4c602307f8..6fc5fec21e 100644 --- a/e2e/tests/a11y/desktop/__snapshots__/guest/cart-a11y-violations.json +++ b/e2e/tests/a11y/desktop/__snapshots__/guest/cart-a11y-violations.json @@ -7,10 +7,10 @@ "helpUrl": "https://dequeuniversity.com/rules/axe/4.10/aria-allowed-attr?application=playwright", "nodes": [ { - "html": "