Commit 2ff615d237e for woocommerce
commit 2ff615d237e880cfb0897fad6bf14f83729183cf
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Mon Sep 14 17:35:47 2026 +0300
[tests] Make the 2 PayPal Standard E2E tests self-cleaning and order-independent (#68645)
* test(e2e): Isolate PayPal Standard settings per test
The two titles in tests/e2e/tests/paypal/paypal.spec.ts share one
store, one `woocommerce_paypal_settings` row and one set of Transact
account caches. The fixture only flipped `_should_load` around each
test; the rest of the cleanup lived in the last step of the second
test's body.
Anything that failed before that step left the gateway enabled, the
onboarding flag set, the merchant and provider account options cached
and the gateway title overwritten with a test value. The next run read
that state as its starting point, so the two titles failed alternately.
Against a clean store, `onboards to Jetpack` fails: onboarding is
persisted while the settings POST is processed, after the form's fields
were built, so the response still carries the pre-onboarding field set.
Run it again and `can be enabled` fails instead, because the gateway is
already enabled and the `Enable` link it clicks is gone.
The fixture now owns all of that state. It sets `enabled`,
`transact_onboarding_complete` and `_should_load` before each test and
deletes the four `woocommerce_paypal_transact_*_account_*` options,
which cache an onboarded account for 24 hours
(`TransactAccountManager::TRANSACT_ACCOUNT_CACHE_EXPIRY`). Setup and
teardown both run under try/finally, and a failure on either side is
re-thrown rather than swallowed.
With the starting state guaranteed, the spec's
`if ( await enableLink.isVisible() )` guard becomes an assertion, the
title revert moves into a `finally` so it survives a failing
assertion, and the settings page is reloaded after the save.
No coverage moves between layers: two browser titles before, two after.
Carries the mega-branch commits:
- ec9603952b fix(e2e): reload PayPal settings after onboarding
- 80e7dea069 test(e2e): Make PayPal onboarding cleanup failure-safe
- 6a73e16e16 test(e2e): Reset PayPal onboarding fixture state
- 94b93f3ead test(e2e): Clear PayPal onboarding account caches
- 7de610117d fix(e2e): isolate PayPal settings per test
Refs TESTOPS-288
Refs #68046
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(e2e): Link the PayPal post-save reload to the gap it works around
The reload after the save is not a wait. The save completes Transact
onboarding, onboarding unlocks `Enable PayPal Buttons`, and the field is
missing from the page the save returns, so the assertion has to run
against the next load — which is the one a merchant reaches too.
Point the comment at the issue rather than leaving the reason in the PR
description.
Refs TESTOPS-288
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/testops-288-paypal-standard b/plugins/woocommerce/changelog/testops-288-paypal-standard
new file mode 100644
index 00000000000..16e591c7222
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-288-paypal-standard
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Isolate the PayPal Standard E2E fixture's settings per test and reload the settings page after onboarding, so both retained browser titles pass from any starting state; no coverage moves between layers.
+
diff --git a/plugins/woocommerce/tests/e2e/fixtures/paypal-fixtures.ts b/plugins/woocommerce/tests/e2e/fixtures/paypal-fixtures.ts
index 1d3b2ac13ee..b49bcc95710 100644
--- a/plugins/woocommerce/tests/e2e/fixtures/paypal-fixtures.ts
+++ b/plugins/woocommerce/tests/e2e/fixtures/paypal-fixtures.ts
@@ -5,17 +5,65 @@ import { test as baseTest } from './fixtures';
import { ADMIN_STATE_PATH } from '../playwright.config';
import { wpCLI } from '../utils/cli';
+const updatePayPalFixtureSettings = async ( shouldLoad: 'yes' | 'no' ) => {
+ await wpCLI(
+ `wp eval '$settings = get_option( "woocommerce_paypal_settings", array() ); if ( ! is_array( $settings ) ) { throw new RuntimeException( "woocommerce_paypal_settings must be an array." ); } $settings["enabled"] = "no"; $settings["transact_onboarding_complete"] = "no"; $settings["_should_load"] = "${ shouldLoad }"; update_option( "woocommerce_paypal_settings", $settings );'`
+ );
+};
+
+const deletePayPalAccountOptions = async () => {
+ await wpCLI(
+ `wp eval 'delete_option( "woocommerce_paypal_transact_merchant_account_live" ); delete_option( "woocommerce_paypal_transact_merchant_account_test" ); delete_option( "woocommerce_paypal_transact_provider_account_live" ); delete_option( "woocommerce_paypal_transact_provider_account_test" );'`
+ );
+};
+
export const test = baseTest.extend( {
- page: async ( { page }, use ) => {
- await wpCLI(
- "wp option patch update woocommerce_paypal_settings _should_load 'yes'"
- );
+ page: async ( { page }, providePage ) => {
+ let setupFailed = false;
+ let setupError: unknown;
+ const cleanupErrors: unknown[] = [];
+
+ try {
+ try {
+ await updatePayPalFixtureSettings( 'yes' );
+ await deletePayPalAccountOptions();
+ } catch ( error ) {
+ setupFailed = true;
+ setupError = error;
+ }
+
+ if ( ! setupFailed ) {
+ await providePage( page );
+ }
+ } finally {
+ try {
+ await updatePayPalFixtureSettings( 'no' );
+ } catch ( error ) {
+ cleanupErrors.push( error );
+ }
- await use( page );
+ try {
+ await deletePayPalAccountOptions();
+ } catch ( error ) {
+ cleanupErrors.push( error );
+ }
+ }
- await wpCLI(
- "wp option patch update woocommerce_paypal_settings _should_load 'no'"
- );
+ if ( setupFailed && cleanupErrors.length ) {
+ throw new AggregateError(
+ [ setupError, ...cleanupErrors ],
+ 'PayPal fixture setup and cleanup failed.'
+ );
+ }
+ if ( setupFailed ) {
+ throw setupError;
+ }
+ if ( cleanupErrors.length ) {
+ throw new AggregateError(
+ cleanupErrors,
+ 'PayPal fixture cleanup failed.'
+ );
+ }
},
storageState: ADMIN_STATE_PATH,
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/paypal/paypal.spec.ts b/plugins/woocommerce/tests/e2e/tests/paypal/paypal.spec.ts
index a83c6f68a87..4a403e3dae8 100644
--- a/plugins/woocommerce/tests/e2e/tests/paypal/paypal.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/paypal/paypal.spec.ts
@@ -130,15 +130,13 @@ test.describe(
name: 'Enable',
} );
- // eslint-disable-next-line playwright/no-conditional-in-test
- if ( await enableLink.isVisible() ) {
- await enableLink.click();
- await expect(
- paypalDiv
- .getByText( 'Active' )
- .or( paypalDiv.getByText( 'Test account' ) )
- ).toBeVisible( visibilityOptions );
- }
+ await expect( enableLink ).toBeVisible( visibilityOptions );
+ await enableLink.click();
+ await expect(
+ paypalDiv
+ .getByText( 'Active' )
+ .or( paypalDiv.getByText( 'Test account' ) )
+ ).toBeVisible( visibilityOptions );
await paypalDiv
.getByRole( 'button', {
@@ -174,58 +172,60 @@ test.describe(
.locator( '#woocommerce_paypal_title' )
.inputValue();
- await test.step( 'Update the title field', async () => {
- await page
- .locator( '#woocommerce_paypal_title' )
- .fill( 'PayPal Custom Title ' + Date.now() );
-
- // TODO: Temporarily removing the disabled attribute from the Save changes button.
- await enableSaveButton( page );
-
- await page
- .getByRole( 'button', {
- name: 'Save changes',
- } )
- .click();
-
- await expect(
- page.locator( 'div.updated.inline' )
- ).toContainText( 'Your settings have been saved.' );
- } );
-
- await test.step( 'Check the setting present only when Jetpack onboarding is complete', async () => {
- const paypalButtonsSetting = page.getByText(
- 'Enable PayPal Buttons',
- { exact: true }
- );
- await expect( paypalButtonsSetting ).toBeVisible();
- } );
+ try {
+ await test.step( 'Update the title field', async () => {
+ await page
+ .locator( '#woocommerce_paypal_title' )
+ .fill( 'PayPal Custom Title ' + Date.now() );
+
+ // TODO: Temporarily removing the disabled attribute from the Save changes button.
+ await enableSaveButton( page );
+
+ await page
+ .getByRole( 'button', {
+ name: 'Save changes',
+ } )
+ .click();
+
+ await expect(
+ page.locator( 'div.updated.inline' )
+ ).toContainText( 'Your settings have been saved.' );
+
+ // The save is what completes Transact onboarding, and onboarding is
+ // what unlocks `Enable PayPal Buttons` — but the field is missing
+ // from the page the save returns, so assert against the next load
+ // instead, which is the one a merchant actually reaches. Not a wait:
+ // see https://github.com/woocommerce/woocommerce/issues/68689.
+ await page.reload();
+ } );
- // Clean up by reverting the title change and disabling PayPal Standard.
- await test.step( 'Revert title change and disable PayPal Standard', async () => {
- await page
- .locator( '#woocommerce_paypal_title' )
- .fill( originalPayPalTitle );
-
- await page
- .getByRole( 'checkbox', {
- name: 'Enable PayPal Standard',
- } )
- .uncheck();
-
- // TODO: Temporarily removing the disabled attribute from the Save changes button.
- await enableSaveButton( page );
-
- await page
- .getByRole( 'button', {
- name: 'Save changes',
- } )
- .click();
-
- await expect(
- page.locator( 'div.updated.inline' )
- ).toContainText( 'Your settings have been saved.' );
- } );
+ await test.step( 'Check the setting present only when Jetpack onboarding is complete', async () => {
+ const paypalButtonsSetting = page.getByText(
+ 'Enable PayPal Buttons',
+ { exact: true }
+ );
+ await expect( paypalButtonsSetting ).toBeVisible();
+ } );
+ } finally {
+ await test.step( 'Revert title change', async () => {
+ await page
+ .locator( '#woocommerce_paypal_title' )
+ .fill( originalPayPalTitle );
+
+ // TODO: Temporarily removing the disabled attribute from the Save changes button.
+ await enableSaveButton( page );
+
+ await page
+ .getByRole( 'button', {
+ name: 'Save changes',
+ } )
+ .click();
+
+ await expect(
+ page.locator( 'div.updated.inline' )
+ ).toContainText( 'Your settings have been saved.' );
+ } );
+ }
} );
}
);