Skip to content

Commit f56da59

Browse files
KevinBatdorfclaude
andcommitted
Add testing guidelines as Claude rules, fix .gitignore
- Add .claude/rules/testing.md with Playwright + WP Playground guidelines (path-scoped to tests/). Auto-loads when working on tests. - Update .gitignore to selectively track .claude/rules/ and settings.json while ignoring user-specific files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6edf706 commit f56da59

3 files changed

Lines changed: 57 additions & 8 deletions

File tree

.claude/rules/testing.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
path: tests/**
3+
---
4+
5+
# Playwright + WP Playground Testing Guidelines
6+
7+
## Architecture
8+
9+
- Each test subdirectory has its own `blueprint.json` + `setup.php` and gets a fresh WP Playground instance.
10+
- `playwright.config.ts` auto-discovers specs by finding `*.spec.ts` files with a `blueprint.json` in the same directory.
11+
- Each spec = one Playwright project = one CI matrix job = one WP Playground instance.
12+
13+
## Editor Canvas vs Page
14+
15+
- Use `editor.canvas` (or `page.locator('iframe[name="editor-canvas"]').contentFrame()`) for anything inside the block editor — blocks, text, styles. WordPress renders the editor in an iframe.
16+
- Use `page` for sidebar panels, buttons, settings, and toolbar.
17+
18+
## Things That Break
19+
20+
- **Never use `page.goBack()`.** WP Playground crashes. Split into separate tests instead.
21+
- **No `retries` in playwright config.** Retries mask real failures. Use `retries: 0`.
22+
- **Tests share state within a spec.** Settings persist between tests in the same spec file. Explicitly reset anything a previous test might have changed.
23+
- **Isolate heavy tests.** Complex tests (WASM compilation, multi-step workflows) should get their own directory with a `blueprint.json` so they run in a fresh Playground instance.
24+
25+
## WASM-Specific
26+
27+
- Always wait for the WASM module to load before filling CSS: poll `window.patternCss?.transform` until truthy.
28+
- After filling CSS, poll `pcssAdditionalCssCompiled` to confirm compilation finished before asserting styles.
29+
- Use `expect(...).toPass({ timeout })` for WASM compilation checks, not `waitForTimeout`.
30+
31+
## Assertions
32+
33+
- Use `toBeInViewport()` not `toBeVisible()` for elements hidden by `max-height` + `overflow: hidden`.
34+
- Use `button#id` not `#id` when IDs may be duplicated (loading placeholders).
35+
- When checking compiled CSS on the element, use `{ timeout: 10000 }` on `toHaveCSS` to allow for style injection delay.
36+
37+
## Preview
38+
39+
- `admin.createNewPost()` creates a post. Preview URL is `/?p=${postId}&preview=true`, not `/?page_id=`.
40+
41+
## Config
42+
43+
- Use `fast-glob` not `node:fs` `globSync` in config files — `@types/node` is pinned to v20 by `@wordpress/e2e-test-utils-playwright`.
44+
- Use `.filter((s): s is { ... } => s !== null)` instead of `.filter(Boolean)` for proper type narrowing.

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ test-results/
99
/vendor/
1010
tsconfig.tsbuildinfo
1111

12-
.claude
12+
# Claude Code — commit rules and settings, ignore user-specific files
13+
.claude/*
14+
!.claude/rules/
15+
!.claude/settings.json

tests/default/pattern-css.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,26 @@ test.describe('Pattern CSS (Block)', () => {
260260
}) => {
261261
await admin.createNewPost({ title: 'Test post' });
262262
await editor.insertBlock({
263-
name: 'core/paragraph',
264-
attributes: { content: 'Hello' },
263+
name: 'core/group',
264+
innerBlocks: [
265+
{ name: 'core/paragraph', attributes: { content: 'Hello' } },
266+
],
265267
});
266268

267269
const editorCanvas = page
268270
.locator('iframe[name="editor-canvas"]')
269271
.contentFrame();
270272

271273
await editor.selectBlocks(
272-
editorCanvas.locator('p[role=document]').first(),
274+
editorCanvas.locator('.wp-block-group').first(),
273275
);
274276
await page.getByRole('button', { name: 'Pattern CSS' }).click();
275277
await waitForWasm(page);
276278

277279
const cssEditor = page.locator(
278280
'[data-cy="pcss-editor-block"] textarea',
279281
);
280-
await cssEditor.fill('[block] { color: rgb(155, 200, 130); }');
282+
await cssEditor.fill('p { color: rgb(155, 200, 130); }');
281283

282284
// No error line should exist
283285
await expect(
@@ -302,22 +304,22 @@ test.describe('Pattern CSS (Block)', () => {
302304
return blocks[0]?.attributes?.pcssClassId;
303305
});
304306

305-
await expect(editorCanvas.locator(`.${className}`)).toHaveCSS(
307+
await expect(editorCanvas.locator(`.${className} > p`)).toHaveCSS(
306308
'color',
307309
'rgb(155, 200, 130)',
308310
{ timeout: 10000 },
309311
);
310312

311313
// Add invalid CSS
312-
await cssEditor.fill('[block] { color: rgb(155, 200, 130); } ??');
314+
await cssEditor.fill('p { color: rgb(155, 200, 130); } ??');
313315

314316
// Error line should appear
315317
await expect(
316318
page.locator('[data-cy="pcss-editor-block"] pre .line-error'),
317319
).toBeVisible({ timeout: 10000 });
318320

319321
// Color should still be the valid one, not changed
320-
await expect(editorCanvas.locator(`.${className}`)).toHaveCSS(
322+
await expect(editorCanvas.locator(`.${className} > p`)).toHaveCSS(
321323
'color',
322324
'rgb(155, 200, 130)',
323325
);

0 commit comments

Comments
 (0)