Skip to content

Commit a40a983

Browse files
committed
Merge branch 'dev/woopmnt-5249-e2e-ensure-version-coverage-for-woocommerce-and-php' into dev/woopmnt-5251-e2e-investigate-and-re-enable-woocommerce-blocks-e2e-tests
2 parents c40d544 + 135e506 commit a40a983

File tree

12 files changed

+153
-102
lines changed

12 files changed

+153
-102
lines changed

.github/actions/e2e/run-log-tests/action.yml

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,14 @@ description: 'Runs E2E tests with retry & upload logs and screenshots'
44
runs:
55
using: "composite"
66
steps:
7-
- name: First Run E2E Tests
8-
id: first_run_e2e_tests
9-
# Use +e to trap errors when running E2E tests.
10-
shell: /bin/bash +e {0}
11-
run: |
12-
npm run test:e2e-ci
13-
14-
if [[ -f "$E2E_RESULT_FILEPATH" ]]; then
15-
E2E_NUM_FAILED_TEST_SUITES=$(cat "$E2E_RESULT_FILEPATH" | jq '.stats["unexpected"]')
16-
echo "FIRST_RUN_FAILED_TEST_SUITES=$(echo $E2E_NUM_FAILED_TEST_SUITES)" >> $GITHUB_OUTPUT
17-
if [[ ${E2E_NUM_FAILED_TEST_SUITES} -gt 0 ]]; then
18-
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."
19-
exit 0
20-
fi
21-
else
22-
echo "FIRST_RUN_FAILED_TEST_SUITES=0" >> $GITHUB_OUTPUT
23-
exit 0
24-
fi
25-
26-
# Retry failed E2E tests
27-
- name: Re-try Failed E2E Files
28-
if: ${{ steps.first_run_e2e_tests.outputs.FIRST_RUN_FAILED_TEST_SUITES > 0 }}
7+
- name: Run E2E Tests
298
shell: bash
30-
# Filter failed E2E files from the result JSON file, and re-run them.
319
run: |
32-
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 | .[]')
10+
set -e
11+
npm run test:e2e-ci || {
12+
echo "::notice::Some tests failed, retrying only failed tests with --last-failed flag"
13+
npx playwright test --config=tests/e2e/playwright.config.ts --grep-invert @todo --last-failed
14+
}
3315
3416
# Archive screenshots if any
3517
- name: Archive e2e test screenshots & logs

.github/scripts/generate-wc-matrix.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,30 @@ else
105105
VERSIONS+=("rc") # Fallback to string if no RC found
106106
fi
107107

108+
# Validate versions before output
109+
if [[ -z "$L1_VERSION" || "$L1_VERSION" == "null" ]]; then
110+
echo "Error: Could not extract L-1 version" >&2
111+
exit 1
112+
fi
113+
114+
if [[ ! "$L1_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
115+
echo "Error: Invalid L-1 version: $L1_VERSION" >&2
116+
exit 1
117+
fi
118+
119+
if [[ -z "$LATEST_RC_VERSION" || "$LATEST_RC_VERSION" == "null" ]]; then
120+
echo "Error: Could not extract RC version" >&2
121+
exit 1
122+
fi
123+
124+
# Only validate beta if it's available
125+
if [[ -n "$LATEST_BETA_VERSION" && "$LATEST_BETA_VERSION" != "null" ]]; then
126+
if [[ ! "$LATEST_BETA_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
127+
echo "Error: Invalid beta version: $LATEST_BETA_VERSION" >&2
128+
exit 1
129+
fi
130+
fi
131+
108132
# Convert to JSON array and output only the JSON (no extra whitespace or newlines)
109133
# Output a single JSON object with both versions and metadata
110134
RESULT=$(jq -n \

.github/workflows/e2e-pull-request.yml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,8 @@ jobs:
5858
SCRIPT_RESULT=$( .github/scripts/generate-wc-matrix.sh )
5959
6060
# Extract versions and metadata from JSON
61-
WC_VERSIONS=$(echo "$SCRIPT_RESULT" | jq -r '.versions')
6261
L1_VERSION=$(echo "$SCRIPT_RESULT" | jq -r '.metadata.l1_version')
6362
64-
# Validate that we got proper versions
65-
if [[ -z "$L1_VERSION" || "$L1_VERSION" == "null" ]]; then
66-
echo "Error: Could not extract L-1 version from script output" >&2
67-
echo "Script result: $SCRIPT_RESULT" >&2
68-
exit 1
69-
fi
70-
71-
if [[ ! "$L1_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
72-
echo "Error: Invalid L-1 version extracted: $L1_VERSION" >&2
73-
echo "WC_VERSIONS: $WC_VERSIONS" >&2
74-
exit 1
75-
fi
76-
7763
echo "Using L-1 version: $L1_VERSION" >&2
7864
7965
# Create PR matrix with L-1 and latest versions

.github/workflows/e2e-test.yml

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,49 +44,17 @@ jobs:
4444
SCRIPT_RESULT=$( .github/scripts/generate-wc-matrix.sh )
4545
4646
# Extract versions and metadata from JSON
47-
WC_VERSIONS=$(echo "$SCRIPT_RESULT" | jq -r '.versions')
4847
L1_VERSION=$(echo "$SCRIPT_RESULT" | jq -r '.metadata.l1_version')
4948
RC_VERSION=$(echo "$SCRIPT_RESULT" | jq -r '.metadata.rc_version')
5049
BETA_VERSION=$(echo "$SCRIPT_RESULT" | jq -r '.metadata.beta_version')
5150
52-
# Check if beta version is actually a version (not null or empty)
53-
if [[ "$BETA_VERSION" == "null" || -z "$BETA_VERSION" ]]; then
51+
if [[ "$BETA_VERSION" == "null" ]]; then
5452
BETA_VERSION=""
55-
echo "No beta version available" >&2
56-
fi
57-
58-
# Validate that we got proper versions
59-
if [[ -z "$L1_VERSION" || "$L1_VERSION" == "null" ]]; then
60-
echo "Error: Could not extract L-1 version from script output" >&2
61-
echo "Script result: $SCRIPT_RESULT" >&2
62-
exit 1
63-
fi
64-
65-
if [[ ! "$L1_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
66-
echo "Error: Invalid L-1 version extracted: $L1_VERSION" >&2
67-
echo "WC_VERSIONS: $WC_VERSIONS" >&2
68-
exit 1
69-
fi
70-
71-
# Validate RC version
72-
if [[ -z "$RC_VERSION" || "$RC_VERSION" == "null" ]]; then
73-
echo "Error: Could not extract RC version from script output" >&2
74-
echo "WC_VERSIONS: $WC_VERSIONS" >&2
75-
exit 1
76-
fi
77-
78-
# Only validate beta if it's available
79-
if [[ -n "$BETA_VERSION" && "$BETA_VERSION" != "null" ]]; then
80-
if [[ ! "$BETA_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
81-
echo "Error: Invalid beta version extracted: $BETA_VERSION" >&2
82-
echo "WC_VERSIONS: $WC_VERSIONS" >&2
83-
exit 1
84-
fi
8553
fi
8654
8755
echo "Using L-1 version: $L1_VERSION" >&2
8856
echo "Using RC version: $RC_VERSION" >&2
89-
if [[ -n "$BETA_VERSION" && "$BETA_VERSION" != "null" ]]; then
57+
if [[ -n "$BETA_VERSION" ]]; then
9058
echo "Using beta version: $BETA_VERSION" >&2
9159
else
9260
echo "No beta version available" >&2

.github/workflows/release-code-freeze.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ jobs:
8686
RELEASE_VERSION: ${{ needs.check-code-freeze.outputs.nextReleaseVersion }}
8787
RELEASE_DATE: ${{ needs.check-code-freeze.outputs.nextReleaseDate }}
8888
RELEASE_PR_ID: ${{ needs.create-release-pr.outputs.release-pr-id }}
89+
GITHUB_RUN_ID: ${{ github.run_id }}
8990
steps:
9091
- name: "Slack ping"
9192
uses: slackapi/[email protected]
@@ -116,7 +117,7 @@ jobs:
116117
"type": "section",
117118
"text": {
118119
"type": "mrkdwn",
119-
"text": "• Created a release branch `release/${{ env.RELEASE_VERSION }}` \n • Raised a <https://github.com/Automattic/woocommerce-payments/pull/${{ env.RELEASE_PR_ID }}|Pull Request> to `trunk`\n • Built a <https://github.com/Automattic/woocommerce-payments/actions/runs/$GITHUB_RUN_ID|zip file and ran smoke tests> against it"
120+
"text": "• Created a release branch `release/${{ env.RELEASE_VERSION }}` \n • Raised a <https://github.com/Automattic/woocommerce-payments/pull/${{ env.RELEASE_PR_ID }}|Pull Request> to `trunk`\n • Built a <https://github.com/Automattic/woocommerce-payments/actions/runs/${{ env.GITHUB_RUN_ID }}|zip file and ran smoke tests> against it"
120121
}
121122
},
122123
{
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Significance: patch
2+
Type: dev
3+
Comment: Fix GITHUB_RUN_ID env variable
4+
5+

tests/e2e/README.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ WooPayments e2e tests can be found in the `./tests/e2e/specs` directory. These t
44

55
E2E tests can be run locally or in GitHub Actions. Github Actions are already configured and don't require any changes to run the tests.
66

7+
## Recent Improvements
8+
9+
### Retry Mechanism
10+
The E2E tests now include an intelligent retry mechanism that:
11+
- **First run**: Executes all tests normally
12+
- **Automatic retry**: If any tests fail, only the failed tests are retried using Playwright's `--last-failed` flag
13+
14+
### Improved Timeout Handling
15+
- **Increased timeouts**: UI interaction timeouts increased from 100ms to 10 seconds for better reliability
16+
- **Better error handling**: More robust page loading and element waiting strategies
17+
- **DevTools reliability**: Improved devtools page navigation and interaction
18+
19+
### Dynamic Matrix Generation
20+
- **L-1 Policy**: Tests automatically run against the latest WooCommerce version and the L-1 (previous major) version
21+
- **Dynamic version resolution**: Automatically fetches latest WC, RC, and beta versions from WordPress.org API
22+
- **Optimized PHP strategy**: Reduces job count while maintaining comprehensive coverage
23+
- **Business continuity**: Maintains support for WC 7.7.0 for significant TPV reasons
24+
725
## Setting up & running E2E tests
826

927
For running E2E tests locally, create a new file named `local.env` under `tests/e2e/config` folder with the following env variables (replace values as required).
@@ -207,6 +225,19 @@ Currently, the best way to debug tests is to use the Playwright UI mode. This mo
207225
You can use the locator functionality to help correctly determine the locator syntax to correctly target the HTML element you need. Lastly, you can also use
208226
`console.log()` to assist with debugging tests in UI mode. To run tests in UI mode, use the `npm run test:e2e-ui path/to/test.spec` command.
209227

228+
### Understanding Test Failures and Retries
229+
230+
When tests fail in CI, the retry mechanism automatically kicks in:
231+
232+
1. **First run**: All tests execute normally
233+
2. **If failures occur**: The system automatically retries only the failed tests
234+
3. **Retry logs**: Look for the message "Some tests failed, retrying only failed tests with --last-failed flag" in the logs
235+
4. **Final results**: The test run will show both the initial results and retry results
236+
237+
This approach helps distinguish between:
238+
- **Flaky tests**: Tests that fail occasionally but pass on retry
239+
- **Consistent failures**: Tests that fail both initially and on retry (indicating real issues)
240+
210241
## Slack integration
211242

212243
The Slack reporter is a custom reporter that sends e2e test failures to a public Slack channel (search Slack channel ID `CQ0Q6N62D`). The reporter is configured to only send the first failure of a test to Slack. If the retry also fails it will not be sent to prevent spamming the channel.
@@ -254,6 +285,16 @@ await page.getByRole( 'button', { name: /submit/i } ).click();
254285
255286
In some cases, you may need to wait for the page to reach a certain load state before interacting with it. You can use `await page.waitForLoadState( 'domcontentloaded' );` to wait for the page to finish loading.
256287

288+
**What timeout values are used for UI interactions?**
289+
290+
The E2E tests use optimized timeout values for better reliability:
291+
- **Global expect timeout**: 20 seconds (configured in `playwright.config.ts`)
292+
- **UI interaction timeouts**: 10 seconds for critical UI elements (buttons, forms, etc.)
293+
- **Page load timeouts**: 120 seconds for test execution
294+
- **Network idle waits**: Used for dynamic content loading
295+
296+
These timeouts have been increased from the previous 100ms values to provide better stability, especially for slower environments or complex UI interactions.
297+
257298
**What is the best way to target elements in the page?**
258299

259300
Prefer the use of [user-facing attribute or test-id locators](https://playwright.dev/docs/locators#locating-elements) to target elements in the page. This will make the tests more resilient to changes to implementation details, such as class names.
@@ -314,15 +355,29 @@ test.describe( 'Sign in as customer', () => {
314355
} );
315356
```
316357

358+
**How does the dynamic matrix generation work?**
359+
360+
The E2E test matrix is dynamically generated using the `.github/scripts/generate-wc-matrix.sh` script:
361+
362+
- **L-1 Policy**: Automatically tests against the latest WooCommerce version and the L-1 (previous major) version
363+
- **Version Resolution**: Fetches latest WC, RC, and beta versions from WordPress.org API
364+
- **PHP Strategy**:
365+
- WC 7.7.0: PHP 7.3 (legacy support)
366+
- WC L-1 & Latest: PHP 8.3 (stable)
367+
- WC RC: PHP 8.4 (latest)
368+
- **Business Continuity**: Maintains WC 7.7.0 support for significant TPV reasons
369+
370+
This ensures comprehensive test coverage while optimizing CI execution time and resource usage.
371+
317372
**How can I investigate and interact with a test failures?**
318373

319374
- **Github Action test runs**
320375
- View GitHub checks in the "Checks" tab of a PR
321-
- There are currently four E2E test workflows:
322-
- E2E Tests - Pull Request / WC - latest | wcpay - merchant (pull_request)
323-
- E2E Tests - Pull Request / WC - latest | wcpay - shopper (pull_request)
324-
- E2E Tests - Pull Request / WC - latest | subscriptions - merchant (pull_request)
325-
- E2E Tests - Pull Request / WC - latest | subscriptions - shopper (pull_request)
376+
- The E2E test matrix now dynamically generates test combinations based on:
377+
- **WooCommerce versions**: Latest, L-1 (previous major), RC, and beta versions
378+
- **PHP versions**: 7.3 (legacy), 8.3 (stable), 8.4 (latest)
379+
- **Test groups**: wcpay, subscriptions
380+
- **Test branches**: merchant, shopper
326381
- Click on the details link to the right of the failed job to see the summary
327382
- In the job summary, click on the "Run tests, upload screenshots & logs" section.
328383
- Click on the artifact download link at the end of the section, then extract and copy the `playwright-report` directory to the root of the WooPayments repository

tests/e2e/playwright.config.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ export default defineConfig( {
5151
fullyParallel: false,
5252
/* Fail the build on CI if you accidentally left test.only in the source code. */
5353
forbidOnly: !! process.env.CI,
54-
/* Retry on CI only */
55-
retries: process.env.CI ? 2 : 0,
5654
/* Opt out of parallel tests. */
5755
workers: 1,
5856
/* Reporters to use. See https://playwright.dev/docs/test-reporters */

tests/e2e/specs/wcpay/shopper/shopper-myaccount-saved-cards.spec.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,6 @@ test.describe( 'Shopper can save and delete cards', () => {
235235
// Take note of the time when we added this card
236236
cardTimingHelper.markCardAdded();
237237

238-
await expect(
239-
shopperPage.getByText(
240-
`${ card2.expires.month }/${ card2.expires.year }`
241-
)
242-
).toBeVisible();
243238
await setDefaultPaymentMethod( shopperPage, card2 );
244239
// Verify that the card was set as default
245240
await expect(
@@ -255,6 +250,15 @@ test.describe( 'Shopper can save and delete cards', () => {
255250
{ tag: '@critical' },
256251
async () => {
257252
await goToMyAccount( shopperPage, 'payment-methods' );
253+
254+
// Verify both cards are visible before trying to delete them
255+
await expect(
256+
shopperPage.getByText( card.label )
257+
).toBeVisible( { timeout: 10000 } );
258+
await expect(
259+
shopperPage.getByText( card2.label )
260+
).toBeVisible( { timeout: 10000 } );
261+
258262
await deleteSavedCard( shopperPage, card );
259263
await expect(
260264
shopperPage.getByText( 'Payment method deleted.' )

tests/e2e/utils/devtools.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,29 @@
33
*/
44
import { Page, expect } from '@playwright/test';
55

6-
const goToDevToolsSettings = ( page: Page ) =>
7-
page.goto( '/wp-admin/admin.php?page=wcpaydev', {
8-
waitUntil: 'load',
6+
const goToDevToolsSettings = async ( page: Page ) => {
7+
await page.goto( '/wp-admin/admin.php?page=wcpaydev', {
8+
waitUntil: 'domcontentloaded',
99
} );
1010

11+
// Wait for the page to be fully loaded
12+
await page.waitForLoadState( 'networkidle' );
13+
};
14+
1115
const saveDevToolsSettings = async ( page: Page ) => {
12-
await page.getByRole( 'button', { name: 'Save Changes' } ).click();
16+
// Wait for the page to be fully loaded before trying to interact
17+
await page.waitForLoadState( 'domcontentloaded' );
18+
19+
// Wait for the Save Changes button to be available
20+
const saveButton = page.getByRole( 'button', { name: 'Save Changes' } );
21+
await expect( saveButton ).toBeVisible( { timeout: 10000 } );
22+
await expect( saveButton ).toBeEnabled( { timeout: 10000 } );
23+
24+
await saveButton.click();
1325
await page.waitForLoadState( 'networkidle' );
14-
await expect( page.getByText( /Settings saved/ ) ).toBeVisible();
26+
await expect( page.getByText( /Settings saved/ ) ).toBeVisible( {
27+
timeout: 10000,
28+
} );
1529
};
1630

1731
const getIsCardTestingProtectionEnabled = ( page: Page ) =>

0 commit comments

Comments
 (0)