Skip to content

Commit 28f6dbc

Browse files
committed
feat: add E2E testing setup with Playwright and wp-env
- Configured Playwright for E2E testing with a custom `playwright.config.ts` tailored for `wp-env`. - Added E2E tests covering PHP-queued and JS-created toasts, toast stacking, dismiss functionality, auto-dismiss, hover pause/resume, and variant rendering. - Introduced a helper MU-plugin to queue test toasts via `wp_tosuto()` when specific query parameters are present. - Updated `package.json` and `pnpm-lock.yaml` to include required Playwright and WordPress testing dependencies. - Enhanced `.wp-env.json` with mappings and ports for `wp-env` testing integration.
1 parent f9039c3 commit 28f6dbc

9 files changed

Lines changed: 349 additions & 12 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: E2E Tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
e2e:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- uses: pnpm/action-setup@v4
16+
17+
- uses: actions/setup-node@v4
18+
with:
19+
node-version: '24'
20+
cache: 'pnpm'
21+
22+
- run: pnpm install --frozen-lockfile
23+
24+
- run: npx playwright install chromium
25+
26+
- uses: shivammathur/setup-php@v2
27+
with:
28+
php-version: '8.4'
29+
30+
- run: composer install --no-interaction --prefer-dist
31+
32+
- run: pnpm build
33+
34+
- name: Start wp-env
35+
run: pnpm wp-env start
36+
37+
- name: Run E2E tests
38+
run: pnpm test:e2e
39+
40+
- uses: actions/upload-artifact@v4
41+
if: failure()
42+
with:
43+
name: playwright-artifacts
44+
path: artifacts/
45+
retention-days: 7

.wp-env.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
"plugins": [
88
".",
99
"https://downloads.wordpress.org/plugin/query-monitor.3.16.4.zip"
10-
],
10+
],
11+
"mappings": {
12+
"wp-content/mu-plugins": "./tests/e2e/mu-plugins"
13+
},
14+
"port": 8877,
15+
"testsPort": 8878,
1116
"config": {
1217
"WP_DEBUG": true,
1318
"WP_ENVIRONMENT_TYPE": "local",

openspec/changes/wp-interactivity-tosuto/tasks.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@
5353

5454
## 7. E2E Tests (Playwright + wp-env)
5555

56-
- [ ] 7.1 Add `@wordpress/e2e-test-utils-playwright` and `@playwright/test` to devDependencies, create Playwright config pointing at wp-env
57-
- [ ] 7.2 Create a mu-plugin that queues test toasts via `wp_tosuto()` when a specific query param is present (e.g. `?tosuto-test=php`)
58-
- [ ] 7.3 E2E: PHP-queued toast is visible after page load with correct content and variant class
59-
- [ ] 7.4 E2E: JS `store('wp-tosuto').actions.add()` creates toast in DOM with correct content and variant class
60-
- [ ] 7.5 E2E: multiple toasts stack vertically
61-
- [ ] 7.6 E2E: dismiss button removes only the clicked toast
62-
- [ ] 7.7 E2E: toast auto-dismisses after duration
63-
- [ ] 7.8 E2E: hover pauses auto-dismiss, mouseleave resumes it
64-
- [ ] 7.9 E2E: variant classes (default, success, error, warning, info) render correctly
56+
- [x] 7.1 Add `@wordpress/e2e-test-utils-playwright` and `@playwright/test` to devDependencies, create Playwright config pointing at wp-env
57+
- [x] 7.2 Create a mu-plugin that queues test toasts via `wp_tosuto()` when a specific query param is present (e.g. `?tosuto-test=php`)
58+
- [x] 7.3 E2E: PHP-queued toast is visible after page load with correct content and variant class
59+
- [x] 7.4 E2E: JS `store('wp-tosuto').actions.add()` creates toast in DOM with correct content and variant class
60+
- [x] 7.5 E2E: multiple toasts stack vertically
61+
- [x] 7.6 E2E: dismiss button removes only the clicked toast
62+
- [x] 7.7 E2E: toast auto-dismisses after duration
63+
- [x] 7.8 E2E: hover pauses auto-dismiss, mouseleave resumes it
64+
- [x] 7.9 E2E: variant classes (default, success, error, warning, info) render correctly

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
"react": "^19.0.0"
2626
},
2727
"devDependencies": {
28+
"@playwright/test": "^1.59.1",
2829
"@types/node": "^24.0.0",
2930
"@types/react": "^19.0.0",
3031
"@types/react-dom": "^19.0.0",
3132
"@types/webpack": "^5.28.5",
33+
"@wordpress/e2e-test-utils-playwright": "^1.44.0",
3234
"@wordpress/env": "^10.39.0",
3335
"@wordpress/prettier-config": "^4.39.0",
3436
"@wordpress/scripts": "^31.4.0",
@@ -39,14 +41,16 @@
3941
"typescript": "~5.9.0"
4042
},
4143
"scripts": {
42-
"env": "wp-env",
44+
"wp-env": "wp-env",
4345
"start": "WP_SOURCE_PATH=sources/client WP_EXPERIMENTAL_MODULES=true WP_BLOCKS_MANIFEST=true wp-scripts start",
4446
"build": "WP_SOURCE_PATH=sources/client WP_EXPERIMENTAL_MODULES=true WP_BLOCKS_MANIFEST=true wp-scripts build",
4547
"build:dev": "WP_SOURCE_PATH=sources/client WP_EXPERIMENTAL_MODULES=true WP_BLOCKS_MANIFEST=true wp-scripts start",
4648
"lint:css": "wp-scripts lint-style",
4749
"lint:css:fix": "wp-scripts lint-style --fix",
4850
"lint:js": "wp-scripts lint-js",
4951
"lint:js:fix": "wp-scripts lint-js --fix",
50-
"format": "wp-scripts format"
52+
"format": "wp-scripts format",
53+
"test:e2e": "playwright test",
54+
"test:e2e:ui": "playwright test --ui"
5155
}
5256
}

playwright.config.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
import path from 'node:path';
3+
4+
process.env.WP_ARTIFACTS_PATH ??= path.join( process.cwd(), 'artifacts' );
5+
6+
const baseUrl = process.env.WP_BASE_URL || 'http://localhost:8877';
7+
8+
export default defineConfig( {
9+
reporter: process.env.CI ? [ [ 'github' ] ] : [ [ 'list' ] ],
10+
forbidOnly: !! process.env.CI,
11+
workers: 1,
12+
retries: process.env.CI ? 2 : 0,
13+
timeout: 100_000,
14+
reportSlowTests: null,
15+
testDir: './tests/e2e/specs',
16+
outputDir: path.join( process.env.WP_ARTIFACTS_PATH, 'test-results' ),
17+
use: {
18+
baseURL: baseUrl,
19+
headless: true,
20+
viewport: { width: 960, height: 700 },
21+
ignoreHTTPSErrors: true,
22+
locale: 'en-US',
23+
contextOptions: {
24+
reducedMotion: 'reduce',
25+
strictSelectors: true,
26+
},
27+
actionTimeout: 10_000,
28+
trace: 'retain-on-failure',
29+
screenshot: 'only-on-failure',
30+
video: 'on-first-retry',
31+
},
32+
webServer: {
33+
command: 'pnpm wp-env start',
34+
url: baseUrl,
35+
timeout: 120_000,
36+
reuseExistingServer: true,
37+
},
38+
projects: [
39+
{
40+
name: 'chromium',
41+
use: { ...devices[ 'Desktop Chrome' ] },
42+
},
43+
],
44+
} );

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
/**
3+
* MU-Plugin: Tosuto E2E Test Helper
4+
*
5+
* Queues test toasts via wp_tosuto() when specific query params are present.
6+
* Only active in wp-env testing environments.
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
add_action('template_redirect', static function (): void {
12+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
13+
if (!isset($_GET['tosuto-test'])) {
14+
return;
15+
}
16+
17+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
18+
$scenario = sanitize_text_field(wp_unslash($_GET['tosuto-test']));
19+
20+
switch ($scenario) {
21+
case 'js':
22+
add_action('wp_footer', static function (): void {
23+
?>
24+
<script type="module">
25+
import { store } from '@wordpress/interactivity';
26+
const { actions } = store('wp-tosuto');
27+
actions.add('JS Toast Content', { variant: 'success' });
28+
</script>
29+
<?php
30+
});
31+
break;
32+
33+
case 'php':
34+
wp_tosuto('<strong>PHP Toast</strong> — queued server-side', [
35+
'variant' => 'default',
36+
'duration' => 5000,
37+
]);
38+
break;
39+
40+
case 'php-success':
41+
wp_tosuto('Success toast from PHP', [
42+
'variant' => 'success',
43+
'duration' => 5000,
44+
]);
45+
break;
46+
47+
case 'php-error':
48+
wp_tosuto('Error toast from PHP', [
49+
'variant' => 'error',
50+
'duration' => 5000,
51+
]);
52+
break;
53+
54+
case 'php-warning':
55+
wp_tosuto('Warning toast from PHP', [
56+
'variant' => 'warning',
57+
'duration' => 5000,
58+
]);
59+
break;
60+
61+
case 'php-info':
62+
wp_tosuto('Info toast from PHP', [
63+
'variant' => 'info',
64+
'duration' => 5000,
65+
]);
66+
break;
67+
68+
case 'php-multiple':
69+
wp_tosuto('First toast', ['variant' => 'default', 'duration' => 10000]);
70+
wp_tosuto('Second toast', ['variant' => 'success', 'duration' => 10000]);
71+
wp_tosuto('Third toast', ['variant' => 'error', 'duration' => 10000]);
72+
break;
73+
74+
case 'php-no-dismiss':
75+
wp_tosuto('Cannot dismiss this', [
76+
'variant' => 'default',
77+
'duration' => 0,
78+
'dismissable' => false,
79+
]);
80+
break;
81+
82+
case 'php-short-duration':
83+
wp_tosuto('Gone in 1 second', [
84+
'variant' => 'default',
85+
'duration' => 1000,
86+
]);
87+
break;
88+
89+
case 'php-all-variants':
90+
wp_tosuto('Default variant', ['variant' => 'default', 'duration' => 0]);
91+
wp_tosuto('Success variant', ['variant' => 'success', 'duration' => 0]);
92+
wp_tosuto('Error variant', ['variant' => 'error', 'duration' => 0]);
93+
wp_tosuto('Warning variant', ['variant' => 'warning', 'duration' => 0]);
94+
wp_tosuto('Info variant', ['variant' => 'info', 'duration' => 0]);
95+
break;
96+
}
97+
});

0 commit comments

Comments
 (0)