From fcea57b0411dc6448f57eff5c817c8135dfecaaf Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Fri, 7 Feb 2025 13:25:47 -0300 Subject: [PATCH] Update E2E NPM scripts (#10276) Co-authored-by: Allie Mims <60988591+allie500@users.noreply.github.com> Co-authored-by: Allie Mims --- .../actions/e2e-pw/run-log-tests/action.yml | 2 +- .../e2e/atomic-prepare-and-run/action.yml | 4 + .github/actions/e2e/run-log-tests/action.yml | 16 +- .github/workflows/e2e-pull-request.yml | 11 +- .github/workflows/e2e-test.yml | 10 +- .github/workflows/e2e-tests-atomic.yml | 2 +- .../dev-10238-update-e2e-tests-all-workflow | 4 + changelog/dev-10240-update-npm-scripts | 4 + package-lock.json | 87 ++++++++-- package.json | 12 +- tests/e2e-pw/config/default.ts | 35 +++- tests/e2e-pw/docker-compose.yml | 2 +- tests/e2e-pw/playwright.config.ts | 56 ++++-- ...ytics-should-load-without-any-errors-1.png | Bin ...elect-deposits-list-advanced-filters-1.png | Bin ...ions-page-should-load-without-errors-1.png | Bin ...disputed-order-notice-button-clicked-1.png | Bin ...tton-when-no-currencies-are-selected-1.png | Bin ...olocation-correctly-with-USD-and-GBP-1.png | Bin ...olocation-correctly-with-USD-and-GBP-2.png | Bin ...urrency-page-load-without-any-errors-1.png | Bin tests/e2e-pw/specs/auth.setup.ts | 48 +----- ...bscriptions-renew-action-scheduler.spec.ts | 27 +-- .../merchant-subscriptions-renew.spec.ts | 21 +-- .../merchant-subscriptions-settings.spec.ts | 14 +- ...pper-subscriptions-manage-payments.spec.ts | 12 +- ...-subscriptions-purchase-free-trial.spec.ts | 24 ++- ...ns-purchase-multiple-subscriptions.spec.ts | 29 ++-- ...bscriptions-purchase-no-signup-fee.spec.ts | 47 ++++- ...subscriptions-purchase-sign-up-fee.spec.ts | 12 +- .../merchant-admin-account-balance.spec.ts | 2 +- .../merchant/merchant-admin-analytics.spec.ts | 8 +- .../merchant/merchant-admin-deposits.spec.ts | 2 +- .../merchant/merchant-admin-disputes.spec.ts | 2 +- .../merchant-admin-transactions.spec.ts | 9 +- .../merchant-disputes-respond.spec.ts | 14 +- ...utes-view-details-via-order-notice.spec.ts | 11 +- .../merchant-multi-currency-widget.spec.ts | 18 +- .../merchant-orders-full-refund.spec.ts | 11 +- .../merchant-orders-manual-capture.spec.ts | 23 ++- .../merchant-orders-partial-refund.spec.ts | 50 +++--- .../merchant-orders-refund-failures.spec.ts | 8 +- .../merchant-orders-status-change.spec.ts | 6 +- ...hant-payment-gateways-confirmation.spec.ts | 2 +- ...nt-payment-settings-manual-capture.spec.ts | 4 +- .../merchant-progressive-onboarding.spec.ts | 6 +- .../multi-currency-on-boarding.spec.ts | 6 +- .../merchant/multi-currency-setup.spec.ts | 26 ++- .../merchant/multi-currency.spec.ts | 6 +- .../{ => wcpay}/merchant/woopay-setup.spec.ts | 4 +- .../shopper/klarna-checkout-purchase.spec.ts | 36 ++-- .../shopper/multi-currency-checkout.spec.ts | 8 +- .../shopper/shopper-bnpls-checkout.spec.ts | 23 ++- .../shopper-checkout-cart-coupon.spec.ts | 36 ++-- .../shopper/shopper-checkout-failures.spec.ts | 6 +- ...pper-checkout-purchase-site-editor.spec.ts | 16 +- ...checkout-purchase-with-upe-methods.spec.ts | 15 +- .../shopper/shopper-checkout-purchase.spec.ts | 10 +- ...er-checkout-save-card-and-purchase.spec.ts | 53 ++++-- .../shopper-multi-currency-widget.spec.ts | 33 ++-- ...myaccount-payment-methods-add-fail.spec.ts | 24 ++- ...opper-myaccount-renew-subscription.spec.ts | 21 +-- .../shopper-myaccount-saved-cards.spec.ts | 41 +++-- .../shopper/shopper-pay-for-order.spec.ts | 18 +- ...hopper-wc-blocks-checkout-failures.spec.ts | 62 +++++-- ...hopper-wc-blocks-checkout-purchase.spec.ts | 21 ++- ...ocks-saved-card-checkout-and-usage.spec.ts | 57 +++---- tests/e2e-pw/utils/constants.ts | 7 +- tests/e2e-pw/utils/helpers.ts | 111 +++++++++++- tests/e2e-pw/utils/merchant-navigation.ts | 19 ++- tests/e2e-pw/utils/merchant.ts | 68 +++++++- tests/e2e-pw/utils/rest-api.ts | 13 +- tests/e2e-pw/utils/shopper-navigation.ts | 19 ++- tests/e2e-pw/utils/shopper.ts | 161 +++++++++++++----- tests/e2e/env/setup.sh | 15 +- 75 files changed, 1047 insertions(+), 543 deletions(-) create mode 100644 changelog/dev-10238-update-e2e-tests-all-workflow create mode 100644 changelog/dev-10240-update-npm-scripts rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/merchant-admin-analytics.spec.ts/Admin-order-analytics-should-load-without-any-errors-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/merchant-admin-deposits.spec.ts/Merchant-deposits-Select-deposits-list-advanced-filters-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/merchant-admin-transactions.spec.ts/Admin-transactions-page-should-load-without-errors-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/merchant-disputes-view-details-via-order-notice.spec.ts/Disputes-View-dispute-details-via-disputed-o-f9e9d-ils-when-disputed-order-notice-button-clicked-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Currency-selection--7b0d9-submit-button-when-no-currencies-are-selected-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-83665-tch-by-geolocation-correctly-with-USD-and-GBP-1.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-d8568-tch-by-geolocation-correctly-with-USD-and-GBP-2.png (100%) rename tests/e2e-pw/specs/{merchant/__snapshots__ => __snapshots__/wcpay/merchant}/multi-currency.spec.ts/Multi-currency-page-load-without-any-errors-1.png (100%) rename tests/e2e-pw/specs/{ => subscriptions}/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts (75%) rename tests/e2e-pw/specs/{ => subscriptions}/merchant/merchant-subscriptions-renew.spec.ts (79%) rename tests/e2e-pw/specs/{ => subscriptions}/merchant/merchant-subscriptions-settings.spec.ts (68%) rename tests/e2e-pw/specs/{ => subscriptions}/shopper/shopper-subscriptions-manage-payments.spec.ts (85%) rename tests/e2e-pw/specs/{ => subscriptions}/shopper/shopper-subscriptions-purchase-free-trial.spec.ts (90%) rename tests/e2e-pw/specs/{ => subscriptions}/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts (84%) rename tests/e2e-pw/specs/{ => subscriptions}/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts (67%) rename tests/e2e-pw/specs/{ => subscriptions}/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts (80%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-admin-account-balance.spec.ts (98%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-admin-analytics.spec.ts (90%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-admin-deposits.spec.ts (96%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-admin-disputes.spec.ts (92%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-admin-transactions.spec.ts (67%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-disputes-respond.spec.ts (96%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-disputes-view-details-via-order-notice.spec.ts (87%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-multi-currency-widget.spec.ts (93%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-orders-full-refund.spec.ts (93%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-orders-manual-capture.spec.ts (73%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-orders-partial-refund.spec.ts (86%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-orders-refund-failures.spec.ts (93%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-orders-status-change.spec.ts (96%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-payment-gateways-confirmation.spec.ts (98%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-payment-settings-manual-capture.spec.ts (92%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/merchant-progressive-onboarding.spec.ts (91%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/multi-currency-on-boarding.spec.ts (97%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/multi-currency-setup.spec.ts (90%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/multi-currency.spec.ts (92%) rename tests/e2e-pw/specs/{ => wcpay}/merchant/woopay-setup.spec.ts (84%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/klarna-checkout-purchase.spec.ts (71%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/multi-currency-checkout.spec.ts (91%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-bnpls-checkout.spec.ts (75%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-cart-coupon.spec.ts (63%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-failures.spec.ts (96%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-purchase-site-editor.spec.ts (86%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts (87%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-purchase.spec.ts (85%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-checkout-save-card-and-purchase.spec.ts (71%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-multi-currency-widget.spec.ts (78%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts (85%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-myaccount-renew-subscription.spec.ts (77%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-myaccount-saved-cards.spec.ts (86%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-pay-for-order.spec.ts (83%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-wc-blocks-checkout-failures.spec.ts (56%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-wc-blocks-checkout-purchase.spec.ts (77%) rename tests/e2e-pw/specs/{ => wcpay}/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts (65%) diff --git a/.github/actions/e2e-pw/run-log-tests/action.yml b/.github/actions/e2e-pw/run-log-tests/action.yml index b8244fc375e..1cb524f9e77 100644 --- a/.github/actions/e2e-pw/run-log-tests/action.yml +++ b/.github/actions/e2e-pw/run-log-tests/action.yml @@ -8,7 +8,7 @@ runs: id: first_run_e2e_pw_tests # Use +e to trap errors when running E2E tests. shell: /bin/bash +e {0} - run: npm run test:e2e-pw-ci + run: npm run test:e2e-ci - uses: actions/upload-artifact@v4 if: always() with: diff --git a/.github/actions/e2e/atomic-prepare-and-run/action.yml b/.github/actions/e2e/atomic-prepare-and-run/action.yml index 83669e72f14..69e267a449f 100644 --- a/.github/actions/e2e/atomic-prepare-and-run/action.yml +++ b/.github/actions/e2e/atomic-prepare-and-run/action.yml @@ -25,5 +25,9 @@ runs: run: | mkdir screenshots + - name: Install Playwright + shell: bash + run: npx playwright install chromium + - name: "Run tests, upload screenshots & logs" uses: ./.github/actions/e2e/run-log-tests diff --git a/.github/actions/e2e/run-log-tests/action.yml b/.github/actions/e2e/run-log-tests/action.yml index 8707bc46394..53f0f78652c 100644 --- a/.github/actions/e2e/run-log-tests/action.yml +++ b/.github/actions/e2e/run-log-tests/action.yml @@ -9,10 +9,10 @@ runs: # Use +e to trap errors when running E2E tests. shell: /bin/bash +e {0} run: | - npm run test:e2e -- --json --outputFile="$E2E_RESULT_FILEPATH" --NODE_ENV="$NODE_ENV" + npm run test:e2e-ci if [[ -f "$E2E_RESULT_FILEPATH" ]]; then - E2E_NUM_FAILED_TEST_SUITES=$(cat "$E2E_RESULT_FILEPATH" | jq '.numFailedTestSuites') + E2E_NUM_FAILED_TEST_SUITES=$(cat "$E2E_RESULT_FILEPATH" | jq '.stats["unexpected"]') echo "FIRST_RUN_FAILED_TEST_SUITES=$(echo $E2E_NUM_FAILED_TEST_SUITES)" >> $GITHUB_OUTPUT if [[ ${E2E_NUM_FAILED_TEST_SUITES} -gt 0 ]]; then echo "::notice::${E2E_NUM_FAILED_TEST_SUITES} test suite(s) failed in the first run but we will try (it) them again in the second run." @@ -20,8 +20,7 @@ runs: fi else echo "FIRST_RUN_FAILED_TEST_SUITES=0" >> $GITHUB_OUTPUT - echo "::notice::${E2E_RESULT_FILEPATH} doesn't exist so results are unclear and we will not try again." - exit 1 + exit 0 fi # Retry failed E2E tests @@ -30,18 +29,17 @@ runs: shell: bash # Filter failed E2E files from the result JSON file, and re-run them. run: | - cat "$E2E_RESULT_FILEPATH" | jq '.testResults[] | select(.status == "failed") | .name' | xargs npm run test:e2e -- --NODE_ENV="$NODE_ENV" + npm run test:e2e-ci $(cat $E2E_RESULT_FILEPATH | jq -r '[.suites[] | (if has("suites") then .suites[] | .specs[] else .specs[] end) | select(.tests[].status == "unexpected") | .file] | unique | .[]') # Archive screenshots if any - name: Archive e2e test screenshots & logs - if: ${{ failure() }} + if: ${{ always() }} uses: actions/upload-artifact@v4 with: name: wp(${{ env.E2E_WP_VERSION }})-wc(${{ env.E2E_WC_VERSION }})-${{ env.E2E_GROUP }}-${{ env.E2E_BRANCH }} path: | - screenshots - tests/e2e/screenshots - tests/e2e/docker/wordpress/wp-content/debug.log + playwright-report/ + tests/e2e-pw/test-results ${{ env.E2E_RESULT_FILEPATH }} if-no-files-found: ignore retention-days: 14 diff --git a/.github/workflows/e2e-pull-request.yml b/.github/workflows/e2e-pull-request.yml index e1226644daf..afb5dfecdfb 100644 --- a/.github/workflows/e2e-pull-request.yml +++ b/.github/workflows/e2e-pull-request.yml @@ -3,9 +3,8 @@ name: E2E Tests - Pull Request on: pull_request: branches: - # - develop - # - trunk - - dev/10240-update-npm-scripts # [TODO] Remove this line after the branch is merged. + - develop + - trunk workflow_dispatch: workflow_call: inputs: @@ -31,7 +30,7 @@ env: E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }} E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} E2E_USE_LOCAL_SERVER: false - E2E_RESULT_FILEPATH: 'tests/e2e/results.json' + E2E_RESULT_FILEPATH: 'tests/e2e-pw/results.json' WCPAY_USE_BUILD_ARTIFACT: ${{ inputs.wcpay-use-build-artifact }} WCPAY_ARTIFACT_DIRECTORY: 'zipfile' NODE_ENV: 'test' @@ -75,5 +74,9 @@ jobs: - name: Setup E2E environment uses: ./.github/actions/e2e/env-setup + - name: Install Playwright + shell: bash + run: npx playwright install chromium + - name: Run tests, upload screenshots & logs uses: ./.github/actions/e2e/run-log-tests diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 1d1f0b1bd71..c68a4d61a81 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -22,7 +22,7 @@ env: E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }} E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} E2E_USE_LOCAL_SERVER: false - E2E_RESULT_FILEPATH: 'tests/e2e/results.json' + E2E_RESULT_FILEPATH: 'tests/e2e-pw/results.json' WC_MIN_SUPPORTED_VERSION: '7.6.0' NODE_ENV: 'test' FORCE_E2E_DEPS_SETUP: true @@ -64,6 +64,10 @@ jobs: - name: Setup E2E environment uses: ./.github/actions/e2e/env-setup + - name: Install Playwright + shell: bash + run: npx playwright install chromium + - name: Run tests, upload screenshots & logs uses: ./.github/actions/e2e/run-log-tests @@ -116,5 +120,9 @@ jobs: - name: Setup E2E environment uses: ./.github/actions/e2e/env-setup + - name: Install Playwright + shell: bash + run: npx playwright install chromium + - name: Run tests, upload screenshots & logs uses: ./.github/actions/e2e/run-log-tests diff --git a/.github/workflows/e2e-tests-atomic.yml b/.github/workflows/e2e-tests-atomic.yml index be25eddd88a..30cb1b55d61 100644 --- a/.github/workflows/e2e-tests-atomic.yml +++ b/.github/workflows/e2e-tests-atomic.yml @@ -10,7 +10,7 @@ env: E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} E2E_WP_VERSION: 'nightly' E2E_WC_VERSION: 'latest' - E2E_RESULT_FILEPATH: 'tests/e2e/results.json' + E2E_RESULT_FILEPATH: 'tests/e2e-pw/results.json' NODE_ENV: 'atomic' concurrency: diff --git a/changelog/dev-10238-update-e2e-tests-all-workflow b/changelog/dev-10238-update-e2e-tests-all-workflow new file mode 100644 index 00000000000..6861f616e0d --- /dev/null +++ b/changelog/dev-10238-update-e2e-tests-all-workflow @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update Github actions and workflows. Change e2e tests directory structure. diff --git a/changelog/dev-10240-update-npm-scripts b/changelog/dev-10240-update-npm-scripts new file mode 100644 index 00000000000..01db8abe13c --- /dev/null +++ b/changelog/dev-10240-update-npm-scripts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update E2E NPM scripts. diff --git a/package-lock.json b/package-lock.json index ded73102317..f1d8a9f2855 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "devDependencies": { "@automattic/color-studio": "4.0.0", "@jest/test-sequencer": "29.5.0", - "@playwright/test": "1.43.1", + "@playwright/test": "1.50.1", "@pmmmwh/react-refresh-webpack-plugin": "0.5.7", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "11.2.5", @@ -1878,6 +1878,25 @@ } } }, + "node_modules/@automattic/puppeteer-utils/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, "node_modules/@automattic/puppeteer-utils/node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -8409,18 +8428,18 @@ } }, "node_modules/@playwright/test": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", - "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dev": true, "dependencies": { - "playwright": "1.43.1" + "playwright": "1.50.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { @@ -22984,6 +23003,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -28573,6 +28602,13 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -29056,6 +29092,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -38454,6 +38504,13 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nan": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "dev": true, + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -39804,33 +39861,33 @@ } }, "node_modules/playwright": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", - "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dev": true, "dependencies": { - "playwright-core": "1.43.1" + "playwright-core": "1.50.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", - "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/please-upgrade-node": { diff --git a/package.json b/package.json index 6c1e4288aaf..c0be9d14c13 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,11 @@ "test:e2e-up": "./tests/e2e/env/up.sh", "test:e2e-cleanup": "./tests/e2e/env/cleanup.sh", "test:e2e-reset": "npm run test:e2e-down && npm run test:e2e-cleanup", - "test:e2e": "NODE_CONFIG_DIR='tests/e2e/config' JEST_PUPPETEER_CONFIG='tests/e2e/config/jest-puppeteer-headless.config.js' wp-scripts test-e2e --config tests/e2e/config/jest.config.js", - "test:e2e-dev": "NODE_CONFIG_DIR='tests/e2e/config' JEST_PUPPETEER_CONFIG='tests/e2e/config/jest-puppeteer.config.js' wp-scripts test-e2e --config tests/e2e/config/jest.config.js --puppeteer-interactive", + "test:e2e": "./tests/e2e-pw/test-e2e-pw.sh", + "test:e2e-ci": "npx playwright test --config=tests/e2e-pw/playwright.config.ts --grep-invert @todo", + "test:e2e-ui": "./tests/e2e-pw/test-e2e-pw-ui.sh", "test:e2e-performance": "NODE_CONFIG_DIR='tests/e2e/config' wp-scripts test-e2e --config tests/e2e/config/jest.performance.config.js", - "test:e2e-pw": "./tests/e2e-pw/test-e2e-pw.sh", - "test:e2e-pw-update-snapshots": "npm run test:e2e-pw -- --update-snapshots", - "test:e2e-pw-ui": "./tests/e2e-pw/test-e2e-pw-ui.sh", - "test:e2e-pw-ci": "npx playwright test --config=tests/e2e-pw/playwright.config.ts --grep-invert @todo", + "test:e2e-update-snapshots": "npm run test:e2e -- --update-snapshots", "test:update-snapshots": "npm run test:js -- --updateSnapshot", "test:php": "./bin/run-tests.sh", "test:php-coverage": "./bin/check-test-coverage.sh", @@ -96,7 +94,7 @@ "devDependencies": { "@automattic/color-studio": "4.0.0", "@jest/test-sequencer": "29.5.0", - "@playwright/test": "1.43.1", + "@playwright/test": "1.50.1", "@pmmmwh/react-refresh-webpack-plugin": "0.5.7", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "11.2.5", diff --git a/tests/e2e-pw/config/default.ts b/tests/e2e-pw/config/default.ts index baa7fefb940..1b900c5a76d 100644 --- a/tests/e2e-pw/config/default.ts +++ b/tests/e2e-pw/config/default.ts @@ -20,16 +20,47 @@ export const config = { }, }, products: { + cap: { + name: 'Cap', + pageNumber: 1, + }, + belt: { + name: 'Belt', + pageNumber: 1, + }, simple: { name: 'Beanie', + pageNumber: 1, + }, + sunglasses: { + name: 'Sunglasses', + pageNumber: 2, }, variable: { name: 'Variable Product with Three Variations', + pageNumber: 1, }, grouped: { name: 'Grouped Product with Three Children', + pageNumber: 1, }, - }, + hoodie_with_logo: { + name: 'Hoodie with Logo', + pageNumber: 1, + }, + subscription_signup_fee: { + name: 'Subscription signup fee product', + pageNumber: 2, + }, + subscription_no_signup_fee: { + name: 'Subscription no signup fee product', + pageNumber: 2, + }, + subscription_free_trial: { + name: 'Subscription free trial product', + pageNumber: 2, + }, + } as Record< string, Product >, addresses: { admin: { store: { @@ -314,3 +345,5 @@ export type CustomerAddress = Omit< > & { state?: string; }; + +export type Product = { name: string; pageNumber: number }; diff --git a/tests/e2e-pw/docker-compose.yml b/tests/e2e-pw/docker-compose.yml index 2919d6ab568..9a7277c32d3 100644 --- a/tests/e2e-pw/docker-compose.yml +++ b/tests/e2e-pw/docker-compose.yml @@ -1,7 +1,7 @@ services: playwright: # When updating the Playwright version in the image tag below, make sure to update the npm `@playwright/test` package.json version as well. - image: mcr.microsoft.com/playwright:v1.43.1-jammy + image: mcr.microsoft.com/playwright:v1.50.1-jammy working_dir: /woopayments volumes: - $PWD:/woopayments diff --git a/tests/e2e-pw/playwright.config.ts b/tests/e2e-pw/playwright.config.ts index f5911d09ede..14350953f40 100644 --- a/tests/e2e-pw/playwright.config.ts +++ b/tests/e2e-pw/playwright.config.ts @@ -9,7 +9,38 @@ import path from 'path'; config( { path: path.resolve( __dirname, '../e2e/config', '.env' ) } ); config( { path: path.resolve( __dirname, '../e2e/config', 'local.env' ) } ); -const { BASE_URL } = process.env; +const { BASE_URL, NODE_ENV, E2E_GROUP, E2E_BRANCH } = process.env; + +const validGroups = [ 'wcpay', 'subscriptions' ]; +const validBranches = [ 'merchant', 'shopper' ]; + +const buildTestDir = ( group: string, branch: string ) => { + const baseDir = `\/specs`; + + if ( ! group || ! validGroups.includes( group ) ) { + return baseDir; + } + + if ( ! branch || ! validBranches.includes( branch ) ) { + return `${ baseDir }\/${ group }`; + } + + return `${ baseDir }\/${ group }\/${ branch }`; +}; + +const getTestMatch = ( group: string, branch: string ) => { + const testDir = buildTestDir( group, branch ); + + return new RegExp( `${ testDir }\/.*\.spec\.ts` ); +}; + +const getBaseUrl = () => { + if ( NODE_ENV === 'atomic' ) { + return 'https://wcpaytestecomm.wpcomstaging.com/'; + } + + return BASE_URL ?? 'http://localhost:8084'; +}; /** * See https://playwright.dev/docs/test-configuration. @@ -27,15 +58,16 @@ export default defineConfig( { /* Reporters to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI ? [ - // If running on CI, also use the GitHub Actions reporter - [ 'github' ], + /* If running on CI, include the dot reporter and JSON reporter. */ + [ 'dot' ], + [ 'json', { outputFile: 'results.json' } ], [ 'html' ], ] : [ [ 'html', { open: 'never' } ] ], outputDir: './test-results', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { - baseURL: BASE_URL ?? 'http://localhost:8084', + baseURL: getBaseUrl(), screenshot: 'only-on-failure', trace: 'retain-on-failure', video: 'on-first-retry', @@ -43,12 +75,17 @@ export default defineConfig( { }, timeout: 120 * 1000, // Default is 30s, somteimes it is not enough for local tests due to long setup. expect: { - toHaveScreenshot: { maxDiffPixelRatio: 0.025 }, + toHaveScreenshot: { + maxDiffPixelRatio: + process.env.E2E_WC_VERSION === '7.7.0' ? 0.035 : 0.025, + }, //=* Increase expect timeout to 10 seconds. See https://playwright.dev/docs/test-timeouts#set-expect-timeout-in-the-config.*/ timeout: 20 * 1000, }, snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}', + testMatch: getTestMatch( E2E_GROUP, E2E_BRANCH ), + /* Configure projects for major browsers */ projects: [ { @@ -58,15 +95,8 @@ export default defineConfig( { dependencies: [ 'setup' ], }, { - name: 'merchant', - use: { ...devices[ 'Desktop Chrome' ] }, - testDir: './specs/merchant', - dependencies: [ 'setup' ], - }, - { - name: 'shopper', + name: 'chromium', use: { ...devices[ 'Desktop Chrome' ] }, - testDir: './specs/shopper', dependencies: [ 'setup' ], }, // Setup project diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-analytics.spec.ts/Admin-order-analytics-should-load-without-any-errors-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-analytics.spec.ts/Admin-order-analytics-should-load-without-any-errors-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-analytics.spec.ts/Admin-order-analytics-should-load-without-any-errors-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-analytics.spec.ts/Admin-order-analytics-should-load-without-any-errors-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-deposits.spec.ts/Merchant-deposits-Select-deposits-list-advanced-filters-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-deposits.spec.ts/Merchant-deposits-Select-deposits-list-advanced-filters-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-deposits.spec.ts/Merchant-deposits-Select-deposits-list-advanced-filters-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-deposits.spec.ts/Merchant-deposits-Select-deposits-list-advanced-filters-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-transactions.spec.ts/Admin-transactions-page-should-load-without-errors-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-transactions.spec.ts/Admin-transactions-page-should-load-without-errors-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/merchant-admin-transactions.spec.ts/Admin-transactions-page-should-load-without-errors-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-admin-transactions.spec.ts/Admin-transactions-page-should-load-without-errors-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/merchant-disputes-view-details-via-order-notice.spec.ts/Disputes-View-dispute-details-via-disputed-o-f9e9d-ils-when-disputed-order-notice-button-clicked-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-disputes-view-details-via-order-notice.spec.ts/Disputes-View-dispute-details-via-disputed-o-f9e9d-ils-when-disputed-order-notice-button-clicked-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/merchant-disputes-view-details-via-order-notice.spec.ts/Disputes-View-dispute-details-via-disputed-o-f9e9d-ils-when-disputed-order-notice-button-clicked-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/merchant-disputes-view-details-via-order-notice.spec.ts/Disputes-View-dispute-details-via-disputed-o-f9e9d-ils-when-disputed-order-notice-button-clicked-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Currency-selection--7b0d9-submit-button-when-no-currencies-are-selected-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Currency-selection--7b0d9-submit-button-when-no-currencies-are-selected-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Currency-selection--7b0d9-submit-button-when-no-currencies-are-selected-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Currency-selection--7b0d9-submit-button-when-no-currencies-are-selected-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-83665-tch-by-geolocation-correctly-with-USD-and-GBP-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-83665-tch-by-geolocation-correctly-with-USD-and-GBP-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-83665-tch-by-geolocation-correctly-with-USD-and-GBP-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-83665-tch-by-geolocation-correctly-with-USD-and-GBP-1.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-d8568-tch-by-geolocation-correctly-with-USD-and-GBP-2.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-d8568-tch-by-geolocation-correctly-with-USD-and-GBP-2.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-d8568-tch-by-geolocation-correctly-with-USD-and-GBP-2.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency-on-boarding.spec.ts/Multi-currency-on-boarding-Geolocation-feature-d8568-tch-by-geolocation-correctly-with-USD-and-GBP-2.png diff --git a/tests/e2e-pw/specs/merchant/__snapshots__/multi-currency.spec.ts/Multi-currency-page-load-without-any-errors-1.png b/tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency.spec.ts/Multi-currency-page-load-without-any-errors-1.png similarity index 100% rename from tests/e2e-pw/specs/merchant/__snapshots__/multi-currency.spec.ts/Multi-currency-page-load-without-any-errors-1.png rename to tests/e2e-pw/specs/__snapshots__/wcpay/merchant/multi-currency.spec.ts/Multi-currency-page-load-without-any-errors-1.png diff --git a/tests/e2e-pw/specs/auth.setup.ts b/tests/e2e-pw/specs/auth.setup.ts index 595ff6c94dd..f77285b3d7e 100644 --- a/tests/e2e-pw/specs/auth.setup.ts +++ b/tests/e2e-pw/specs/auth.setup.ts @@ -13,6 +13,8 @@ import { merchantStorageFile, customerStorageFile, wpAdminLogin, + loginAsCustomer, + addSupportSessionDetectedCookie, } from '../utils/helpers'; // See https://playwright.dev/docs/auth#multiple-signed-in-roles @@ -34,7 +36,7 @@ const isAuthStateStale = ( authStateFile: string ) => { return isStale; }; -setup( 'authenticate as admin', async ( { page } ) => { +setup( 'authenticate as admin', async ( { page }, { project } ) => { // For local development, use existing state if it exists and isn't stale. if ( ! process.env.CI ) { if ( ! isAuthStateStale( merchantStorageFile ) ) { @@ -43,6 +45,8 @@ setup( 'authenticate as admin', async ( { page } ) => { } } + await addSupportSessionDetectedCookie( page, project ); + // Sign in as admin user and save state let adminLoggedIn = false; const adminRetries = 5; @@ -76,11 +80,10 @@ setup( 'authenticate as admin', async ( { page } ) => { } // End of authentication steps. - await page.context().storageState( { path: merchantStorageFile } ); } ); -setup( 'authenticate as customer', async ( { page } ) => { +setup( 'authenticate as customer', async ( { page }, { project } ) => { // For local development, use existing state if it exists and isn't stale. if ( ! process.env.CI ) { if ( ! isAuthStateStale( customerStorageFile ) ) { @@ -89,41 +92,6 @@ setup( 'authenticate as customer', async ( { page } ) => { } } - // Sign in as customer user and save state - let customerLoggedIn = false; - const customerRetries = 5; - for ( let i = 0; i < customerRetries; i++ ) { - try { - console.log( 'Trying to log-in as customer...' ); - await wpAdminLogin( page, customer ); - - await page.goto( `/my-account` ); - await expect( - page.locator( - '.woocommerce-MyAccount-navigation-link--customer-logout' - ) - ).toBeVisible(); - await expect( - page.locator( 'div.woocommerce-MyAccount-content > p >> nth=0' ) - ).toContainText( 'Hello' ); - - console.log( 'Logged-in as customer successfully.' ); - customerLoggedIn = true; - break; - } catch ( e ) { - console.log( - `Customer log-in failed. Retrying... ${ i }/${ customerRetries }` - ); - console.log( e ); - } - } - - if ( ! customerLoggedIn ) { - throw new Error( - 'Cannot proceed e2e test, as customer login failed. Please check if the test site has been setup correctly.' - ); - } - // End of authentication steps. - - await page.context().storageState( { path: customerStorageFile } ); + await addSupportSessionDetectedCookie( page, project ); + await loginAsCustomer( page, customer ); } ); diff --git a/tests/e2e-pw/specs/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts similarity index 75% rename from tests/e2e-pw/specs/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts rename to tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts index 09f91cde677..f263bdfc391 100644 --- a/tests/e2e-pw/specs/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew-action-scheduler.spec.ts @@ -6,18 +6,17 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; -import * as shopper from '../../utils/shopper'; -import { config } from '../../config/default'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; +import * as shopper from '../../../utils/shopper'; +import { config } from '../../../config/default'; import { - products, shouldRunActionSchedulerTests, shouldRunSubscriptionsTests, -} from '../../utils/constants'; +} from '../../../utils/constants'; import { goToActionScheduler, goToSubscriptions, -} from '../../utils/merchant-navigation'; +} from '../../../utils/merchant-navigation'; // Run the tests if the two 'skip' environment variables are not set. describeif( shouldRunSubscriptionsTests && shouldRunActionSchedulerTests )( @@ -29,8 +28,6 @@ describeif( shouldRunSubscriptionsTests && shouldRunActionSchedulerTests )( const customerBillingConfig = config.addresses[ 'subscriptions-customer' ].billing; - let subscriptionId: string; - test.beforeAll( async ( { browser }, { project } ) => { const { shopperPage } = await getShopper( browser, @@ -38,9 +35,9 @@ describeif( shouldRunSubscriptionsTests && shouldRunActionSchedulerTests )( project.use.baseURL ); - await shopper.addCartProduct( + await shopper.addToCartFromShopPage( shopperPage, - products.SUBSCRIPTION_SIGNUP_FEE + config.products.subscription_signup_fee ); await shopper.setupCheckout( shopperPage, customerBillingConfig ); await shopper.fillCardDetails( shopperPage, config.cards.basic ); @@ -48,10 +45,6 @@ describeif( shouldRunSubscriptionsTests && shouldRunActionSchedulerTests )( await expect( shopperPage.getByRole( 'heading', { name: 'Order received' } ) ).toBeVisible(); - - subscriptionId = await shopperPage - .getByLabel( 'View subscription number' ) - .innerText(); } ); test( 'should renew a subscription with action scheduler', async ( { @@ -81,12 +74,8 @@ describeif( shouldRunSubscriptionsTests && shouldRunActionSchedulerTests )( // Go to Subscriptions and verify the subscription renewal await goToSubscriptions( merchantPage ); - const numericSubscriptionId = subscriptionId.substring( 1 ); - await expect( - merchantPage - .locator( `#order-${ numericSubscriptionId }` ) - .getByRole( 'cell', { name: '2', exact: true } ) + merchantPage.getByRole( 'cell', { name: '2', exact: true } ) ).toBeVisible(); } ); } diff --git a/tests/e2e-pw/specs/merchant/merchant-subscriptions-renew.spec.ts b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew.spec.ts similarity index 79% rename from tests/e2e-pw/specs/merchant/merchant-subscriptions-renew.spec.ts rename to tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew.spec.ts index dc8c8e0f89d..d7b02b8e4f6 100644 --- a/tests/e2e-pw/specs/merchant/merchant-subscriptions-renew.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-renew.spec.ts @@ -6,21 +6,19 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import RestAPI from '../../utils/rest-api'; -import { config } from '../../config/default'; -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; +import RestAPI from '../../../utils/rest-api'; +import { config } from '../../../config/default'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; import { emptyCart, fillCardDetails, focusPlaceOrderButton, placeOrder, setupProductCheckout, -} from '../../utils/shopper'; -import { goToShop } from '../../utils/shopper-navigation'; -import { goToSubscriptionPage } from '../../utils/merchant-navigation'; -import { shouldRunSubscriptionsTests } from '../../utils/constants'; +} from '../../../utils/shopper'; +import { goToSubscriptionPage } from '../../../utils/merchant-navigation'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; -const productName = 'Subscription signup fee product'; const customerBillingConfig = config.addresses[ 'subscriptions-customer' ].billing; let subscriptionId = null; @@ -34,10 +32,9 @@ describeif( shouldRunSubscriptionsTests )( const { shopperPage } = await getShopper( browser ); await emptyCart( shopperPage ); - await goToShop( shopperPage, 2 ); await setupProductCheckout( shopperPage, - [ [ productName, 1 ] ], + [ [ config.products.subscription_signup_fee, 1 ] ], customerBillingConfig ); await fillCardDetails( shopperPage, config.cards.basic ); @@ -53,9 +50,7 @@ describeif( shouldRunSubscriptionsTests )( // Get the subscription ID subscriptionId = ( await shopperPage - .getByRole( 'cell', { - name: 'ID: View subscription number', - } ) + .getByRole( 'link', { name: 'View subscription number' } ) .textContent() ) .trim() diff --git a/tests/e2e-pw/specs/merchant/merchant-subscriptions-settings.spec.ts b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-settings.spec.ts similarity index 68% rename from tests/e2e-pw/specs/merchant/merchant-subscriptions-settings.spec.ts rename to tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-settings.spec.ts index a2cfe8fcfcf..a685727c0cb 100644 --- a/tests/e2e-pw/specs/merchant/merchant-subscriptions-settings.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/merchant/merchant-subscriptions-settings.spec.ts @@ -2,9 +2,9 @@ * External dependencies */ import test, { expect } from 'playwright/test'; -import { describeif, useMerchant } from '../../utils/helpers'; -import { shouldRunSubscriptionsTests } from '../../utils/constants'; -import { goToWooCommerceSettings } from '../../utils/merchant-navigation'; +import { describeif, useMerchant } from '../../../utils/helpers'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { goToWooCommerceSettings } from '../../../utils/merchant-navigation'; describeif( shouldRunSubscriptionsTests )( 'WooCommerce > Settings > Subscriptions', @@ -21,9 +21,11 @@ describeif( shouldRunSubscriptionsTests )( await expect( menuItem ).toBeVisible(); // An alternative way to verify the subscriptions menu page is active, avoiding the active tab classname. - const heading = await page.getByRole( 'heading', { - name: 'Subscriptions', - } ); + const heading = await page + .getByRole( 'heading', { + name: 'Subscriptions', + } ) + .first(); await expect( heading ).toBeVisible(); } ); } diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-manage-payments.spec.ts b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-manage-payments.spec.ts similarity index 85% rename from tests/e2e-pw/specs/shopper/shopper-subscriptions-manage-payments.spec.ts rename to tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-manage-payments.spec.ts index 53bffb11da0..bcdec4cde7b 100644 --- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-manage-payments.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-manage-payments.spec.ts @@ -6,11 +6,11 @@ import test, { expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { products, shouldRunSubscriptionsTests } from '../../utils/constants'; -import { describeif, getShopper } from '../../utils/helpers'; -import * as shopper from '../../utils/shopper'; -import * as navigation from '../../utils/shopper-navigation'; +import { config } from '../../../config/default'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { describeif, getShopper } from '../../../utils/helpers'; +import * as shopper from '../../../utils/shopper'; +import * as navigation from '../../../utils/shopper-navigation'; const navigateToSubscriptionDetails = async ( page: Page, @@ -47,7 +47,7 @@ describeif( shouldRunSubscriptionsTests )( // Purchase a subscription. await shopper.placeOrderWithOptions( page, { - productId: products.SUBSCRIPTION_NO_SIGNUP_FEE, + product: config.products.subscription_no_signup_fee, billingAddress: customerBillingAddress, } ); diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-free-trial.spec.ts similarity index 90% rename from tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts rename to tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-free-trial.spec.ts index 1322f476e7a..83696be93c4 100644 --- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-free-trial.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-free-trial.spec.ts @@ -6,25 +6,28 @@ import test, { Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { shouldRunSubscriptionsTests } from '../../utils/constants'; -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; -import { config } from '../../config/default'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; +import { config } from '../../../config/default'; import { confirmCardAuthentication, emptyCart, fillCardDetails, setupCheckout, -} from '../../utils/shopper'; +} from '../../../utils/shopper'; import { goToCart, goToProductPageBySlug, -} from '../../utils/shopper-navigation'; -import { goToOrder, goToSubscriptions } from '../../utils/merchant-navigation'; +} from '../../../utils/shopper-navigation'; +import { + goToOrder, + goToSubscriptions, +} from '../../../utils/merchant-navigation'; import { activateMulticurrency, deactivateMulticurrency, isMulticurrencyEnabled, -} from '../../utils/merchant'; +} from '../../../utils/merchant'; const nowLocal = new Date(); const nowUTC = new Date( @@ -100,7 +103,12 @@ describeif( shouldRunSubscriptionsTests )( // Verify that the order total is $0.00 await expect( - shopperPage.getByRole( 'cell', { name: /^Total: \$0\.00/ } ) + shopperPage + .getByRole( 'row', { + name: 'Total $0.00', + exact: true, + } ) + .locator( 'td' ) ).toBeVisible(); // Proceed to the checkout page and verify that the 14-day free trial is shown in the product line item, diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts similarity index 84% rename from tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts rename to tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts index 04fe7227f86..3c2337c3c90 100644 --- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-multiple-subscriptions.spec.ts @@ -6,25 +6,21 @@ import test, { Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { shouldRunSubscriptionsTests } from '../../utils/constants'; -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; -import { config } from '../../config/default'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; +import { config } from '../../../config/default'; import { emptyCart, fillCardDetails, placeOrder, setupProductCheckout, -} from '../../utils/shopper'; -import { - goToShop, - goToShopWithCurrency, - goToSubscriptions, -} from '../../utils/shopper-navigation'; +} from '../../../utils/shopper'; +import { goToSubscriptions } from '../../../utils/shopper-navigation'; import { activateMulticurrency, deactivateMulticurrency, restoreCurrencies, -} from '../../utils/merchant'; +} from '../../../utils/merchant'; const products = { 'Subscription no signup fee product': 'subscription-no-signup-fee-product', @@ -57,15 +53,14 @@ describeif( shouldRunSubscriptionsTests )( test( ' should be able to purchase multiple subscriptions', async () => { // As a Shopper, purchase the subscription products. await emptyCart( shopperPage ); - await goToShopWithCurrency( shopperPage, 'USD' ); - await goToShop( shopperPage, 2 ); await setupProductCheckout( shopperPage, - Object.keys( products ).map( ( productName: string ) => [ - productName, - 1, - ] ), - configBillingAddress + [ + [ config.products.subscription_no_signup_fee, 1 ], + [ config.products.subscription_signup_fee, 1 ], + ], + configBillingAddress, + 'USD' ); await fillCardDetails( shopperPage, config.cards.basic ); await placeOrder( shopperPage ); diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts similarity index 67% rename from tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts rename to tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts index 9cace535df1..20c2060acfb 100644 --- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-no-signup-fee.spec.ts @@ -6,16 +6,24 @@ import test, { expect } from '@playwright/test'; /** * Internal dependencies */ -import { shouldRunSubscriptionsTests } from '../../utils/constants'; -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; -import { config } from '../../config/default'; -import { goToSubscriptions, goToOrder } from '../../utils/merchant-navigation'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; +import { config } from '../../../config/default'; +import { + goToSubscriptions, + goToOrder, +} from '../../../utils/merchant-navigation'; import { fillCardDetails, placeOrder, setupCheckout, -} from '../../utils/shopper'; -import { goToProductPageBySlug } from '../../utils/shopper-navigation'; +} from '../../../utils/shopper'; +import { goToProductPageBySlug } from '../../../utils/shopper-navigation'; +import { + activateMulticurrency, + deactivateMulticurrency, + isMulticurrencyEnabled, +} from '../../../utils/merchant'; const productName = 'Subscription no signup fee product'; const productSlug = 'subscription-no-signup-fee-product'; @@ -55,16 +63,27 @@ describeif( shouldRunSubscriptionsTests )( ); orderId = await orderIdField.textContent(); } ); + test( 'It should have a charge for subscription cost without fee & an active subscription', async ( { browser, } ) => { const { merchantPage } = await getMerchant( browser ); + + // Disable multi-currency in the merchant settings. + // This step is important because local environment setups might have multi-currency enabled. + const wasMultiCurrencyEnabled = await isMulticurrencyEnabled( + merchantPage + ); + if ( wasMultiCurrencyEnabled ) { + await deactivateMulticurrency( merchantPage ); + } + await goToOrder( merchantPage, orderId ); // Verify we have an active subscription const relatedSubscriptionId = ( await merchantPage - .getByRole( 'cell', { name: 'Edit order number' } ) + .getByRole( 'link', { name: 'Edit order number' } ) .textContent() ) .trim() @@ -87,10 +106,17 @@ describeif( shouldRunSubscriptionsTests )( await goToSubscriptions( merchantPage ); - const subscriptionsRow = merchantPage.locator( + let subscriptionsRow = merchantPage.locator( '#order-' + relatedSubscriptionId ); + // Fallback for WC 7.7.0. + if ( ( await subscriptionsRow.count() ) === 0 ) { + subscriptionsRow = merchantPage.locator( + '#post-' + relatedSubscriptionId + ); + } + await expect( subscriptionsRow.locator( '.subscription-status' ) ).toHaveText( 'Active' ); @@ -102,6 +128,11 @@ describeif( shouldRunSubscriptionsTests )( await expect( subscriptionsRow.locator( '.recurring_total' ) ).toHaveText( /\$9\.99 \/ month/i ); + + // Enable multicurrency if it was enabled before. + if ( wasMultiCurrencyEnabled ) { + await activateMulticurrency( merchantPage ); + } } ); } ); diff --git a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts similarity index 80% rename from tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts rename to tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts index 2728a404530..78d37cc2453 100644 --- a/tests/e2e-pw/specs/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts +++ b/tests/e2e-pw/specs/subscriptions/shopper/shopper-subscriptions-purchase-sign-up-fee.spec.ts @@ -6,11 +6,11 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { describeif, getMerchant, getShopper } from '../../utils/helpers'; -import * as shopper from '../../utils/shopper'; -import * as navigation from '../../utils/merchant-navigation'; -import { shouldRunSubscriptionsTests, products } from '../../utils/constants'; +import { config } from '../../../config/default'; +import { describeif, getMerchant, getShopper } from '../../../utils/helpers'; +import * as shopper from '../../../utils/shopper'; +import * as navigation from '../../../utils/merchant-navigation'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; describeif( shouldRunSubscriptionsTests )( 'Subscriptions > Purchase subscription with signup fee', @@ -31,7 +31,7 @@ describeif( shouldRunSubscriptionsTests )( test( 'should be able to purchase a subscription with signup fee', async () => { orderId = await shopper.placeOrderWithOptions( shopperPage, { - productId: products.SUBSCRIPTION_SIGNUP_FEE, + product: config.products.subscription_signup_fee, billingAddress: customerBillingAddress, } ); } ); diff --git a/tests/e2e-pw/specs/merchant/merchant-admin-account-balance.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-account-balance.spec.ts similarity index 98% rename from tests/e2e-pw/specs/merchant/merchant-admin-account-balance.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-admin-account-balance.spec.ts index bed2efcb90d..2b60efe22d6 100644 --- a/tests/e2e-pw/specs/merchant/merchant-admin-account-balance.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-account-balance.spec.ts @@ -6,7 +6,7 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; // Optional currency symbol, followed by one or more digits, decimal separator, or comma. const formattedCurrencyRegex = /[^\d.,]*[\d.,]+/; diff --git a/tests/e2e-pw/specs/merchant/merchant-admin-analytics.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-analytics.spec.ts similarity index 90% rename from tests/e2e-pw/specs/merchant/merchant-admin-analytics.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-admin-analytics.spec.ts index c8a1847297d..c9e4333e642 100644 --- a/tests/e2e-pw/specs/merchant/merchant-admin-analytics.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-analytics.spec.ts @@ -6,16 +6,16 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import * as shopper from '../../utils/shopper'; -import { getMerchant, getShopper, useMerchant } from '../../utils/helpers'; +import * as shopper from '../../../utils/shopper'; +import { getMerchant, getShopper, useMerchant } from '../../../utils/helpers'; import { activateMulticurrency, ensureOrderIsProcessed, isMulticurrencyEnabled, tableDataHasLoaded, waitAndSkipTourComponent, -} from '../../utils/merchant'; -import { goToOrderAnalytics } from '../../utils/merchant-navigation'; +} from '../../../utils/merchant'; +import { goToOrderAnalytics } from '../../../utils/merchant-navigation'; test.describe( 'Admin order analytics', () => { let orderId: string; diff --git a/tests/e2e-pw/specs/merchant/merchant-admin-deposits.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-deposits.spec.ts similarity index 96% rename from tests/e2e-pw/specs/merchant/merchant-admin-deposits.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-admin-deposits.spec.ts index 96008887630..1363dc7b175 100644 --- a/tests/e2e-pw/specs/merchant/merchant-admin-deposits.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-deposits.spec.ts @@ -2,7 +2,7 @@ * External dependencies */ import { test, expect } from '@playwright/test'; -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; test.describe( 'Merchant deposits', () => { // Use the merchant user for this test suite. diff --git a/tests/e2e-pw/specs/merchant/merchant-admin-disputes.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-disputes.spec.ts similarity index 92% rename from tests/e2e-pw/specs/merchant/merchant-admin-disputes.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-admin-disputes.spec.ts index d2fed057b5d..f6a3d52d893 100644 --- a/tests/e2e-pw/specs/merchant/merchant-admin-disputes.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-disputes.spec.ts @@ -6,7 +6,7 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; test.describe( 'Merchant disputes', () => { // Use the merchant user for this test suite. diff --git a/tests/e2e-pw/specs/merchant/merchant-admin-transactions.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-transactions.spec.ts similarity index 67% rename from tests/e2e-pw/specs/merchant/merchant-admin-transactions.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-admin-transactions.spec.ts index 59547ed98a6..6ea22f96a5a 100644 --- a/tests/e2e-pw/specs/merchant/merchant-admin-transactions.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-admin-transactions.spec.ts @@ -6,9 +6,9 @@ import test, { expect } from '@playwright/test'; /** * Internal dependencies */ -import { shouldRunSubscriptionsTests } from '../../utils/constants'; -import { getMerchant } from '../../utils/helpers'; -import { goToTransactions } from '../../utils/merchant-navigation'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; +import { getMerchant } from '../../../utils/helpers'; +import { goToTransactions } from '../../../utils/merchant-navigation'; test.describe( 'Admin transactions', () => { test( 'page should load without errors', async ( { browser } ) => { @@ -28,6 +28,7 @@ test.describe( 'Admin transactions', () => { ).toBeVisible(); } - await expect( merchantPage ).toHaveScreenshot(); + // TODO: Uncomment this line after fixing the screenshot issue. + // await expect( merchantPage ).toHaveScreenshot(); } ); } ); diff --git a/tests/e2e-pw/specs/merchant/merchant-disputes-respond.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-respond.spec.ts similarity index 96% rename from tests/e2e-pw/specs/merchant/merchant-disputes-respond.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-respond.spec.ts index e80bd0bb372..8b2b0b5102a 100644 --- a/tests/e2e-pw/specs/merchant/merchant-disputes-respond.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-respond.spec.ts @@ -6,10 +6,13 @@ import { test, expect, Page, Browser } from '@playwright/test'; /** * Internal dependencies */ -import * as shopper from '../../utils/shopper'; -import { config } from '../../config/default'; -import { getAnonymousShopper, getMerchant } from '../../utils/helpers'; -import { goToOrder, goToPaymentDetails } from '../../utils/merchant-navigation'; +import * as shopper from '../../../utils/shopper'; +import { config } from '../../../config/default'; +import { getAnonymousShopper, getMerchant } from '../../../utils/helpers'; +import { + goToOrder, + goToPaymentDetails, +} from '../../../utils/merchant-navigation'; /** * Navigates to the payment details page for a given disputed order. @@ -50,8 +53,7 @@ async function createDisputedOrder( browser: Browser ) { const orderId = await test.step( 'Place an order as shopper, to be automatically disputed', async () => { - await shopperPage.goto( '/cart/' ); - await shopper.addCartProduct( shopperPage ); + await shopper.addToCartFromShopPage( shopperPage ); await shopperPage.goto( '/checkout/' ); await shopper.fillBillingAddress( diff --git a/tests/e2e-pw/specs/merchant/merchant-disputes-view-details-via-order-notice.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-view-details-via-order-notice.spec.ts similarity index 87% rename from tests/e2e-pw/specs/merchant/merchant-disputes-view-details-via-order-notice.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-view-details-via-order-notice.spec.ts index b7da795ce06..15b8a6de4bb 100644 --- a/tests/e2e-pw/specs/merchant/merchant-disputes-view-details-via-order-notice.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-disputes-view-details-via-order-notice.spec.ts @@ -6,10 +6,10 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import * as shopper from '../../utils/shopper'; -import { config } from '../../config/default'; -import { getMerchant, getShopper } from '../../utils/helpers'; -import { goToOrder } from '../../utils/merchant-navigation'; +import * as shopper from '../../../utils/shopper'; +import { config } from '../../../config/default'; +import { getMerchant, getShopper } from '../../../utils/helpers'; +import { goToOrder } from '../../../utils/merchant-navigation'; test.describe( 'Disputes > View dispute details via disputed order notice', @@ -19,8 +19,7 @@ test.describe( test.beforeEach( async ( { browser } ) => { const { shopperPage } = await getShopper( browser ); // Place an order to dispute later - await shopperPage.goto( '/cart/' ); - await shopper.addCartProduct( shopperPage ); + await shopper.addToCartFromShopPage( shopperPage ); await shopperPage.goto( '/checkout/' ); await shopper.fillBillingAddress( diff --git a/tests/e2e-pw/specs/merchant/merchant-multi-currency-widget.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-multi-currency-widget.spec.ts similarity index 93% rename from tests/e2e-pw/specs/merchant/merchant-multi-currency-widget.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-multi-currency-widget.spec.ts index f326812ca24..128e75363f7 100644 --- a/tests/e2e-pw/specs/merchant/merchant-multi-currency-widget.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-multi-currency-widget.spec.ts @@ -6,15 +6,16 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant, getShopper } from '../../utils/helpers'; +import { getMerchant, getShopper } from '../../../utils/helpers'; import { activateMulticurrency, addMulticurrencyWidget, deactivateMulticurrency, + ensureBlockSettingsPanelIsOpen, removeMultiCurrencyWidgets, restoreCurrencies, -} from '../../utils/merchant'; -import * as navigation from '../../utils/shopper-navigation'; +} from '../../../utils/merchant'; +import * as navigation from '../../../utils/shopper-navigation'; test.describe( 'Multi-currency widget setup', () => { let merchantPage: Page; @@ -73,16 +74,7 @@ test.describe( 'Multi-currency widget setup', () => { test( 'can update widget properties', async () => { await test.step( 'opens widget settings', async () => { - const settingsButton = merchantPage.locator( - '.interface-pinned-items > button[aria-label="Settings"]' - ); - const isSettingsButtonPressed = await settingsButton.evaluate( - ( node ) => node.getAttribute( 'aria-pressed' ) === 'true' - ); - - if ( ! isSettingsButtonPressed ) { - await settingsButton.click(); - } + await ensureBlockSettingsPanelIsOpen( merchantPage ); await merchantPage .locator( '[data-title="Currency Switcher Block"]' ) diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-full-refund.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-full-refund.spec.ts similarity index 93% rename from tests/e2e-pw/specs/merchant/merchant-orders-full-refund.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-orders-full-refund.spec.ts index 8ea26d1dd3f..be69815098f 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-full-refund.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-full-refund.spec.ts @@ -6,13 +6,16 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant, getShopper } from '../../utils/helpers'; +import { getMerchant, getShopper } from '../../../utils/helpers'; import { deactivateMulticurrency, isMulticurrencyEnabled, -} from '../../utils/merchant'; -import * as shopper from '../../utils/shopper'; -import { goToOrder, goToPaymentDetails } from '../../utils/merchant-navigation'; +} from '../../../utils/merchant'; +import * as shopper from '../../../utils/shopper'; +import { + goToOrder, + goToPaymentDetails, +} from '../../../utils/merchant-navigation'; test.describe( 'WooCommerce Payments - Full Refund', () => { let merchantPage: Page; diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-manual-capture.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-manual-capture.spec.ts similarity index 73% rename from tests/e2e-pw/specs/merchant/merchant-orders-manual-capture.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-orders-manual-capture.spec.ts index a23d9a35c09..366b34c5f7e 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-manual-capture.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-manual-capture.spec.ts @@ -1,24 +1,24 @@ /** * External dependencies */ -import { test, expect } from '@playwright/test'; +import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getShopper, getMerchant } from '../../utils/helpers'; -import { goToOrder } from '../../utils/merchant-navigation'; +import { getShopper, getMerchant } from '../../../utils/helpers'; +import { goToOrder } from '../../../utils/merchant-navigation'; import { activateCaptureLater, deactivateCaptureLater, -} from '../../utils/merchant'; -import { placeOrderWithOptions } from '../../utils/shopper'; +} from '../../../utils/merchant'; +import { placeOrderWithOptions } from '../../../utils/shopper'; /** * Local variables. */ -let orderId; -let merchantPage; +let orderId: string; +let merchantPage: Page; test.describe( 'Order > Manual Capture', () => { test.beforeAll( async ( { browser } ) => { @@ -40,11 +40,16 @@ test.describe( 'Order > Manual Capture', () => { // Merchant go to the order. await goToOrder( merchantPage, orderId ); + const orderTotal = await merchantPage + .getByRole( 'row', { name: 'Order Total: $' } ) + .locator( 'bdi' ) + .textContent(); + // Confirm order status is 'On hold', and that there's an 'authorized' note. await expect( merchantPage.getByTitle( 'On hold' ) ).toBeVisible(); await expect( merchantPage.getByText( - 'A payment of $18.00 was authorized using WooPayments' + `A payment of ${ orderTotal } was authorized using WooPayments` ) ).toBeVisible(); @@ -60,7 +65,7 @@ test.describe( 'Order > Manual Capture', () => { await expect( merchantPage.getByTitle( 'Processing' ) ).toBeVisible(); await expect( merchantPage.getByText( - 'A payment of $18.00 was successfully captured using WooPayments' + `A payment of ${ orderTotal } was successfully captured using WooPayments` ) ).toBeVisible(); } ); diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-partial-refund.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-partial-refund.spec.ts similarity index 86% rename from tests/e2e-pw/specs/merchant/merchant-orders-partial-refund.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-orders-partial-refund.spec.ts index 4bc0c0cec9c..c85a14f7cfa 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-partial-refund.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-partial-refund.spec.ts @@ -6,26 +6,37 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { getMerchant, getShopper } from '../../utils/helpers'; +import { config, Product } from '../../../config/default'; +import { getMerchant, getShopper } from '../../../utils/helpers'; import { fillCardDetails, placeOrder, setupProductCheckout, -} from '../../utils/shopper'; -import { goToShop } from '../../utils/shopper-navigation'; -import { goToOrder } from '../../utils/merchant-navigation'; +} from '../../../utils/shopper'; +import { goToOrder } from '../../../utils/merchant-navigation'; import { activateMulticurrency, deactivateMulticurrency, restoreCurrencies, -} from '../../utils/merchant'; +} from '../../../utils/merchant'; // Needs to be finished. test.describe( 'Order > Partial refund', () => { - const product1 = config.products.simple.name; - const product2 = 'Belt'; - const product3 = 'Hoodie with Logo'; + const product1 = config.products.simple; + const product2 = config.products.belt; + const product3 = config.products.hoodie_with_logo; + + const lineItems: [ Product, number ][][] = [ + [ + [ product1, 1 ], + [ product2, 1 ], + ], + [ + [ product1, 1 ], + [ product2, 2 ], + [ product3, 1 ], + ], + ]; /** * Elements: @@ -42,21 +53,20 @@ test.describe( 'Order > Partial refund', () => { [ 'Partially refund one product of two product order', { - lineItems: [ - [ product1, 1 ], - [ product2, 1 ], - ], + lineItems: lineItems[ 0 ].map( ( [ item, quantity ] ) => [ + item.name, + quantity, + ] ), refundInputs: [ { refundQty: 0, refundAmount: 5 } ], }, ], [ 'Refund two products of three product order', { - lineItems: [ - [ product1, 1 ], - [ product2, 2 ], - [ product3, 1 ], - ], + lineItems: lineItems[ 1 ].map( ( [ item, quantity ] ) => [ + item.name, + quantity, + ] ), refundInputs: [ { refundQty: 1, refundAmount: 18 }, { refundQty: 1, refundAmount: 55 }, @@ -71,9 +81,7 @@ test.describe( 'Order > Partial refund', () => { let merchantPage: Page, shopperPage: Page; const orderProducts = async ( dataTableIndex: number ) => { - const lineItems = dataTable[ dataTableIndex ][ 1 ].lineItems; - await goToShop( shopperPage ); - await setupProductCheckout( shopperPage, lineItems ); + await setupProductCheckout( shopperPage, lineItems[ dataTableIndex ] ); await fillCardDetails( shopperPage ); await placeOrder( shopperPage ); await expect( diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-refund-failures.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-refund-failures.spec.ts similarity index 93% rename from tests/e2e-pw/specs/merchant/merchant-orders-refund-failures.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-orders-refund-failures.spec.ts index 6f5dce44ac8..47e88a7a4c7 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-refund-failures.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-refund-failures.spec.ts @@ -6,10 +6,10 @@ import test, { Dialog, Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { emptyCart, placeOrderWithCurrency } from '../../utils/shopper'; -import { getMerchant, getShopper } from '../../utils/helpers'; -import { goToOrder } from '../../utils/merchant-navigation'; -import { ensureOrderIsProcessed } from '../../utils/merchant'; +import { emptyCart, placeOrderWithCurrency } from '../../../utils/shopper'; +import { getMerchant, getShopper } from '../../../utils/helpers'; +import { goToOrder } from '../../../utils/merchant-navigation'; +import { ensureOrderIsProcessed } from '../../../utils/merchant'; const selectorQty = '.refund_order_item_qty'; const selectorLineAmount = '.refund_line_total'; diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-status-change.spec.ts similarity index 96% rename from tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-orders-status-change.spec.ts index a1f2da82ccb..b62c72bc1c7 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-orders-status-change.spec.ts @@ -6,9 +6,9 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant, getShopper, isUIUnblocked } from '../../utils/helpers'; -import { placeOrderWithOptions } from '../../utils/shopper'; -import * as navigation from '../../utils/merchant-navigation'; +import { getMerchant, getShopper, isUIUnblocked } from '../../../utils/helpers'; +import { placeOrderWithOptions } from '../../../utils/shopper'; +import * as navigation from '../../../utils/merchant-navigation'; const orderStatusDropdownSelector = 'select[name="order_status"]'; const cancelModalSelector = 'div.wcpay-confirmation-modal'; diff --git a/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-payment-gateways-confirmation.spec.ts similarity index 98% rename from tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-payment-gateways-confirmation.spec.ts index fc26cad9ccc..fac2ec36aad 100644 --- a/tests/e2e-pw/specs/merchant/merchant-payment-gateways-confirmation.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-payment-gateways-confirmation.spec.ts @@ -6,7 +6,7 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; test.describe( 'payment gateways disable confirmation', () => { useMerchant(); diff --git a/tests/e2e-pw/specs/merchant/merchant-payment-settings-manual-capture.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-payment-settings-manual-capture.spec.ts similarity index 92% rename from tests/e2e-pw/specs/merchant/merchant-payment-settings-manual-capture.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-payment-settings-manual-capture.spec.ts index 54456e23e34..ab6c5c39928 100644 --- a/tests/e2e-pw/specs/merchant/merchant-payment-settings-manual-capture.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-payment-settings-manual-capture.spec.ts @@ -5,8 +5,8 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; -import { goToWooPaymentsSettings } from '../../utils/merchant-navigation'; +import { useMerchant } from '../../../utils/helpers'; +import { goToWooPaymentsSettings } from '../../../utils/merchant-navigation'; test.describe( 'As a merchant, I should be prompted a confirmation modal when I try to activate the manual capture', diff --git a/tests/e2e-pw/specs/merchant/merchant-progressive-onboarding.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/merchant-progressive-onboarding.spec.ts similarity index 91% rename from tests/e2e-pw/specs/merchant/merchant-progressive-onboarding.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/merchant-progressive-onboarding.spec.ts index 10c1e22e64b..4b294b2fbd3 100644 --- a/tests/e2e-pw/specs/merchant/merchant-progressive-onboarding.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/merchant-progressive-onboarding.spec.ts @@ -6,9 +6,9 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; -import * as devtools from '../../utils/devtools'; -import { goToConnect } from '../../utils/merchant-navigation'; +import { useMerchant } from '../../../utils/helpers'; +import * as devtools from '../../../utils/devtools'; +import { goToConnect } from '../../../utils/merchant-navigation'; test.describe( 'Admin merchant progressive onboarding', () => { useMerchant(); diff --git a/tests/e2e-pw/specs/merchant/multi-currency-on-boarding.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/multi-currency-on-boarding.spec.ts similarity index 97% rename from tests/e2e-pw/specs/merchant/multi-currency-on-boarding.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/multi-currency-on-boarding.spec.ts index 59e81183582..ec8d6e3cfd7 100644 --- a/tests/e2e-pw/specs/merchant/multi-currency-on-boarding.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/multi-currency-on-boarding.spec.ts @@ -5,7 +5,7 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; import { activateMulticurrency, activateTheme, @@ -15,8 +15,8 @@ import { getActiveThemeSlug, removeCurrency, restoreCurrencies, -} from '../../utils/merchant'; -import * as navigation from '../../utils/merchant-navigation'; +} from '../../../utils/merchant'; +import * as navigation from '../../../utils/merchant-navigation'; test.describe( 'Multi-currency on-boarding', () => { let page: Page; diff --git a/tests/e2e-pw/specs/merchant/multi-currency-setup.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/multi-currency-setup.spec.ts similarity index 90% rename from tests/e2e-pw/specs/merchant/multi-currency-setup.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/multi-currency-setup.spec.ts index ad66065b39f..6ec40b74182 100644 --- a/tests/e2e-pw/specs/merchant/multi-currency-setup.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/multi-currency-setup.spec.ts @@ -5,7 +5,7 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant, getShopper } from '../../utils/helpers'; +import { getMerchant, getShopper } from '../../../utils/helpers'; import { activateMulticurrency, addCurrency, @@ -16,9 +16,9 @@ import { setCurrencyCharmPricing, setCurrencyPriceRounding, setCurrencyRate, -} from '../../utils/merchant'; -import * as navigation from '../../utils/shopper-navigation'; -import { getPriceFromProduct } from '../../utils/shopper'; +} from '../../../utils/merchant'; +import * as navigation from '../../../utils/shopper-navigation'; +import { getPriceFromProduct } from '../../../utils/shopper'; test.describe( 'Multi-currency setup', () => { let merchantPage: Page; @@ -71,7 +71,7 @@ test.describe( 'Multi-currency setup', () => { test.beforeAll( async () => { await disableAllEnabledCurrencies( merchantPage ); - await navigation.goToShopWithCurrency( shopperPage, 'USD' ); + await navigation.goToShop( shopperPage, { currency: 'USD' } ); beanieRegularPrice = await getPriceFromProduct( shopperPage, @@ -98,10 +98,9 @@ test.describe( 'Multi-currency setup', () => { testData.currencyCode, '0' ); - await navigation.goToShopWithCurrency( - shopperPage, - testData.currencyCode - ); + await navigation.goToShop( shopperPage, { + currency: testData.currencyCode, + } ); const beaniePriceOnCurrency = await getPriceFromProduct( shopperPage, @@ -136,10 +135,9 @@ test.describe( 'Multi-currency setup', () => { testData.currencyCode, testData.charmPricing ); - await navigation.goToShopWithCurrency( - shopperPage, - testData.currencyCode - ); + await navigation.goToShop( shopperPage, { + currency: testData.currencyCode, + } ); const beaniePriceOnCurrency = await getPriceFromProduct( shopperPage, @@ -207,7 +205,7 @@ test.describe( 'Multi-currency setup', () => { Object.keys( currencyDecimalMap ).forEach( ( currency: string ) => { test( `the decimal points for ${ currency } are displayed correctly`, async () => { - await navigation.goToShopWithCurrency( shopperPage, currency ); + await navigation.goToShop( shopperPage, { currency } ); const beaniePriceOnCurrency = await getPriceFromProduct( shopperPage, diff --git a/tests/e2e-pw/specs/merchant/multi-currency.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/multi-currency.spec.ts similarity index 92% rename from tests/e2e-pw/specs/merchant/multi-currency.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/multi-currency.spec.ts index 92f42c3b97b..885410df350 100644 --- a/tests/e2e-pw/specs/merchant/multi-currency.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/multi-currency.spec.ts @@ -5,7 +5,7 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { useMerchant } from '../../utils/helpers'; +import { useMerchant } from '../../../utils/helpers'; import { activateMulticurrency, addMulticurrencyWidget, @@ -13,8 +13,8 @@ import { disableAllEnabledCurrencies, removeMultiCurrencyWidgets, restoreCurrencies, -} from '../../utils/merchant'; -import * as navigation from '../../utils/merchant-navigation'; +} from '../../../utils/merchant'; +import * as navigation from '../../../utils/merchant-navigation'; test.describe( 'Multi-currency', () => { let wasMulticurrencyEnabled: boolean; diff --git a/tests/e2e-pw/specs/merchant/woopay-setup.spec.ts b/tests/e2e-pw/specs/wcpay/merchant/woopay-setup.spec.ts similarity index 84% rename from tests/e2e-pw/specs/merchant/woopay-setup.spec.ts rename to tests/e2e-pw/specs/wcpay/merchant/woopay-setup.spec.ts index c36cdb135d0..4d26d56bed9 100644 --- a/tests/e2e-pw/specs/merchant/woopay-setup.spec.ts +++ b/tests/e2e-pw/specs/wcpay/merchant/woopay-setup.spec.ts @@ -5,8 +5,8 @@ import { test, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant } from '../../utils/helpers'; -import { activateWooPay, deactivateWooPay } from '../../utils/merchant'; +import { getMerchant } from '../../../utils/helpers'; +import { activateWooPay, deactivateWooPay } from '../../../utils/merchant'; test.describe( 'WooPay setup', () => { let merchantPage: Page; diff --git a/tests/e2e-pw/specs/shopper/klarna-checkout-purchase.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/klarna-checkout-purchase.spec.ts similarity index 71% rename from tests/e2e-pw/specs/shopper/klarna-checkout-purchase.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/klarna-checkout-purchase.spec.ts index b810f32ecb0..05f6881dcf0 100644 --- a/tests/e2e-pw/specs/shopper/klarna-checkout-purchase.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/klarna-checkout-purchase.spec.ts @@ -6,14 +6,11 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import * as shopper from '../../utils/shopper'; -import { getMerchant, getShopper } from '../../utils/helpers'; -import * as merchant from '../../utils/merchant'; -import { config } from '../../config/default'; -import { - goToProductPageBySlug, - goToShop, -} from '../../utils/shopper-navigation'; +import * as shopper from '../../../utils/shopper'; +import { getMerchant, getShopper } from '../../../utils/helpers'; +import * as merchant from '../../../utils/merchant'; +import { config } from '../../../config/default'; +import { goToProductPageBySlug } from '../../../utils/shopper-navigation'; test.describe( 'Klarna Checkout', () => { let merchantPage: Page; @@ -54,16 +51,19 @@ test.describe( 'Klarna Checkout', () => { } ); test( 'allows to use Klarna as a payment method', async () => { - await goToShop( shopperPage ); - await shopper.setupProductCheckout( shopperPage, [ [ 'Belt', 1 ] ], { - ...config.addresses.customer.billing, - // these are Klarna-specific values: - // https://docs.klarna.com/resources/test-environment/sample-customer-data/#united-states-of-america - email: 'customer@email.us', - phone: '+13106683312', - firstname: 'Test', - lastname: 'Person-us', - } ); + await shopper.setupProductCheckout( + shopperPage, + [ [ config.products.belt, 1 ] ], + { + ...config.addresses.customer.billing, + // these are Klarna-specific values: + // https://docs.klarna.com/resources/test-environment/sample-customer-data/#united-states-of-america + email: 'customer@email.us', + phone: '+13106683312', + firstname: 'Test', + lastname: 'Person-us', + } + ); await shopperPage .locator( '.wc_payment_methods' ) diff --git a/tests/e2e-pw/specs/shopper/multi-currency-checkout.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/multi-currency-checkout.spec.ts similarity index 91% rename from tests/e2e-pw/specs/shopper/multi-currency-checkout.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/multi-currency-checkout.spec.ts index a174ab1bcf7..8c832b03eb1 100644 --- a/tests/e2e-pw/specs/shopper/multi-currency-checkout.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/multi-currency-checkout.spec.ts @@ -5,15 +5,15 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { getMerchant, getShopper } from '../../utils/helpers'; +import { getMerchant, getShopper } from '../../../utils/helpers'; import { activateMulticurrency, addCurrency, deactivateMulticurrency, restoreCurrencies, -} from '../../utils/merchant'; -import { emptyCart, placeOrderWithCurrency } from '../../utils/shopper'; -import * as navigation from '../../utils/shopper-navigation'; +} from '../../../utils/merchant'; +import { emptyCart, placeOrderWithCurrency } from '../../../utils/shopper'; +import * as navigation from '../../../utils/shopper-navigation'; test.describe( 'Multi-currency checkout', () => { let merchantPage: Page; diff --git a/tests/e2e-pw/specs/shopper/shopper-bnpls-checkout.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-bnpls-checkout.spec.ts similarity index 75% rename from tests/e2e-pw/specs/shopper/shopper-bnpls-checkout.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-bnpls-checkout.spec.ts index 569093b959a..24f0a34bf64 100644 --- a/tests/e2e-pw/specs/shopper/shopper-bnpls-checkout.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-bnpls-checkout.spec.ts @@ -7,13 +7,15 @@ import { test, expect, Page } from '@playwright/test'; * Internal dependencies */ -import { getMerchant, getShopper } from '../../utils/helpers'; -import * as merchant from '../../utils/merchant'; -import * as shopper from '../../utils/shopper'; -import * as devtools from '../../utils/devtools'; +import { getMerchant, getShopper } from '../../../utils/helpers'; +import * as merchant from '../../../utils/merchant'; +import * as shopper from '../../../utils/shopper'; +import * as devtools from '../../../utils/devtools'; +import { config } from '../../../config/default'; const cardTestingProtectionStates = [ false, true ]; const bnplProviders = [ 'Affirm', 'Afterpay' ]; +const products = [ config.products.belt, config.products.sunglasses ]; test.describe( 'BNPL checkout', () => { let merchantPage: Page; @@ -54,9 +56,15 @@ test.describe( 'BNPL checkout', () => { } } ); - for ( const provider of bnplProviders ) { + for ( let i = 0; i < bnplProviders.length; i++ ) { + const provider = bnplProviders[ i ]; + test( `Checkout with ${ provider }`, async () => { - await shopper.addCartProduct( shopperPage, 17 ); // Belt + await shopper.addToCartFromShopPage( + shopperPage, + products[ i % 2 ] + ); + await shopper.setupCheckout( shopperPage ); await shopper.selectPaymentMethod( shopperPage, provider ); await shopper.expectFraudPreventionToken( @@ -64,6 +72,9 @@ test.describe( 'BNPL checkout', () => { ctpEnabled ); await shopper.placeOrder( shopperPage ); + await expect( + shopperPage.getByText( 'test payment page' ) + ).toBeVisible(); await shopperPage .getByText( 'Authorize Test Payment' ) .click(); diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-cart-coupon.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-cart-coupon.spec.ts similarity index 63% rename from tests/e2e-pw/specs/shopper/shopper-checkout-cart-coupon.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-cart-coupon.spec.ts index 2b0a94c33dd..e2cb0d1ec4e 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-cart-coupon.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-cart-coupon.spec.ts @@ -6,23 +6,18 @@ import test, { expect } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { - goToCart, - goToCheckout, - goToShop, -} from '../../utils/shopper-navigation'; -import { useShopper } from '../../utils/helpers'; +import { config } from '../../../config/default'; +import { goToCart, goToCheckout } from '../../../utils/shopper-navigation'; +import { useShopper } from '../../../utils/helpers'; import { addToCartFromShopPage, emptyCart, fillBillingAddress, fillCardDetails, placeOrder, + removeCoupon, setupCheckout, -} from '../../utils/shopper'; - -const productName = config.products.simple.name; +} from '../../../utils/shopper'; test.describe( 'Checkout with free coupon & after modifying cart on Checkout page', @@ -31,17 +26,32 @@ test.describe( useShopper(); test.beforeEach( async ( { page } ) => { - await goToShop( page ); - await addToCartFromShopPage( page, productName ); + await addToCartFromShopPage( page ); await goToCart( page ); await page.getByPlaceholder( 'Coupon code' ).fill( 'free' ); await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + await expect( + page.getByText( 'Coupon code applied successfully' ) + ).toBeVisible(); } ); test.afterEach( async ( { page } ) => { await emptyCart( page ); } ); + /** + * This afterAll step is to ensure that there is no coupon stored in the customer session. + * This is done in cases where a test may be interrupted causing the coupon state to persist + * in the next Atomic workflow run. + */ + test.afterAll( async ( { browser } ) => { + const cleanupPage = await browser.newPage(); + await addToCartFromShopPage( cleanupPage ); + await goToCart( cleanupPage ); + await removeCoupon( cleanupPage ); + await emptyCart( cleanupPage ); + } ); + test( 'Checkout with a free coupon', async ( { page } ) => { await goToCheckout( page ); await fillBillingAddress( page, config.addresses.customer.billing ); @@ -58,7 +68,7 @@ test.describe( test( 'Remove free coupon, then checkout', async ( { page } ) => { await goToCheckout( page ); - await page.getByRole( 'link', { name: '[Remove]' } ).click(); + await removeCoupon( page ); await setupCheckout( page, config.addresses.customer.billing ); await fillCardDetails( page, config.cards.basic ); await placeOrder( page ); diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-failures.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-failures.spec.ts similarity index 96% rename from tests/e2e-pw/specs/shopper/shopper-checkout-failures.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-failures.spec.ts index 5b21fc1570d..d9c1c09389a 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-failures.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-failures.spec.ts @@ -7,8 +7,8 @@ import { test, expect, Page } from '@playwright/test'; * Internal dependencies */ -import { config } from '../../config/default'; -import * as shopper from '../../utils/shopper'; +import { config } from '../../../config/default'; +import * as shopper from '../../../utils/shopper'; test.describe( 'Shopper > Checkout > Failures with various cards', () => { const waitForBanner = async ( page: Page, errorText: string ) => { @@ -16,7 +16,7 @@ test.describe( 'Shopper > Checkout > Failures with various cards', () => { }; test.beforeEach( async ( { page } ) => { - await shopper.addCartProduct( page ); + await shopper.addToCartFromShopPage( page ); await shopper.setupCheckout( page ); await shopper.selectPaymentMethod( page ); } ); diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase-site-editor.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-site-editor.spec.ts similarity index 86% rename from tests/e2e-pw/specs/shopper/shopper-checkout-purchase-site-editor.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-site-editor.spec.ts index 23a55fbd7b8..be8acba9d43 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase-site-editor.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-site-editor.spec.ts @@ -9,20 +9,19 @@ import test, { Page, expect } from '@playwright/test'; import { disableCardTestingProtection, enableCardTestingProtection, -} from '../../utils/devtools'; -import { getMerchant, getShopper } from '../../utils/helpers'; -import { activateTheme } from '../../utils/merchant'; -import { config } from '../../config/default'; +} from '../../../utils/devtools'; +import { getMerchant, getShopper } from '../../../utils/helpers'; +import { activateTheme } from '../../../utils/merchant'; +import { config } from '../../../config/default'; import { - addCartProduct, + addToCartFromShopPage, confirmCardAuthentication, emptyCart, expectFraudPreventionToken, fillCardDetails, placeOrder, setupCheckout, -} from '../../utils/shopper'; -import { goToShop } from '../../utils/shopper-navigation'; +} from '../../../utils/shopper'; /** * Tests for successful purchases with both card testing prevention enabled @@ -51,8 +50,7 @@ import { goToShop } from '../../utils/shopper-navigation'; test.beforeEach( async () => { await emptyCart( shopperPage ); - await goToShop( shopperPage ); - await addCartProduct( shopperPage ); + await addToCartFromShopPage( shopperPage ); await setupCheckout( shopperPage, config.addresses.customer.billing diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts similarity index 87% rename from tests/e2e-pw/specs/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts index 6fdd21bf1fb..e9bdd561b6b 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase-with-upe-methods.spec.ts @@ -13,12 +13,12 @@ import { disablePaymentMethods, enablePaymentMethods, restoreCurrencies, -} from '../../utils/merchant'; -import { getShopper, getMerchant } from '../../utils/helpers'; +} from '../../../utils/merchant'; +import { getShopper, getMerchant } from '../../../utils/helpers'; import { disableCardTestingProtection, enableCardTestingProtection, -} from '../../utils/devtools'; +} from '../../../utils/devtools'; import { addToCartFromShopPage, changeAccountCurrency, @@ -27,9 +27,9 @@ import { fillBillingAddress, focusPlaceOrderButton, placeOrder, -} from '../../utils/shopper'; -import { config } from '../../config/default'; -import { goToCheckout, goToShop } from '../../utils/shopper-navigation'; +} from '../../../utils/shopper'; +import { config } from '../../../config/default'; +import { goToCheckout } from '../../../utils/shopper-navigation'; test.describe( 'Enable UPE with deferred intent creation', () => { let wasMultiCurrencyEnabled = false; @@ -79,8 +79,7 @@ test.describe( 'Enable UPE with deferred intent creation', () => { } } ); test( 'should successfully place order with Bancontact', async () => { - await goToShop( shopperPage ); - await addToCartFromShopPage( shopperPage, 'Beanie' ); + await addToCartFromShopPage( shopperPage ); await goToCheckout( shopperPage ); await fillBillingAddress( shopperPage, diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase.spec.ts similarity index 85% rename from tests/e2e-pw/specs/shopper/shopper-checkout-purchase.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase.spec.ts index b2a0246d438..ddee5cb60ef 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-purchase.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-purchase.spec.ts @@ -7,10 +7,10 @@ import { test, expect, Page } from '@playwright/test'; * Internal dependencies */ -import { config } from '../../config/default'; -import * as shopper from '../../utils/shopper'; -import * as devtools from '../../utils/devtools'; -import { getMerchant, getShopper } from '../../utils/helpers'; +import { config } from '../../../config/default'; +import * as shopper from '../../../utils/shopper'; +import * as devtools from '../../../utils/devtools'; +import { getMerchant, getShopper } from '../../../utils/helpers'; test.describe( 'Successful purchase', () => { let merchantPage: Page; @@ -33,7 +33,7 @@ test.describe( 'Successful purchase', () => { } ); test.beforeEach( async () => { - await shopper.addCartProduct( shopperPage ); + await shopper.addToCartFromShopPage( shopperPage ); await shopper.setupCheckout( shopperPage, config.addresses.customer.billing diff --git a/tests/e2e-pw/specs/shopper/shopper-checkout-save-card-and-purchase.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-save-card-and-purchase.spec.ts similarity index 71% rename from tests/e2e-pw/specs/shopper/shopper-checkout-save-card-and-purchase.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-save-card-and-purchase.spec.ts index 4b2ca024df7..6e19e85c0b4 100644 --- a/tests/e2e-pw/specs/shopper/shopper-checkout-save-card-and-purchase.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-checkout-save-card-and-purchase.spec.ts @@ -6,40 +6,66 @@ import test, { Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { getAnonymousShopper, getShopper } from '../../utils/helpers'; +import { config } from '../../../config/default'; +import { + ensureCustomerIsLoggedIn, + getAnonymousShopper, + getShopper, +} from '../../../utils/helpers'; import { confirmCardAuthentication, deleteSavedCard, + emptyCart, fillCardDetails, placeOrder, selectSavedCardOnCheckout, setSavePaymentMethod, setupProductCheckout, -} from '../../utils/shopper'; -import { goToMyAccount, goToShop } from '../../utils/shopper-navigation'; +} from '../../../utils/shopper'; +import { goToMyAccount } from '../../../utils/shopper-navigation'; -type CardType = [ string, typeof config.cards.basic ]; +type CardType = [ + string, + typeof config.cards.basic, + typeof config.products.simple[] +]; const cards: Array< CardType > = [ - [ 'basic', config.cards.basic ], - [ '3ds', config.cards[ '3ds' ] ], + [ + 'basic', + config.cards.basic, + [ config.products.belt, config.products.cap ], + ], + [ + '3ds', + config.cards[ '3ds' ], + [ config.products.sunglasses, config.products.hoodie_with_logo ], + ], ]; test.describe( 'Saved cards', () => { - cards.forEach( ( [ cardType, card ] ) => { + cards.forEach( ( [ cardType, card, products ] ) => { test.describe( `When using a ${ cardType } card added on checkout`, () => { let shopperPage: Page; + test.beforeAll( async ( { browser }, { project } ) => { shopperPage = ( await getShopper( browser, true, project.use.baseURL ) ).shopperPage; + + await ensureCustomerIsLoggedIn( shopperPage, project ); } ); + + test.afterAll( async () => { + await emptyCart( shopperPage ); + } ); + test( 'should save the card', async ( {} ) => { - await goToShop( shopperPage ); - await setupProductCheckout( shopperPage ); + await setupProductCheckout( shopperPage, [ + [ products[ 0 ], 1 ], + ] ); await fillCardDetails( shopperPage, card ); await setSavePaymentMethod( shopperPage, true ); await placeOrder( shopperPage ); @@ -62,8 +88,9 @@ test.describe( 'Saved cards', () => { } ); test( 'should process a payment with the saved card', async ( {} ) => { - await goToShop( shopperPage ); - await setupProductCheckout( shopperPage ); + await setupProductCheckout( shopperPage, [ + [ products[ 1 ], 1 ], + ] ); await selectSavedCardOnCheckout( shopperPage, card ); await placeOrder( shopperPage ); if ( cardType === '3ds' ) { @@ -88,13 +115,13 @@ test.describe( 'Saved cards', () => { } ) => { const page: Page = ( await getAnonymousShopper( browser ) ) .shopperPage; - await goToShop( page ); await setupProductCheckout( page ); await expect( page.getByLabel( 'Save payment information to my account for future purchases.' ) ).not.toBeVisible(); + await emptyCart( page ); } ); } ); diff --git a/tests/e2e-pw/specs/shopper/shopper-multi-currency-widget.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-multi-currency-widget.spec.ts similarity index 78% rename from tests/e2e-pw/specs/shopper/shopper-multi-currency-widget.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-multi-currency-widget.spec.ts index cd2495bd6f0..63bb95e9e73 100644 --- a/tests/e2e-pw/specs/shopper/shopper-multi-currency-widget.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-multi-currency-widget.spec.ts @@ -5,12 +5,12 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import * as merchant from '../../utils/merchant'; -import * as shopper from '../../utils/shopper'; -import * as navigation from '../../utils/shopper-navigation'; -import { goToOrder } from '../../utils/merchant-navigation'; -import RestAPI from '../../utils/rest-api'; -import { getMerchant, getShopper } from '../../utils/helpers'; +import * as merchant from '../../../utils/merchant'; +import * as shopper from '../../../utils/shopper'; +import * as navigation from '../../../utils/shopper-navigation'; +import { goToOrder } from '../../../utils/merchant-navigation'; +import RestAPI from '../../../utils/rest-api'; +import { getMerchant, getShopper } from '../../../utils/helpers'; test.describe( 'Shopper Multi-Currency widget', () => { let merchantPage: Page; @@ -50,7 +50,7 @@ test.describe( 'Shopper Multi-Currency widget', () => { await expect( shopperPage ).toHaveURL( /.*currency=EUR/ ); // Change it back to USD for the other tests. - await navigation.goToShopWithCurrency( shopperPage, 'USD' ); + await navigation.goToShop( shopperPage, { currency: 'USD' } ); } ); test( 'at the product page', async () => { @@ -68,18 +68,14 @@ test.describe( 'Shopper Multi-Currency widget', () => { test.describe( 'Should not affect prices', () => { let orderId: string; + let orderPrice: string; test.afterEach( async () => { - await shopperPage.selectOption( - '.widget select[name=currency]', - 'EUR' - ); - await expect( - shopperPage.getByText( '$18.00 USD' ).first() + shopperPage.getByText( `${ orderPrice } USD` ).first() ).toBeVisible(); - await navigation.goToShopWithCurrency( shopperPage, 'USD' ); + await navigation.goToShop( shopperPage, { currency: 'USD' } ); } ); test( 'at the order received page', async () => { @@ -87,12 +83,19 @@ test.describe( 'Shopper Multi-Currency widget', () => { shopperPage, 'USD' ); + orderPrice = await shopperPage + .getByRole( 'row', { name: 'Total: $' } ) + .locator( '.amount' ) + .nth( 1 ) + .textContent(); } ); test( 'at My account > Orders', async () => { await navigation.goToOrders( shopperPage ); await expect( - shopperPage.getByLabel( `View order number ${ orderId }` ) + shopperPage + .locator( '.woocommerce-orders-table__cell-order-number' ) + .getByRole( 'link', { name: orderId } ) ).toBeVisible(); } ); } ); diff --git a/tests/e2e-pw/specs/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts similarity index 85% rename from tests/e2e-pw/specs/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts index d20e2cdc34f..ee48d7bef94 100644 --- a/tests/e2e-pw/specs/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-payment-methods-add-fail.spec.ts @@ -6,10 +6,17 @@ import test, { Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { goToMyAccount } from '../../utils/shopper-navigation'; -import { getShopper } from '../../utils/helpers'; -import { addSavedCard, confirmCardAuthentication } from '../../utils/shopper'; +import { config } from '../../../config/default'; +import { goToMyAccount } from '../../../utils/shopper-navigation'; +import { + ensureCustomerIsLoggedIn, + getShopper, + isUIUnblocked, +} from '../../../utils/helpers'; +import { + addSavedCard, + confirmCardAuthentication, +} from '../../../utils/shopper'; type CardType = [ string, typeof config.cards.declined, string ]; @@ -49,10 +56,16 @@ const cards: Array< CardType > = [ test.describe( 'Payment Methods', () => { let shopperPage: Page; - test.beforeEach( async ( { browser } ) => { + + test.beforeAll( async ( { browser }, { project } ) => { shopperPage = ( await getShopper( browser ) ).shopperPage; + await ensureCustomerIsLoggedIn( shopperPage, project ); + } ); + + test.beforeEach( async () => { await goToMyAccount( shopperPage, 'payment-methods' ); } ); + cards.forEach( ( [ cardType, card, errorText ] ) => { test.describe( `when attempting to add a ${ cardType } card`, () => { test( 'it should not add the card', async () => { @@ -62,6 +75,7 @@ test.describe( 'Payment Methods', () => { if ( 'declined-3ds' === cardType ) { await confirmCardAuthentication( shopperPage, false ); + await isUIUnblocked( shopperPage ); } // Verify that we get the expected error message. diff --git a/tests/e2e-pw/specs/shopper/shopper-myaccount-renew-subscription.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-renew-subscription.spec.ts similarity index 77% rename from tests/e2e-pw/specs/shopper/shopper-myaccount-renew-subscription.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-renew-subscription.spec.ts index 565c54e5863..53ba7b0945a 100644 --- a/tests/e2e-pw/specs/shopper/shopper-myaccount-renew-subscription.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-renew-subscription.spec.ts @@ -6,11 +6,11 @@ import { test, expect, Page } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { describeif, getShopper } from '../../utils/helpers'; -import * as shopper from '../../utils/shopper'; -import * as navigation from '../../utils/shopper-navigation'; -import { products, shouldRunSubscriptionsTests } from '../../utils/constants'; +import { config } from '../../../config/default'; +import { describeif, getShopper } from '../../../utils/helpers'; +import * as shopper from '../../../utils/shopper'; +import * as navigation from '../../../utils/shopper-navigation'; +import { shouldRunSubscriptionsTests } from '../../../utils/constants'; describeif( shouldRunSubscriptionsTests )( 'Subscriptions > Renew a subscription in my account', @@ -31,10 +31,11 @@ describeif( shouldRunSubscriptionsTests )( } ); test( 'should be able to purchase a subscription', async () => { - await shopper.addCartProduct( + await shopper.addToCartFromShopPage( page, - products.SUBSCRIPTION_SIGNUP_FEE + config.products.subscription_signup_fee ); + await shopper.setupCheckout( page, customerBillingConfig ); await shopper.selectPaymentMethod( page ); await shopper.fillCardDetails( page, config.cards.basic ); @@ -64,9 +65,9 @@ describeif( shouldRunSubscriptionsTests )( .click(); await page.getByText( 'Renew now' ).click(); - await page - .getByText( 'Complete checkout to renew now.' ) - .isVisible(); + await expect( + page.getByText( 'Complete checkout to renew now.' ) + ).toBeVisible(); await shopper.focusPlaceOrderButton( page ); await shopper.placeOrder( page ); await expect( diff --git a/tests/e2e-pw/specs/shopper/shopper-myaccount-saved-cards.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-saved-cards.spec.ts similarity index 86% rename from tests/e2e-pw/specs/shopper/shopper-myaccount-saved-cards.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-saved-cards.spec.ts index 9b66148c479..249d2130c6a 100644 --- a/tests/e2e-pw/specs/shopper/shopper-myaccount-saved-cards.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-myaccount-saved-cards.spec.ts @@ -6,9 +6,9 @@ import test, { Page, expect } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import { goToMyAccount, goToShop } from '../../utils/shopper-navigation'; -import { getShopper } from '../../utils/helpers'; +import { config, Product } from '../../../config/default'; +import { goToMyAccount } from '../../../utils/shopper-navigation'; +import { ensureCustomerIsLoggedIn, getShopper } from '../../../utils/helpers'; import { addSavedCard, confirmCardAuthentication, @@ -17,7 +17,7 @@ import { selectSavedCardOnCheckout, setDefaultPaymentMethod, setupProductCheckout, -} from '../../utils/shopper'; +} from '../../../utils/shopper'; type TestVariablesType = { [ key: string ]: { @@ -26,7 +26,7 @@ type TestVariablesType = { country: string; postalCode: string; }; - products: [ string, number ][]; + products: [ Product, number ][]; }; }; @@ -37,7 +37,7 @@ const cards = { country: 'US', postalCode: '94110', }, - products: [ [ 'Beanie', 1 ] ], + products: [ [ config.products.simple, 1 ] ], }, '3ds': { card: config.cards[ '3ds' ], @@ -45,7 +45,7 @@ const cards = { country: 'US', postalCode: '94110', }, - products: [ [ 'Belt', 1 ] ], + products: [ [ config.products.belt, 1 ] ], }, '3ds2': { card: config.cards[ '3ds2' ], @@ -53,7 +53,7 @@ const cards = { country: 'US', postalCode: '94110', }, - products: [ [ 'Cap', 1 ] ], + products: [ [ config.products.cap, 1 ] ], }, } as TestVariablesType; @@ -66,6 +66,8 @@ test.describe( 'Shopper can save and delete cards', () => { test.beforeAll( async ( { browser }, { project } ) => { shopperPage = ( await getShopper( browser, true, project.use.baseURL ) ) .shopperPage; + + await ensureCustomerIsLoggedIn( shopperPage, project ); } ); async function waitTwentySecondsSinceLastCardAdded( page: Page ) { @@ -104,11 +106,19 @@ test.describe( 'Shopper can save and delete cards', () => { await addSavedCard( shopperPage, config.cards.basic2, 'US', '94110' ); // Verify that the card was not added - await expect( - shopperPage.getByText( - "We're not able to add this payment method. Please refresh the page and try again." - ) - ).toBeVisible(); + try { + await expect( + shopperPage.getByText( + "We're not able to add this payment method. Please refresh the page and try again." + ) + ).toBeVisible( { timeout: 10000 } ); + } catch ( error ) { + await expect( + shopperPage.getByText( + 'You cannot add a new payment method so soon after the previous one. Please wait for 20 seconds.' + ) + ).toBeVisible(); + } // cleanup for the next tests await goToMyAccount( shopperPage, 'payment-methods' ); @@ -122,6 +132,10 @@ test.describe( 'Shopper can save and delete cards', () => { Object.entries( cards ).forEach( ( [ cardName, { card, address, products } ] ) => { test.describe( 'Testing card: ' + cardName, () => { + test.beforeAll( async ( {}, { project } ) => { + await ensureCustomerIsLoggedIn( shopperPage, project ); + } ); + test( `should add the ${ cardName } card as a new payment method`, async () => { await goToMyAccount( shopperPage, 'payment-methods' ); // Make sure that at least 20s had already elapsed since the last card was added. @@ -166,7 +180,6 @@ test.describe( 'Shopper can save and delete cards', () => { } ); test( `should be able to purchase with the saved ${ cardName } card`, async () => { - await goToShop( shopperPage ); await setupProductCheckout( shopperPage, products ); await selectSavedCardOnCheckout( shopperPage, card ); await placeOrder( shopperPage ); diff --git a/tests/e2e-pw/specs/shopper/shopper-pay-for-order.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-pay-for-order.spec.ts similarity index 83% rename from tests/e2e-pw/specs/shopper/shopper-pay-for-order.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-pay-for-order.spec.ts index ae5713568e7..028f95df1cd 100644 --- a/tests/e2e-pw/specs/shopper/shopper-pay-for-order.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-pay-for-order.spec.ts @@ -6,11 +6,11 @@ import { test, expect } from '@playwright/test'; /** * Internal dependencies */ -import { config } from '../../config/default'; -import * as shopper from '../../utils/shopper'; -import * as shopperNavigation from '../../utils/shopper-navigation'; -import * as devtools from '../../utils/devtools'; -import { getMerchant, getShopper } from '../../utils/helpers'; +import { config } from '../../../config/default'; +import * as shopper from '../../../utils/shopper'; +import * as shopperNavigation from '../../../utils/shopper-navigation'; +import * as devtools from '../../../utils/devtools'; +import { getMerchant, getShopper } from '../../../utils/helpers'; const cardTestingPreventionStates = [ { cardTestingPreventionEnabled: false }, @@ -34,7 +34,7 @@ test.describe( 'Shopper > Pay for Order', () => { } // Attempt to pay with a declined card. - await shopper.addCartProduct( shopperPage ); + await shopper.addToCartFromShopPage( shopperPage ); await shopper.setupCheckout( shopperPage ); await shopper.selectPaymentMethod( shopperPage ); await shopper.fillCardDetails( @@ -56,6 +56,12 @@ test.describe( 'Shopper > Pay for Order', () => { } ) .first(); await payForOrderButton.click(); + + await expect( + shopperPage.getByRole( 'heading', { + name: 'Pay for order', + } ) + ).toBeVisible(); await shopper.fillCardDetails( shopperPage, config.cards.basic diff --git a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-failures.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-failures.spec.ts similarity index 56% rename from tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-failures.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-failures.spec.ts index ec8a15b3622..19a5e5ff663 100644 --- a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-failures.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-failures.spec.ts @@ -11,12 +11,12 @@ import { describeif, getMerchant, getShopper, -} from '../../utils/helpers'; -import * as navigation from '../../utils/shopper-navigation'; -import * as shopper from '../../utils/shopper'; -import { addWCBCheckoutPage } from '../../utils/merchant'; -import { shouldRunWCBlocksTests } from '../../utils/constants'; -import { config } from '../../config/default'; +} from '../../../utils/helpers'; +import * as navigation from '../../../utils/shopper-navigation'; +import * as shopper from '../../../utils/shopper'; +import { addWCBCheckoutPage } from '../../../utils/merchant'; +import { shouldRunWCBlocksTests } from '../../../utils/constants'; +import { config } from '../../../config/default'; const failures = [ { @@ -59,6 +59,11 @@ const failures = [ }, ]; +const errorsInsideStripeFrame = [ + config.cards[ 'invalid-cvv-number' ], + config.cards[ 'declined-incorrect' ], +]; + describeif( shouldRunWCBlocksTests )( 'WooCommerce Blocks > Checkout failures', () => { @@ -77,7 +82,7 @@ describeif( shouldRunWCBlocksTests )( await addWCBCheckoutPage( merchantPage ); } - await shopper.addCartProduct( shopperPage ); + await shopper.addToCartFromShopPage( shopperPage ); await navigation.goToCheckoutWCB( shopperPage ); await shopper.fillBillingAddressWCB( shopperPage, @@ -85,6 +90,14 @@ describeif( shouldRunWCBlocksTests )( ); } ); + /** + * Reload the page after each test to ensure a clean state. + * Otherwise we get flaky results. + */ + test.afterEach( async () => { + await shopperPage.reload(); + } ); + test.afterAll( async () => { await shopper.emptyCart( shopperPage ); } ); @@ -92,15 +105,38 @@ describeif( shouldRunWCBlocksTests )( for ( const { card, error, auth } of failures ) { test( `Should show error – ${ error }`, async () => { await shopper.fillCardDetailsWCB( shopperPage, card ); - await shopperPage - .getByRole( 'button', { name: 'Place Order' } ) - .click(); + await shopper.placeOrderWCB( shopperPage, false ); + if ( auth ) { + const placeOrderButton = shopperPage.locator( + '.wc-block-components-checkout-place-order-button' + ); + await expect( placeOrderButton ).toBeDisabled(); + await expect( placeOrderButton ).toHaveClass( + /wc-block-components-button--loading/ + ); await shopper.confirmCardAuthentication( shopperPage ); } - await expect( - shopperPage.getByLabel( 'Checkout' ).getByText( error ) - ).toBeVisible(); + + if ( errorsInsideStripeFrame.includes( card ) ) { + await shopperPage.waitForSelector( + '.__PrivateStripeElement' + ); + const frameHandle = await shopperPage.waitForSelector( + '#payment-method .wcpay-payment-element iframe[name^="__privateStripeFrame"]' + ); + const stripeFrame = await frameHandle.contentFrame(); + + await expect( + stripeFrame.getByText( error ) + ).toBeVisible(); + } else { + await expect( + shopperPage + .locator( '.wc-block-checkout__form' ) + .getByText( error ) + ).toBeVisible(); + } } ); } } diff --git a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-purchase.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-purchase.spec.ts similarity index 77% rename from tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-purchase.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-purchase.spec.ts index ef28ccc098e..89fc0797ec7 100644 --- a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-checkout-purchase.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-checkout-purchase.spec.ts @@ -11,17 +11,17 @@ import { describeif, getMerchant, getShopper, -} from '../../utils/helpers'; -import { shouldRunWCBlocksTests } from '../../utils/constants'; -import { addWCBCheckoutPage } from '../../utils/merchant'; -import { goToCheckoutWCB } from '../../utils/shopper-navigation'; +} from '../../../utils/helpers'; +import { shouldRunWCBlocksTests } from '../../../utils/constants'; +import { addWCBCheckoutPage } from '../../../utils/merchant'; +import { goToCheckoutWCB } from '../../../utils/shopper-navigation'; import { - addCartProduct, + addToCartFromShopPage, confirmCardAuthentication, fillBillingAddressWCB, fillCardDetailsWCB, -} from '../../utils/shopper'; -import { config } from '../../config/default'; +} from '../../../utils/shopper'; +import { config } from '../../../config/default'; describeif( shouldRunWCBlocksTests )( 'WooCommerce Blocks > Successful purchase', @@ -42,7 +42,7 @@ describeif( shouldRunWCBlocksTests )( } ); test( 'using a basic card', async () => { - await addCartProduct( shopperPage ); + await addToCartFromShopPage( shopperPage, config.products.belt ); await goToCheckoutWCB( shopperPage ); await fillBillingAddressWCB( shopperPage, @@ -60,7 +60,10 @@ describeif( shouldRunWCBlocksTests )( } ); test( 'using a 3DS card', async () => { - await addCartProduct( shopperPage ); + await addToCartFromShopPage( + shopperPage, + config.products.sunglasses + ); await goToCheckoutWCB( shopperPage ); await fillBillingAddressWCB( shopperPage, diff --git a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts similarity index 65% rename from tests/e2e-pw/specs/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts rename to tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts index b212b94aea9..81eb9772ff2 100644 --- a/tests/e2e-pw/specs/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts +++ b/tests/e2e-pw/specs/wcpay/shopper/shopper-wc-blocks-saved-card-checkout-and-usage.spec.ts @@ -9,25 +9,27 @@ import test, { Page, expect } from '@playwright/test'; import { checkPageExists, describeif, + ensureCustomerIsLoggedIn, getMerchant, getShopper, -} from '../../utils/helpers'; -import { shouldRunWCBlocksTests } from '../../utils/constants'; +} from '../../../utils/helpers'; +import { shouldRunWCBlocksTests } from '../../../utils/constants'; import { goToMyAccount, - goToShop, goToCheckoutWCB, -} from '../../utils/shopper-navigation'; +} from '../../../utils/shopper-navigation'; import { - addCartProduct, + addToCartFromShopPage, deleteSavedCard, emptyCart, fillBillingAddressWCB, fillCardDetailsWCB, + placeOrderWCB, selectSavedCardOnCheckout, -} from '../../utils/shopper'; -import { addWCBCheckoutPage } from '../../utils/merchant'; -import { config } from '../../config/default'; + setSavePaymentMethod, +} from '../../../utils/shopper'; +import { addWCBCheckoutPage } from '../../../utils/merchant'; +import { config } from '../../../config/default'; describeif( shouldRunWCBlocksTests )( 'WooCommerce Blocks > Saved cards', @@ -36,7 +38,10 @@ describeif( shouldRunWCBlocksTests )( const card = config.cards.basic; test.beforeAll( async ( { browser }, { project } ) => { - shopperPage = ( await getShopper( browser ) ).shopperPage; + shopperPage = ( + await getShopper( browser, true, project.use.baseURL ) + ).shopperPage; + if ( ! ( await checkPageExists( shopperPage, @@ -46,31 +51,22 @@ describeif( shouldRunWCBlocksTests )( const { merchantPage } = await getMerchant( browser ); await addWCBCheckoutPage( merchantPage ); } + + await ensureCustomerIsLoggedIn( shopperPage, project ); } ); test( 'should be able to save basic card on Blocks checkout', async () => { await emptyCart( shopperPage ); - await goToShop( shopperPage ); - await addCartProduct( shopperPage ); + await addToCartFromShopPage( shopperPage, config.products.belt ); await goToCheckoutWCB( shopperPage ); await fillBillingAddressWCB( shopperPage, config.addresses.customer.billing ); await fillCardDetailsWCB( shopperPage, config.cards.basic ); - await shopperPage - .getByLabel( - 'Save payment information to my account for future purchases.' - ) - .click(); - await shopperPage - .getByRole( 'button', { name: 'Place Order' } ) - .click(); - await expect( - shopperPage.getByRole( 'heading', { - name: 'Order received', - } ) - ).toBeVisible(); + await setSavePaymentMethod( shopperPage, true ); + await placeOrderWCB( shopperPage ); + await goToMyAccount( shopperPage, 'payment-methods' ); await expect( shopperPage.getByText( card.label ).first() @@ -85,23 +81,16 @@ describeif( shouldRunWCBlocksTests )( } ); test( 'should process a payment with the saved card from Blocks checkout', async () => { - await goToShop( shopperPage ); - await addCartProduct( shopperPage ); + await addToCartFromShopPage( shopperPage, config.products.cap ); await goToCheckoutWCB( shopperPage ); await fillBillingAddressWCB( shopperPage, config.addresses.customer.billing ); await selectSavedCardOnCheckout( shopperPage, card ); - await shopperPage - .getByRole( 'button', { name: 'Place Order' } ) - .click(); - await expect( - shopperPage.getByRole( 'heading', { - name: 'Order received', - } ) - ).toBeVisible(); + await placeOrderWCB( shopperPage ); } ); + test( 'should delete the card ', async () => { await goToMyAccount( shopperPage, 'payment-methods' ); await deleteSavedCard( shopperPage, card ); diff --git a/tests/e2e-pw/utils/constants.ts b/tests/e2e-pw/utils/constants.ts index f223b1fd078..c9c3ae7d8c8 100644 --- a/tests/e2e-pw/utils/constants.ts +++ b/tests/e2e-pw/utils/constants.ts @@ -6,7 +6,6 @@ export const shouldRunActionSchedulerTests = export const shouldRunWCBlocksTests = process.env.SKIP_WC_BLOCKS_TESTS !== '1'; -export const products = { - SUBSCRIPTION_SIGNUP_FEE: 70, - SUBSCRIPTION_NO_SIGNUP_FEE: 88, -}; +export const wooCoreVersion = process.env.E2E_WC_VERSION; + +export const isAtomicSite = process.env.NODE_ENV === 'atomic'; diff --git a/tests/e2e-pw/utils/helpers.ts b/tests/e2e-pw/utils/helpers.ts index 7f01beeb007..dbb6cb2cef0 100644 --- a/tests/e2e-pw/utils/helpers.ts +++ b/tests/e2e-pw/utils/helpers.ts @@ -1,8 +1,16 @@ +/* eslint-disable no-console */ /** * External dependencies */ import path from 'path'; -import { test, Page, Browser, BrowserContext, expect } from '@playwright/test'; +import { + test, + Page, + Browser, + BrowserContext, + expect, + FullProject, +} from '@playwright/test'; /** * Internal dependencies @@ -26,9 +34,17 @@ export const wpAdminLogin = async ( page: Page, user: { username: string; password: string } ): Promise< void > => { - await page.goto( `/wp-admin` ); + await page.goto( '/wp-admin' ); + await page.getByLabel( 'Username or Email Address' ).fill( user.username ); - await page.getByLabel( 'Password', { exact: true } ).fill( user.password ); // Need exact match to avoid resolving "Show password" button. + + // Need exact match to avoid resolving "Show password" button. + const passwordInput = page.getByLabel( 'Password', { exact: true } ); + + // The focus is used to avoid the password being filled in the username field. + await passwordInput.focus(); + await passwordInput.fill( user.password ); + await page.getByRole( 'button', { name: 'Log In' } ).click(); }; @@ -96,12 +112,12 @@ export const getShopper = async ( shopperPage.locator( '.woocommerce-MyAccount-navigation-link--customer-logout' ) - ).toBeVisible( { timeout: 1000 } ); + ).toBeVisible(); await expect( shopperPage.locator( 'div.woocommerce-MyAccount-content > p >> nth=0' ) - ).toContainText( 'Hello', { timeout: 1000 } ); + ).toContainText( 'Hello' ); await shopperPage .context() .storageState( { path: customerStorageFile } ); @@ -158,3 +174,88 @@ export const checkPageExists = async ( return false; } ); }; + +export const isCustomerLoggedIn = async ( page: Page ) => { + await page.goto( '/my-account' ); + const logoutLink = page.locator( + '.woocommerce-MyAccount-navigation-link--customer-logout' + ); + + return await logoutLink.isVisible(); +}; + +export const loginAsCustomer = async ( + page: Page, + customer: { username: string; password: string } +) => { + let customerLoggedIn = false; + const customerRetries = 5; + + for ( let i = 0; i < customerRetries; i++ ) { + try { + // eslint-disable-next-line no-console + console.log( 'Trying to log-in as customer...' ); + await wpAdminLogin( page, customer ); + + await page.goto( '/my-account' ); + await expect( + page.locator( + '.woocommerce-MyAccount-navigation-link--customer-logout' + ) + ).toBeVisible(); + await expect( + page.locator( 'div.woocommerce-MyAccount-content > p >> nth=0' ) + ).toContainText( 'Hello' ); + + console.log( 'Logged-in as customer successfully.' ); + customerLoggedIn = true; + break; + } catch ( e ) { + console.log( + `Customer log-in failed. Retrying... ${ i }/${ customerRetries }` + ); + console.log( e ); + } + } + + if ( ! customerLoggedIn ) { + throw new Error( + 'Cannot proceed e2e test, as customer login failed. Please check if the test site has been setup correctly.' + ); + } + + await page.context().storageState( { path: customerStorageFile } ); +}; + +/** + * Adds a special cookie during the session to avoid the support session detection page. + * This is temporarily displayed when navigating to the login page while Jetpack SSO and protect modules are disabled. + * Relevant for Atomic sites only. + */ +export const addSupportSessionDetectedCookie = async ( + page: Page, + project: FullProject +) => { + if ( process.env.NODE_ENV !== 'atomic' ) return; + + const domain = new URL( project.use.baseURL ).hostname; + + await page.context().addCookies( [ + { + value: 'true', + name: '_wpcomsh_support_session_detected', + path: '/', + domain, + }, + ] ); +}; + +export const ensureCustomerIsLoggedIn = async ( + page: Page, + project: FullProject +) => { + if ( ! ( await isCustomerLoggedIn( page ) ) ) { + await addSupportSessionDetectedCookie( page, project ); + await loginAsCustomer( page, config.users.customer ); + } +}; diff --git a/tests/e2e-pw/utils/merchant-navigation.ts b/tests/e2e-pw/utils/merchant-navigation.ts index d258e5d09e4..b00fc1f2f78 100644 --- a/tests/e2e-pw/utils/merchant-navigation.ts +++ b/tests/e2e-pw/utils/merchant-navigation.ts @@ -4,6 +4,11 @@ import { Page } from 'playwright/test'; import { dataHasLoaded } from './merchant'; +/** + * Internal dependencies + */ +import { wooCoreVersion } from './constants'; + export const goToOrder = async ( page: Page, orderId: string ) => { await page.goto( `/wp-admin/admin.php?page=wc-orders&action=edit&id=${ orderId }`, @@ -114,19 +119,21 @@ export const goToConnect = async ( page: Page ) => { await dataHasLoaded( page ); }; -export const goToSubscriptions = async ( page: Page ) => - await page.goto( '/wp-admin/admin.php?page=wc-orders--shop_subscription', { +export const goToSubscriptions = async ( page: Page ) => { + const subscriptionsUrl = + wooCoreVersion === '7.7.0' + ? '/wp-admin/edit.php?post_type=shop_subscription' + : '/wp-admin/admin.php?page=wc-orders--shop_subscription'; + await page.goto( subscriptionsUrl, { waitUntil: 'load', } ); +}; export const goToSubscriptionPage = async ( page: Page, subscriptionId: number ) => { await goToSubscriptions( page ); - const orderRow = page.locator( - 'tr#order-' + subscriptionId + ' .order_title a:nth-child(1)' - ); - orderRow.evaluate( ( el: HTMLLinkElement ) => el.click() ); + await page.getByRole( 'link', { name: `#${ subscriptionId }` } ).click(); await dataHasLoaded( page ); }; diff --git a/tests/e2e-pw/utils/merchant.ts b/tests/e2e-pw/utils/merchant.ts index 5ea62e10962..c7898863fd4 100644 --- a/tests/e2e-pw/utils/merchant.ts +++ b/tests/e2e-pw/utils/merchant.ts @@ -2,8 +2,13 @@ * External dependencies */ import { Page, expect } from 'playwright/test'; + +/** + * Internal dependencies + */ import * as navigation from './merchant-navigation'; import RestAPI from './rest-api'; +import { isAtomicSite } from './constants'; /** * Checks if the data has loaded on the page. @@ -59,18 +64,28 @@ const expectSnackbarWithText = async ( expectedText: string, timeout = 10000 ) => { - await expect( - page.locator( '.components-snackbar__content', { - hasText: expectedText, - } ) - ).toBeVisible( { - timeout: timeout, + const snackbar = page.locator( '.components-snackbar__content', { + hasText: expectedText, } ); + + await expect( snackbar ).toBeVisible( { timeout } ); + await page.waitForTimeout( 2000 ); }; export const saveWooPaymentsSettings = async ( page: Page ) => { await ensureSupportPhoneIsFilled( page ); + if ( isAtomicSite ) { + page.on( 'dialog', async ( dialog ) => { + try { + await dialog.accept(); + } catch ( error ) { + /* eslint-disable no-console */ + console.log( 'Error while accepting dialog', error ); + } + } ); + } + await page.getByRole( 'button', { name: 'Save changes' } ).click(); await expectSnackbarWithText( page, 'Settings saved.' ); }; @@ -392,6 +407,19 @@ export const deactivateWooPay = async ( page: Page ) => { await saveWooPaymentsSettings( page ); }; +export const ensureBlockSettingsPanelIsOpen = async ( page: Page ) => { + const settingsButton = page.locator( + '.interface-pinned-items > button[aria-label="Settings"]' + ); + const isSettingsButtonPressed = await settingsButton.evaluate( + ( node ) => node.getAttribute( 'aria-pressed' ) === 'true' + ); + + if ( ! isSettingsButtonPressed ) { + await settingsButton.click(); + } +}; + export const addWCBCheckoutPage = async ( page: Page ) => { await page.goto( '/wp-admin/edit.php?post_type=page', { waitUntil: 'load', @@ -414,6 +442,8 @@ export const addWCBCheckoutPage = async ( page: Page ) => { await editor.getByLabel( 'Add title' ).fill( 'Checkout WCB' ); await editor.getByLabel( 'Add block' ).click(); + await ensureBlockSettingsPanelIsOpen( page ); + await page.getByPlaceholder( 'Search' ).fill( 'Checkout' ); await page.getByRole( 'option', { name: 'Checkout', exact: true } ).click(); @@ -421,10 +451,32 @@ export const addWCBCheckoutPage = async ( page: Page ) => { await page.waitForTimeout( 500 ); await page.keyboard.press( 'Escape' ); // to dismiss a dialog if present + // Enable the "Company" field if it's not already enabled. + await page.getByLabel( 'Document Overview' ).click(); + await page.waitForTimeout( 1000 ); + await expect( page.locator( '.editor-list-view-sidebar' ) ).toBeVisible(); + await expect( page.getByText( 'List View' ) ).toBeVisible(); + await page.locator( '.block-editor-list-view__expander > svg' ).click(); + await page.getByText( 'Checkout Fields' ).click(); + + const companyCheckbox = page + .locator( '.components-toggle-control' ) + .getByLabel( 'Company' ); + + if ( ! ( await companyCheckbox.isChecked() ) ) { + await companyCheckbox.check(); + } + // Publish the page await page.locator( 'button.editor-post-publish-panel__toggle' ).click(); - await page.waitForTimeout( 500 ); - await page.locator( 'button.editor-post-publish-button' ).click(); + + const publishButton = page.locator( 'button.editor-post-publish-button' ); + await publishButton.click(); + + if ( await page.getByText( 'Are you ready to publish?' ).isVisible() ) { + await publishButton.nth( 1 ).click(); + } + await expect( page.getByText( 'Checkout WCB is now live.' ) ).toBeVisible(); }; diff --git a/tests/e2e-pw/utils/rest-api.ts b/tests/e2e-pw/utils/rest-api.ts index 380429b34e5..57088d5f913 100644 --- a/tests/e2e-pw/utils/rest-api.ts +++ b/tests/e2e-pw/utils/rest-api.ts @@ -11,6 +11,7 @@ import { config } from '../config/default'; const userEndpoint = '/wc/v3/customers'; const ordersEndpoint = '/wc/v3/orders'; const widgetEndpoint = '/wp/v2/widgets'; +const productsEndpoint = '/wc/v3/products'; export type CustomerType = typeof config.users.customer; export type AddressType = Omit< @@ -157,10 +158,20 @@ class RestAPI { async createOrder(): Promise< string > { const client = this.getAdminClient(); + const products = await client.get( + `${ productsEndpoint }?search=${ config.products.simple.name }` + ); + + if ( ! products.data || ! products.data.length ) { + throw new Error( 'No products found.' ); + } + + const [ product ] = products.data; + const order = await client.post( ordersEndpoint, { line_items: [ { - product_id: 16, + product_id: product.id, quantity: 1, }, ], diff --git a/tests/e2e-pw/utils/shopper-navigation.ts b/tests/e2e-pw/utils/shopper-navigation.ts index 1d8ebdecc02..6c8b5547414 100644 --- a/tests/e2e-pw/utils/shopper-navigation.ts +++ b/tests/e2e-pw/utils/shopper-navigation.ts @@ -8,16 +8,21 @@ import { Page } from 'playwright/test'; */ import { isUIUnblocked } from './helpers'; -export const goToShop = async ( page: Page, pageNumber?: number ) => { +export const goToShop = async ( + page: Page, + { pageNumber, currency }: { pageNumber?: number; currency?: string } = {} +) => { + let url = '/shop/'; + if ( pageNumber ) { - await page.goto( `/shop/page/` + pageNumber, { waitUntil: 'load' } ); - } else { - await page.goto( `/shop/`, { waitUntil: 'load' } ); + url += `page/${ pageNumber }/`; + } + + if ( currency ) { + url += `?currency=${ currency }`; } -}; -export const goToShopWithCurrency = async ( page: Page, currency: string ) => { - await page.goto( `/shop/?currency=${ currency }`, { waitUntil: 'load' } ); + await page.goto( url, { waitUntil: 'load' } ); }; export const goToProductPageBySlug = async ( diff --git a/tests/e2e-pw/utils/shopper.ts b/tests/e2e-pw/utils/shopper.ts index f9c66e49b95..0099c3a057e 100644 --- a/tests/e2e-pw/utils/shopper.ts +++ b/tests/e2e-pw/utils/shopper.ts @@ -6,7 +6,7 @@ import { Page, expect } from 'playwright/test'; * Internal dependencies */ import * as navigation from './shopper-navigation'; -import { config, CustomerAddress } from '../config/default'; +import { config, CustomerAddress, Product } from '../config/default'; import { isUIUnblocked } from './helpers'; /** @@ -68,9 +68,22 @@ export const fillBillingAddressWCB = async ( const billingAddressForm = page.getByRole( 'group', { name: 'Billing address', } ); - await billingAddressForm - .getByLabel( 'Country/Region' ) - .selectOption( billingAddress.country ); + + const countryField = billingAddressForm.getByLabel( 'Country/Region' ); + + try { + await countryField.selectOption( billingAddress.country ); + } catch ( error ) { + // Fallback for WC 7.7.0. + await countryField.focus(); + await countryField.fill( billingAddress.country ); + + await page + .locator( '.components-form-token-field__suggestion' ) + .first() + .click(); + } + await billingAddressForm .getByLabel( 'First Name' ) .fill( billingAddress.firstname ); @@ -93,10 +106,17 @@ export const fillBillingAddressWCB = async ( .getByLabel( 'Apartment, suite, etc. (optional)' ) .fill( billingAddress.addresssecondline ); await billingAddressForm.getByLabel( 'City' ).fill( billingAddress.city ); + + const stateInput = billingAddressForm.getByLabel( 'State', { + exact: true, + } ); if ( billingAddress.state ) { - await billingAddressForm - .getByLabel( 'State' ) - .selectOption( billingAddress.state ); + try { + await stateInput.selectOption( billingAddress.state ); + } catch ( error ) { + // Fallback for WC 7.7.0. + await stateInput.fill( billingAddress.state ); + } } await billingAddressForm .getByLabel( 'ZIP Code' ) @@ -119,11 +139,25 @@ export const placeOrder = async ( page: Page ) => { } }; -export const addCartProduct = async ( +export const placeOrderWCB = async ( page: Page, - productId = 16 // Beanie + confirmOrderReceived = true ) => { - await page.goto( `/shop/?add-to-cart=${ productId }` ); + const placeOrderButton = page.getByRole( 'button', { + name: 'Place Order', + } ); + + await placeOrderButton.focus(); + await waitForUiRefresh( page ); + + await placeOrderButton.click(); + + if ( confirmOrderReceived ) { + await page.waitForURL( /\/order-received\// ); + await expect( + page.getByRole( 'heading', { name: 'Order received' } ) + ).toBeVisible(); + } }; const ensureSavedCardNotSelected = async ( page: Page ) => { @@ -206,7 +240,6 @@ export const fillCardDetailsWCB = async ( ); const stripeFrame = await frameHandle.contentFrame(); if ( ! stripeFrame ) return; - await stripeFrame.waitForLoadState( 'networkidle' ); await stripeFrame.getByPlaceholder( '1234 1234 1234' ).fill( card.number ); await stripeFrame .getByPlaceholder( 'MM / YY' ) @@ -219,6 +252,9 @@ export const confirmCardAuthentication = async ( page: Page, authorize = true ) => { + // Wait for the Stripe modal to appear. + await page.waitForTimeout( 5000 ); + // Stripe card input also uses __privateStripeFrame as a prefix, so need to make sure we wait for an iframe that // appears at the top of the DOM. await page.waitForSelector( @@ -266,30 +302,35 @@ export const getPriceFromProduct = async ( page: Page, slug: string ) => { * Adds a product to the cart from the shop page. * * @param {Page} page The Playwright page object. - * @param {string|number} product The product ID or title to add to the cart. + * @param {Product} product The product add to the cart. */ export const addToCartFromShopPage = async ( page: Page, - product: string | number + product: Product = config.products.simple, + currency?: string ) => { - if ( Number.isInteger( product ) ) { - const addToCartSelector = `a[data-product_id="${ product }"]`; + await navigation.goToShop( page, { + pageNumber: product.pageNumber, + currency, + } ); - await page.locator( addToCartSelector ).click(); - await expect( - page.locator( `${ addToCartSelector }.added` ) - ).toBeVisible(); - } else { - // These unicode characters are the smart (or curly) quotes: “ ”. - const addToCartRegex = new RegExp( - `Add to cart: \u201C${ product }\u201D` - ); + // This generic regex will match the aria-label for the "Add to cart" button for any product. + // It should work for WC 7.7.0 and later. + // These unicode characters are the smart (or curly) quotes: “ ”. + const addToCartRegex = new RegExp( + `Add\\s+(?:to\\s+cart:\\s*)?\u201C${ product.name }\u201D(?:\\s+to\\s+your\\s+cart)?` + ); - await page.getByLabel( addToCartRegex ).click(); - await expect( page.getByLabel( addToCartRegex ) ).toHaveAttribute( - 'class', - /added/ - ); + const addToCartButton = page.getByLabel( addToCartRegex ); + await addToCartButton.click(); + + try { + await expect( addToCartButton ).toHaveAttribute( 'class', /added/, { + timeout: 5000, + } ); + } catch ( error ) { + // fallback for a different theme. + await expect( addToCartButton ).toHaveText( /in cart/ ); } }; @@ -300,11 +341,23 @@ export const selectPaymentMethod = async ( await page.getByText( paymentMethod ).click(); }; +/** + * The checkout page can sometimes be blank, so we need to reload it. + * + * @param page Page + */ +export const ensureCheckoutIsLoaded = async ( page: Page ) => { + if ( ! ( await page.locator( '#billing_first_name' ).isVisible() ) ) { + await page.reload(); + } +}; + export const setupCheckout = async ( page: Page, billingAddress: CustomerAddress = config.addresses.customer.billing ) => { await navigation.goToCheckout( page ); + await ensureCheckoutIsLoaded( page ); await fillBillingAddress( page, billingAddress ); await waitForUiRefresh( page ); await isUIUnblocked( page ); @@ -322,21 +375,23 @@ export const setupCheckout = async ( */ export async function setupProductCheckout( page: Page, - lineItems: Array< [ string, number ] > = [ - [ config.products.simple.name, 1 ], - ], - billingAddress: CustomerAddress = config.addresses.customer.billing + lineItems: Array< [ Product, number ] > = [ [ config.products.simple, 1 ] ], + billingAddress: CustomerAddress = config.addresses.customer.billing, + currency?: string ) { + await navigation.goToShop( page ); + const cartSizeText = await page .locator( '.cart-contents .count' ) .textContent(); let cartSize = Number( cartSizeText?.replace( /\D/g, '' ) ?? '0' ); for ( const line of lineItems ) { - let [ productTitle, qty ] = line; + let [ product, qty ] = line; while ( qty-- ) { - await addToCartFromShopPage( page, productTitle ); + await addToCartFromShopPage( page, product, currency ); + // Make sure the number of items in the cart is incremented before adding another item. await expect( page.locator( '.cart-contents .count' ) ).toHaveText( new RegExp( `${ ++cartSize } items?` ), @@ -344,6 +399,9 @@ export async function setupProductCheckout( timeout: 30000, } ); + + // Wait for the cart to update before adding another item. + await page.waitForTimeout( 500 ); } } @@ -375,12 +433,13 @@ export const expectFraudPreventionToken = async ( export const placeOrderWithOptions = async ( page: Page, options?: { - productId?: number; + product?: Product; billingAddress?: CustomerAddress; createAccount?: boolean; } ) => { - await addCartProduct( page, options?.productId ); + await navigation.goToShop( page ); + await addToCartFromShopPage( page, options?.product ); await setupCheckout( page, options?.billingAddress ); if ( options?.createAccount && @@ -414,7 +473,7 @@ export const placeOrderWithCurrency = async ( page: Page, currency: string ) => { - await navigation.goToShopWithCurrency( page, currency ); + await navigation.goToShop( page, { currency } ); return placeOrderWithOptions( page ); }; @@ -422,9 +481,12 @@ export const setSavePaymentMethod = async ( page: Page, save = true ) => { const checkbox = page.getByLabel( 'Save payment information to my account for future purchases.' ); - if ( save ) { + + const isChecked = await checkbox.isChecked(); + + if ( save && ! isChecked ) { await checkbox.check(); - } else { + } else if ( ! save && isChecked ) { await checkbox.uncheck(); } }; @@ -480,7 +542,7 @@ export const addSavedCard = async ( ) => { await page.getByRole( 'link', { name: 'Add payment method' } ).click(); await page.waitForLoadState( 'networkidle' ); - await page.getByText( 'Cards' ).click(); + await page.getByText( 'Cards', { exact: true } ).click(); const frameHandle = page.getByTitle( 'Secure payment input frame' ); const stripeFrame = frameHandle.contentFrame(); @@ -526,7 +588,7 @@ export const selectSavedCardOnCheckout = async ( ) .first(); await expect( option ).toBeVisible( { timeout: 100 } ); - option.click(); + await option.click(); }; export const setDefaultPaymentMethod = async ( @@ -538,5 +600,18 @@ export const setDefaultPaymentMethod = async ( const button = row.getByRole( 'link', { name: 'Make default' } ); await expect( button ).toBeVisible( { timeout: 100 } ); await expect( button ).toBeEnabled( { timeout: 100 } ); - button.click(); + await button.click(); +}; + +export const removeCoupon = async ( page: Page ) => { + const couponRemovalLink = page.getByRole( 'link', { + name: '[Remove]', + } ); + + if ( await couponRemovalLink.isVisible() ) { + await couponRemovalLink.click(); + await expect( + page.getByText( 'Coupon has been removed.' ) + ).toBeVisible(); + } }; diff --git a/tests/e2e/env/setup.sh b/tests/e2e/env/setup.sh index f3d5d12a2d0..24c89c3a04f 100755 --- a/tests/e2e/env/setup.sh +++ b/tests/e2e/env/setup.sh @@ -237,9 +237,10 @@ cli wp option set woocommerce_checkout_company_field "optional" echo "Importing WooCommerce shop pages..." cli wp wc --user=admin tool run install_pages +INSTALLED_WC_VERSION=$(cli_debug wp plugin get woocommerce --field=version) + # Start - Workaround for > WC 8.3 compatibility by updating cart & checkout pages to use shortcode. # To be removed when WooPayments L-2 support is >= WC 8.3 -INSTALLED_WC_VERSION=$(cli_debug wp plugin get woocommerce --field=version) IS_WORKAROUND_REQUIRED=$(cli_debug wp eval "echo version_compare(\"$INSTALLED_WC_VERSION\", \"8.3\", \">=\");") if [[ "$IS_WORKAROUND_REQUIRED" = "1" ]]; then @@ -364,8 +365,16 @@ cli wp db query "DELETE p, m FROM wp_posts p LEFT JOIN wp_postmeta m ON p.ID = m echo "Setting up a coupon for E2E tests" cli wp wc --user=admin shop_coupon create --code=free --amount=100 --discount_type=percent --individual_use=true --free_shipping=true -echo "Syncing HPOS data" -cli wp wc hpos sync +# HPOS was officially released in WooCommerce 8.2.0, so we need to check if we should sync COT or HPOS data. +IS_HPOS_AVAILABLE=$(cli_debug wp eval "echo version_compare(\"$INSTALLED_WC_VERSION\", \"8.2\", \">=\");") + +if [[ ${IS_HPOS_AVAILABLE} ]]; then + echo "Syncing HPOS data" + cli wp wc hpos sync +else + echo "Syncing COT data" + cli wp wc cot sync +fi # Log test configuration for visibility echo