@stratawp/testing 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,6 +63,7 @@ describe('My Block', () => {
63
63
  ```
64
64
 
65
65
  Run tests:
66
+
66
67
  ```bash
67
68
  pnpm test
68
69
  ```
@@ -95,13 +96,13 @@ test.describe('My Block E2E', () => {
95
96
 
96
97
  await publishPost(page)
97
98
 
98
- await expect(page.locator('.components-snackbar'))
99
- .toContainText('published')
99
+ await expect(page.locator('.components-snackbar')).toContainText('published')
100
100
  })
101
101
  })
102
102
  ```
103
103
 
104
104
  Run E2E tests:
105
+
105
106
  ```bash
106
107
  pnpm test:e2e
107
108
  ```
@@ -121,6 +122,7 @@ beforeAll(() => {
121
122
  ```
122
123
 
123
124
  **Mocked APIs:**
125
+
124
126
  - `@wordpress/blocks` - Block registration and manipulation
125
127
  - `@wordpress/data` - Data stores and state management
126
128
  - `@wordpress/i18n` - Internationalization functions
@@ -374,12 +376,14 @@ pnpm test:coverage
374
376
  ```
375
377
 
376
378
  **Coverage Thresholds:**
379
+
377
380
  - Lines: 80%
378
381
  - Functions: 80%
379
382
  - Branches: 75%
380
383
  - Statements: 80%
381
384
 
382
385
  View coverage report:
386
+
383
387
  ```bash
384
388
  open coverage/index.html
385
389
  ```
@@ -389,6 +393,7 @@ open coverage/index.html
389
393
  ### Unit Tests
390
394
 
391
395
  1. **Mock WordPress APIs**
396
+
392
397
  ```typescript
393
398
  beforeAll(() => {
394
399
  setupWordPressMocks()
@@ -397,6 +402,7 @@ open coverage/index.html
397
402
  ```
398
403
 
399
404
  2. **Test Block Registration**
405
+
400
406
  ```typescript
401
407
  it('should register with correct configuration', () => {
402
408
  testBlockRegistration('my-theme/my-block', expectedConfig)
@@ -404,6 +410,7 @@ open coverage/index.html
404
410
  ```
405
411
 
406
412
  3. **Test Component Rendering**
413
+
407
414
  ```typescript
408
415
  it('should render with attributes', () => {
409
416
  const { getByText } = renderBlockEdit(EditComponent, {
@@ -414,6 +421,7 @@ open coverage/index.html
414
421
  ```
415
422
 
416
423
  4. **Test Attribute Updates**
424
+
417
425
  ```typescript
418
426
  it('should update attributes', () => {
419
427
  const setAttributes = vi.fn()
@@ -427,6 +435,7 @@ open coverage/index.html
427
435
  ### E2E Tests
428
436
 
429
437
  1. **Login Before Each Test**
438
+
430
439
  ```typescript
431
440
  test.beforeEach(async ({ page }) => {
432
441
  await wpLogin(page)
@@ -434,6 +443,7 @@ open coverage/index.html
434
443
  ```
435
444
 
436
445
  2. **Use Descriptive Test Names**
446
+
437
447
  ```typescript
438
448
  test('should insert heading block and change level to H3', async ({ page }) => {
439
449
  // ...
@@ -441,11 +451,13 @@ open coverage/index.html
441
451
  ```
442
452
 
443
453
  3. **Wait for Network Idle**
454
+
444
455
  ```typescript
445
456
  await page.waitForLoadState('networkidle')
446
457
  ```
447
458
 
448
459
  4. **Take Screenshots on Failure**
460
+
449
461
  ```typescript
450
462
  test('visual test', async ({ page }) => {
451
463
  await page.screenshot({ path: 'screenshots/my-block.png' })
@@ -521,6 +533,7 @@ See the `examples/basic-theme/__tests__` and `examples/basic-theme/e2e` director
521
533
  **Problem:** Tests fail with "wp is not defined"
522
534
 
523
535
  **Solution:** Call `setupWordPressMocks()` before your tests:
536
+
524
537
  ```typescript
525
538
  beforeAll(() => {
526
539
  setupWordPressMocks()
@@ -532,6 +545,7 @@ beforeAll(() => {
532
545
  **Problem:** Custom matchers like `toHaveBlockClass` are not recognized
533
546
 
534
547
  **Solution:** Call `setupCustomMatchers()`:
548
+
535
549
  ```typescript
536
550
  beforeAll(() => {
537
551
  setupCustomMatchers()
@@ -543,6 +557,7 @@ beforeAll(() => {
543
557
  **Problem:** E2E tests timeout
544
558
 
545
559
  **Solution:** Increase timeout in `playwright.config.ts`:
560
+
546
561
  ```typescript
547
562
  export default defineConfig({
548
563
  timeout: 60 * 1000, // 60 seconds
@@ -554,6 +569,7 @@ export default defineConfig({
554
569
  **Problem:** Block editor doesn't load in E2E tests
555
570
 
556
571
  **Solution:** Ensure WordPress is running and the URL is correct:
572
+
557
573
  ```typescript
558
574
  use: {
559
575
  baseURL: process.env.WP_BASE_URL || 'http://localhost:8888',
package/dist/index.d.ts CHANGED
@@ -3,11 +3,6 @@ export { RenderOptions } from '@testing-library/react';
3
3
  import * as vitest from 'vitest';
4
4
  import '@testing-library/user-event';
5
5
 
6
- /**
7
- * Block Testing Utilities
8
- * Helper functions for testing Gutenberg blocks
9
- */
10
-
11
6
  /**
12
7
  * Block Configuration Interface
13
8
  */
@@ -42,7 +37,7 @@ declare function createMockAttributes(blockType: string, overrides?: Record<stri
42
37
  * Test block attribute validation
43
38
  */
44
39
  declare function testBlockAttributes(EditComponent: React.ComponentType<any>, attributes: Record<string, any>): {
45
- setAttributes: any;
40
+ setAttributes: vitest.Mock<any, any>;
46
41
  rerender: (newAttributes: Record<string, any>) => void;
47
42
  };
48
43
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/utils/block-testing.ts
2
2
  import { render } from "@testing-library/react";
3
3
  import { createElement } from "react";
4
+ import { vi } from "vitest";
4
5
  function renderBlockEdit(EditComponent, props = {}, options) {
5
6
  const defaultProps = {
6
7
  attributes: {},
@@ -136,9 +137,7 @@ var mockWordPressData = {
136
137
  var mockWordPressi18n = {
137
138
  __: vi2.fn((text) => text),
138
139
  _x: vi2.fn((text) => text),
139
- _n: vi2.fn(
140
- (single, plural, number) => number === 1 ? single : plural
141
- ),
140
+ _n: vi2.fn((single, plural, number) => number === 1 ? single : plural),
142
141
  sprintf: vi2.fn((format, ..._args) => format),
143
142
  setLocaleData: vi2.fn()
144
143
  };
@@ -244,9 +243,7 @@ function toHaveBlockAttributes(received, expectedAttributes) {
244
243
  };
245
244
  }
246
245
  const actualAttributes = Object.keys(received.attributes);
247
- const missingAttributes = expectedAttributes.filter(
248
- (attr) => !actualAttributes.includes(attr)
249
- );
246
+ const missingAttributes = expectedAttributes.filter((attr) => !actualAttributes.includes(attr));
250
247
  const pass = missingAttributes.length === 0;
251
248
  return {
252
249
  pass,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/block-testing.ts","../src/mocks/wordpress.ts","../src/matchers/index.ts"],"sourcesContent":["/**\n * Block Testing Utilities\n * Helper functions for testing Gutenberg blocks\n */\n\nimport { render, RenderOptions } from '@testing-library/react'\nimport { createElement } from 'react'\n\n/**\n * Block Configuration Interface\n */\nexport interface BlockConfiguration {\n name?: string\n title?: string\n description?: string\n category?: string\n icon?: string | Record<string, any>\n keywords?: string[]\n attributes?: Record<string, any>\n supports?: Record<string, any>\n [key: string]: any\n}\n\n/**\n * Render a block's edit component for testing\n */\nexport function renderBlockEdit(\n EditComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n setAttributes: vi.fn(),\n isSelected: false,\n clientId: 'test-block-id',\n ...props,\n }\n\n return render(createElement(EditComponent, defaultProps), options)\n}\n\n/**\n * Render a block's save component for testing\n */\nexport function renderBlockSave(\n SaveComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n ...props,\n }\n\n return render(createElement(SaveComponent, defaultProps), options)\n}\n\n/**\n * Test if a block is registered correctly\n */\nexport function testBlockRegistration(\n blockName: string,\n expectedConfig: Partial<BlockConfiguration>\n) {\n const { getBlockType } = wp.blocks\n\n const blockType = getBlockType(blockName)\n\n if (!blockType) {\n throw new Error(`Block ${blockName} is not registered`)\n }\n\n // Check required fields\n expect(blockType.name).toBe(blockName)\n\n if (expectedConfig.title) {\n expect(blockType.title).toBe(expectedConfig.title)\n }\n\n if (expectedConfig.category) {\n expect(blockType.category).toBe(expectedConfig.category)\n }\n\n if (expectedConfig.attributes) {\n expect(blockType.attributes).toEqual(expectedConfig.attributes)\n }\n\n return blockType\n}\n\n/**\n * Create mock block attributes\n */\nexport function createMockAttributes(\n blockType: string,\n overrides: Record<string, any> = {}\n): Record<string, any> {\n const defaults: Record<string, Record<string, any>> = {\n 'core/paragraph': {\n content: 'Test paragraph content',\n align: 'left',\n },\n 'core/heading': {\n content: 'Test heading',\n level: 2,\n },\n 'core/image': {\n url: 'https://example.com/image.jpg',\n alt: 'Test image',\n id: 123,\n },\n }\n\n return {\n ...(defaults[blockType] || {}),\n ...overrides,\n }\n}\n\n/**\n * Test block attribute validation\n */\nexport function testBlockAttributes(\n EditComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const setAttributes = vi.fn()\n\n const { rerender } = renderBlockEdit(EditComponent, {\n attributes,\n setAttributes,\n })\n\n return {\n setAttributes,\n rerender: (newAttributes: Record<string, any>) => {\n return rerender(\n createElement(EditComponent, {\n attributes: newAttributes,\n setAttributes,\n })\n )\n },\n }\n}\n\n/**\n * Test block save output\n */\nexport function testBlockSaveOutput(\n SaveComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const { container } = renderBlockSave(SaveComponent, { attributes })\n return container.innerHTML\n}\n\n/**\n * Mock block context\n */\nexport function createMockBlockContext(overrides: any = {}) {\n return {\n clientId: 'mock-client-id',\n isSelected: false,\n isMultiSelected: false,\n hasSelectedInnerBlock: false,\n index: 0,\n ...overrides,\n }\n}\n\n/**\n * Wait for block to render\n */\nexport async function waitForBlockRender(\n callback: () => void,\n options: { timeout?: number } = {}\n) {\n const timeout = options.timeout || 1000\n const startTime = Date.now()\n\n while (Date.now() - startTime < timeout) {\n try {\n callback()\n return\n } catch (error) {\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n }\n\n throw new Error('Block failed to render within timeout')\n}\n","/**\n * WordPress API Mocks\n * Mock implementations of WordPress JavaScript APIs for testing\n */\n\nimport { vi } from 'vitest'\n\n/**\n * Mock @wordpress/blocks\n */\nexport const mockWordPressBlocks = {\n registerBlockType: vi.fn((name: string, settings: any) => {\n return { name, ...settings }\n }),\n unregisterBlockType: vi.fn(),\n getBlockType: vi.fn((name: string) => ({\n name,\n title: 'Mock Block',\n category: 'common',\n icon: 'block-default',\n attributes: {},\n })),\n getBlockTypes: vi.fn(() => []),\n hasBlockSupport: vi.fn(() => true),\n createBlock: vi.fn((name: string, attributes?: any) => ({\n name,\n attributes: attributes || {},\n clientId: 'mock-client-id',\n })),\n}\n\n/**\n * Mock @wordpress/data\n */\nexport const mockWordPressData = {\n useSelect: vi.fn((callback: any) => callback(() => ({}))),\n useDispatch: vi.fn((_storeName: string) => ({})),\n select: vi.fn((_storeName: string) => ({})),\n dispatch: vi.fn((_storeName: string) => ({})),\n subscribe: vi.fn(),\n registerStore: vi.fn(),\n}\n\n/**\n * Mock @wordpress/i18n\n */\nexport const mockWordPressi18n = {\n __: vi.fn((text: string) => text),\n _x: vi.fn((text: string) => text),\n _n: vi.fn((single: string, plural: string, number: number) =>\n number === 1 ? single : plural\n ),\n sprintf: vi.fn((format: string, ..._args: any[]) => format),\n setLocaleData: vi.fn(),\n}\n\n/**\n * Mock @wordpress/components\n */\nexport const mockWordPressComponents = {\n Button: vi.fn(({ children }: any) => children),\n TextControl: vi.fn(() => null),\n SelectControl: vi.fn(() => null),\n RangeControl: vi.fn(() => null),\n ToggleControl: vi.fn(() => null),\n Panel: vi.fn(({ children }: any) => children),\n PanelBody: vi.fn(({ children }: any) => children),\n PanelRow: vi.fn(({ children }: any) => children),\n}\n\n/**\n * Mock @wordpress/block-editor\n */\nexport const mockWordPressBlockEditor = {\n InspectorControls: vi.fn(({ children }: any) => children),\n BlockControls: vi.fn(({ children }: any) => children),\n useBlockProps: vi.fn((props?: any) => ({\n className: 'wp-block',\n ...props,\n })),\n RichText: vi.fn(() => null),\n MediaUpload: vi.fn(() => null),\n MediaUploadCheck: vi.fn(({ children }: any) => children),\n ColorPalette: vi.fn(() => null),\n URLInput: vi.fn(() => null),\n}\n\n/**\n * Mock @wordpress/element\n */\nexport const mockWordPressElement = {\n createElement: vi.fn((type: any, props?: any, ...children: any[]) => ({\n type,\n props: { ...props, children },\n })),\n Fragment: vi.fn(({ children }: any) => children),\n useState: vi.fn((initialState: any) => [initialState, vi.fn()]),\n useEffect: vi.fn((_effect: () => void, _deps?: any[]) => undefined),\n useRef: vi.fn((initialValue: any) => ({ current: initialValue })),\n useMemo: vi.fn((factory: () => any, _deps?: any[]) => factory()),\n useCallback: vi.fn((callback: (...args: any[]) => any, _deps?: any[]) => callback),\n}\n\n/**\n * Mock @wordpress/api-fetch\n */\nexport const mockWordPressApiFetch = vi.fn(\n (_options: { path: string; method?: string; data?: any }) =>\n Promise.resolve({ ok: true, json: () => Promise.resolve({}) })\n)\n\n/**\n * Setup all WordPress mocks\n */\nexport function setupWordPressMocks() {\n vi.mock('@wordpress/blocks', () => mockWordPressBlocks)\n vi.mock('@wordpress/data', () => mockWordPressData)\n vi.mock('@wordpress/i18n', () => mockWordPressi18n)\n vi.mock('@wordpress/components', () => mockWordPressComponents)\n vi.mock('@wordpress/block-editor', () => mockWordPressBlockEditor)\n vi.mock('@wordpress/element', () => mockWordPressElement)\n vi.mock('@wordpress/api-fetch', () => ({ default: mockWordPressApiFetch }))\n\n // Setup global WordPress objects\n ;(global as any).wp = {\n blocks: mockWordPressBlocks,\n data: mockWordPressData,\n i18n: mockWordPressi18n,\n components: mockWordPressComponents,\n blockEditor: mockWordPressBlockEditor,\n element: mockWordPressElement,\n apiFetch: mockWordPressApiFetch,\n }\n}\n\n/**\n * Clear all WordPress mocks\n */\nexport function clearWordPressMocks() {\n Object.values(mockWordPressBlocks).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n Object.values(mockWordPressData).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n // Clear other mocks...\n}\n","/**\n * Custom Vitest Matchers\n * WordPress-specific test matchers\n */\n\nimport { expect } from 'vitest'\n\n/**\n * Check if element has WordPress block class\n */\nexport function toHaveBlockClass(received: HTMLElement, blockName: string) {\n const className = `wp-block-${blockName.replace('/', '-')}`\n const pass = received.classList.contains(className)\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected element not to have block class ${className}`\n : `Expected element to have block class ${className}`,\n }\n}\n\n/**\n * Check if block is registered\n */\nexport function toBeRegisteredBlock(received: string) {\n const { getBlockType } = (global as any).wp?.blocks || {}\n\n if (!getBlockType) {\n return {\n pass: false,\n message: () => 'WordPress blocks API is not available',\n }\n }\n\n const blockType = getBlockType(received)\n const pass = blockType !== undefined\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block ${received} not to be registered`\n : `Expected block ${received} to be registered`,\n }\n}\n\n/**\n * Check if block has required attributes\n */\nexport function toHaveBlockAttributes(\n received: any,\n expectedAttributes: string[]\n) {\n if (!received || !received.attributes) {\n return {\n pass: false,\n message: () => 'Block does not have attributes property',\n }\n }\n\n const actualAttributes = Object.keys(received.attributes)\n const missingAttributes = expectedAttributes.filter(\n (attr) => !actualAttributes.includes(attr)\n )\n\n const pass = missingAttributes.length === 0\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block not to have attributes: ${expectedAttributes.join(', ')}`\n : `Expected block to have attributes: ${missingAttributes.join(', ')}`,\n }\n}\n\n/**\n * Check if element is a valid WordPress block\n */\nexport function toBeValidWordPressBlock(received: HTMLElement) {\n const hasDataType = received.hasAttribute('data-type')\n const hasWpBlock = received.classList.contains('wp-block') ||\n Array.from(received.classList).some(c => c.startsWith('wp-block-'))\n\n const pass = hasDataType && hasWpBlock\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected element not to be a valid WordPress block'\n : 'Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)',\n }\n}\n\n/**\n * Check if string is valid block markup\n */\nexport function toBeValidBlockMarkup(received: string) {\n // Check for WordPress block comment syntax\n const hasStartComment = received.includes('<!-- wp:')\n const hasEndComment = received.includes('<!-- /wp:') || received.includes('/-->')\n\n const pass = hasStartComment && hasEndComment\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected string not to be valid block markup'\n : 'Expected string to be valid block markup (must include WordPress block comments)',\n }\n}\n\n/**\n * Extend Vitest matchers\n */\nexport function setupCustomMatchers() {\n expect.extend({\n toHaveBlockClass,\n toBeRegisteredBlock,\n toHaveBlockAttributes,\n toBeValidWordPressBlock,\n toBeValidBlockMarkup,\n })\n}\n\n// Type declarations for TypeScript\ndeclare global {\n namespace Vi {\n interface Matchers<R = any> {\n toHaveBlockClass(blockName: string): R\n toBeRegisteredBlock(): R\n toHaveBlockAttributes(attributes: string[]): R\n toBeValidWordPressBlock(): R\n toBeValidBlockMarkup(): R\n }\n }\n}\n"],"mappings":"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","names":["vi","expect"]}
1
+ {"version":3,"sources":["../src/utils/block-testing.ts","../src/mocks/wordpress.ts","../src/matchers/index.ts"],"sourcesContent":["/**\n * Block Testing Utilities\n * Helper functions for testing Gutenberg blocks\n */\n\nimport { render, RenderOptions } from '@testing-library/react'\nimport { createElement } from 'react'\nimport { vi } from 'vitest'\n\n/**\n * Block Configuration Interface\n */\nexport interface BlockConfiguration {\n name?: string\n title?: string\n description?: string\n category?: string\n icon?: string | Record<string, any>\n keywords?: string[]\n attributes?: Record<string, any>\n supports?: Record<string, any>\n [key: string]: any\n}\n\n/**\n * Render a block's edit component for testing\n */\nexport function renderBlockEdit(\n EditComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n setAttributes: vi.fn(),\n isSelected: false,\n clientId: 'test-block-id',\n ...props,\n }\n\n return render(createElement(EditComponent, defaultProps), options)\n}\n\n/**\n * Render a block's save component for testing\n */\nexport function renderBlockSave(\n SaveComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n ...props,\n }\n\n return render(createElement(SaveComponent, defaultProps), options)\n}\n\n/**\n * Test if a block is registered correctly\n */\nexport function testBlockRegistration(\n blockName: string,\n expectedConfig: Partial<BlockConfiguration>\n) {\n const { getBlockType } = wp.blocks\n\n const blockType = getBlockType(blockName)\n\n if (!blockType) {\n throw new Error(`Block ${blockName} is not registered`)\n }\n\n // Check required fields\n expect(blockType.name).toBe(blockName)\n\n if (expectedConfig.title) {\n expect(blockType.title).toBe(expectedConfig.title)\n }\n\n if (expectedConfig.category) {\n expect(blockType.category).toBe(expectedConfig.category)\n }\n\n if (expectedConfig.attributes) {\n expect(blockType.attributes).toEqual(expectedConfig.attributes)\n }\n\n return blockType\n}\n\n/**\n * Create mock block attributes\n */\nexport function createMockAttributes(\n blockType: string,\n overrides: Record<string, any> = {}\n): Record<string, any> {\n const defaults: Record<string, Record<string, any>> = {\n 'core/paragraph': {\n content: 'Test paragraph content',\n align: 'left',\n },\n 'core/heading': {\n content: 'Test heading',\n level: 2,\n },\n 'core/image': {\n url: 'https://example.com/image.jpg',\n alt: 'Test image',\n id: 123,\n },\n }\n\n return {\n ...(defaults[blockType] || {}),\n ...overrides,\n }\n}\n\n/**\n * Test block attribute validation\n */\nexport function testBlockAttributes(\n EditComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const setAttributes = vi.fn()\n\n const { rerender } = renderBlockEdit(EditComponent, {\n attributes,\n setAttributes,\n })\n\n return {\n setAttributes,\n rerender: (newAttributes: Record<string, any>) => {\n return rerender(\n createElement(EditComponent, {\n attributes: newAttributes,\n setAttributes,\n })\n )\n },\n }\n}\n\n/**\n * Test block save output\n */\nexport function testBlockSaveOutput(\n SaveComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const { container } = renderBlockSave(SaveComponent, { attributes })\n return container.innerHTML\n}\n\n/**\n * Mock block context\n */\nexport function createMockBlockContext(overrides: any = {}) {\n return {\n clientId: 'mock-client-id',\n isSelected: false,\n isMultiSelected: false,\n hasSelectedInnerBlock: false,\n index: 0,\n ...overrides,\n }\n}\n\n/**\n * Wait for block to render\n */\nexport async function waitForBlockRender(callback: () => void, options: { timeout?: number } = {}) {\n const timeout = options.timeout || 1000\n const startTime = Date.now()\n\n while (Date.now() - startTime < timeout) {\n try {\n callback()\n return\n } catch (error) {\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n }\n\n throw new Error('Block failed to render within timeout')\n}\n","/**\n * WordPress API Mocks\n * Mock implementations of WordPress JavaScript APIs for testing\n */\n\nimport { vi } from 'vitest'\n\n/**\n * Mock @wordpress/blocks\n */\nexport const mockWordPressBlocks = {\n registerBlockType: vi.fn((name: string, settings: any) => {\n return { name, ...settings }\n }),\n unregisterBlockType: vi.fn(),\n getBlockType: vi.fn((name: string) => ({\n name,\n title: 'Mock Block',\n category: 'common',\n icon: 'block-default',\n attributes: {},\n })),\n getBlockTypes: vi.fn(() => []),\n hasBlockSupport: vi.fn(() => true),\n createBlock: vi.fn((name: string, attributes?: any) => ({\n name,\n attributes: attributes || {},\n clientId: 'mock-client-id',\n })),\n}\n\n/**\n * Mock @wordpress/data\n */\nexport const mockWordPressData = {\n useSelect: vi.fn((callback: any) => callback(() => ({}))),\n useDispatch: vi.fn((_storeName: string) => ({})),\n select: vi.fn((_storeName: string) => ({})),\n dispatch: vi.fn((_storeName: string) => ({})),\n subscribe: vi.fn(),\n registerStore: vi.fn(),\n}\n\n/**\n * Mock @wordpress/i18n\n */\nexport const mockWordPressi18n = {\n __: vi.fn((text: string) => text),\n _x: vi.fn((text: string) => text),\n _n: vi.fn((single: string, plural: string, number: number) => (number === 1 ? single : plural)),\n sprintf: vi.fn((format: string, ..._args: any[]) => format),\n setLocaleData: vi.fn(),\n}\n\n/**\n * Mock @wordpress/components\n */\nexport const mockWordPressComponents = {\n Button: vi.fn(({ children }: any) => children),\n TextControl: vi.fn(() => null),\n SelectControl: vi.fn(() => null),\n RangeControl: vi.fn(() => null),\n ToggleControl: vi.fn(() => null),\n Panel: vi.fn(({ children }: any) => children),\n PanelBody: vi.fn(({ children }: any) => children),\n PanelRow: vi.fn(({ children }: any) => children),\n}\n\n/**\n * Mock @wordpress/block-editor\n */\nexport const mockWordPressBlockEditor = {\n InspectorControls: vi.fn(({ children }: any) => children),\n BlockControls: vi.fn(({ children }: any) => children),\n useBlockProps: vi.fn((props?: any) => ({\n className: 'wp-block',\n ...props,\n })),\n RichText: vi.fn(() => null),\n MediaUpload: vi.fn(() => null),\n MediaUploadCheck: vi.fn(({ children }: any) => children),\n ColorPalette: vi.fn(() => null),\n URLInput: vi.fn(() => null),\n}\n\n/**\n * Mock @wordpress/element\n */\nexport const mockWordPressElement = {\n createElement: vi.fn((type: any, props?: any, ...children: any[]) => ({\n type,\n props: { ...props, children },\n })),\n Fragment: vi.fn(({ children }: any) => children),\n useState: vi.fn((initialState: any) => [initialState, vi.fn()]),\n useEffect: vi.fn((_effect: () => void, _deps?: any[]) => undefined),\n useRef: vi.fn((initialValue: any) => ({ current: initialValue })),\n useMemo: vi.fn((factory: () => any, _deps?: any[]) => factory()),\n useCallback: vi.fn((callback: (...args: any[]) => any, _deps?: any[]) => callback),\n}\n\n/**\n * Mock @wordpress/api-fetch\n */\nexport const mockWordPressApiFetch = vi.fn(\n (_options: { path: string; method?: string; data?: any }) =>\n Promise.resolve({ ok: true, json: () => Promise.resolve({}) })\n)\n\n/**\n * Setup all WordPress mocks\n */\nexport function setupWordPressMocks() {\n vi.mock('@wordpress/blocks', () => mockWordPressBlocks)\n vi.mock('@wordpress/data', () => mockWordPressData)\n vi.mock('@wordpress/i18n', () => mockWordPressi18n)\n vi.mock('@wordpress/components', () => mockWordPressComponents)\n vi.mock('@wordpress/block-editor', () => mockWordPressBlockEditor)\n vi.mock('@wordpress/element', () => mockWordPressElement)\n vi.mock('@wordpress/api-fetch', () => ({ default: mockWordPressApiFetch }))\n\n // Setup global WordPress objects\n ;(global as any).wp = {\n blocks: mockWordPressBlocks,\n data: mockWordPressData,\n i18n: mockWordPressi18n,\n components: mockWordPressComponents,\n blockEditor: mockWordPressBlockEditor,\n element: mockWordPressElement,\n apiFetch: mockWordPressApiFetch,\n }\n}\n\n/**\n * Clear all WordPress mocks\n */\nexport function clearWordPressMocks() {\n Object.values(mockWordPressBlocks).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n Object.values(mockWordPressData).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n // Clear other mocks...\n}\n","/**\n * Custom Vitest Matchers\n * WordPress-specific test matchers\n */\n\nimport { expect } from 'vitest'\n\n/**\n * Check if element has WordPress block class\n */\nexport function toHaveBlockClass(received: HTMLElement, blockName: string) {\n const className = `wp-block-${blockName.replace('/', '-')}`\n const pass = received.classList.contains(className)\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected element not to have block class ${className}`\n : `Expected element to have block class ${className}`,\n }\n}\n\n/**\n * Check if block is registered\n */\nexport function toBeRegisteredBlock(received: string) {\n const { getBlockType } = (global as any).wp?.blocks || {}\n\n if (!getBlockType) {\n return {\n pass: false,\n message: () => 'WordPress blocks API is not available',\n }\n }\n\n const blockType = getBlockType(received)\n const pass = blockType !== undefined\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block ${received} not to be registered`\n : `Expected block ${received} to be registered`,\n }\n}\n\n/**\n * Check if block has required attributes\n */\nexport function toHaveBlockAttributes(received: any, expectedAttributes: string[]) {\n if (!received || !received.attributes) {\n return {\n pass: false,\n message: () => 'Block does not have attributes property',\n }\n }\n\n const actualAttributes = Object.keys(received.attributes)\n const missingAttributes = expectedAttributes.filter((attr) => !actualAttributes.includes(attr))\n\n const pass = missingAttributes.length === 0\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block not to have attributes: ${expectedAttributes.join(', ')}`\n : `Expected block to have attributes: ${missingAttributes.join(', ')}`,\n }\n}\n\n/**\n * Check if element is a valid WordPress block\n */\nexport function toBeValidWordPressBlock(received: HTMLElement) {\n const hasDataType = received.hasAttribute('data-type')\n const hasWpBlock =\n received.classList.contains('wp-block') ||\n Array.from(received.classList).some((c) => c.startsWith('wp-block-'))\n\n const pass = hasDataType && hasWpBlock\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected element not to be a valid WordPress block'\n : 'Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)',\n }\n}\n\n/**\n * Check if string is valid block markup\n */\nexport function toBeValidBlockMarkup(received: string) {\n // Check for WordPress block comment syntax\n const hasStartComment = received.includes('<!-- wp:')\n const hasEndComment = received.includes('<!-- /wp:') || received.includes('/-->')\n\n const pass = hasStartComment && hasEndComment\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected string not to be valid block markup'\n : 'Expected string to be valid block markup (must include WordPress block comments)',\n }\n}\n\n/**\n * Extend Vitest matchers\n */\nexport function setupCustomMatchers() {\n expect.extend({\n toHaveBlockClass,\n toBeRegisteredBlock,\n toHaveBlockAttributes,\n toBeValidWordPressBlock,\n toBeValidBlockMarkup,\n })\n}\n\n// Type declarations for TypeScript — namespace Vi is required by Vitest's type augmentation API\n/* eslint-disable @typescript-eslint/no-namespace */\ndeclare global {\n namespace Vi {\n interface Matchers<R = any> {\n toHaveBlockClass(blockName: string): R\n toBeRegisteredBlock(): R\n toHaveBlockAttributes(attributes: string[]): R\n toBeValidWordPressBlock(): R\n toBeValidBlockMarkup(): R\n }\n }\n}\n/* eslint-enable @typescript-eslint/no-namespace */\n"],"mappings":"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","names":["vi","expect"]}
@@ -1,5 +1,6 @@
1
1
  import { Page, Locator } from '@playwright/test';
2
2
  export { Browser, BrowserContext, Locator, Page, expect, test } from '@playwright/test';
3
+ import AxeBuilder from '@axe-core/playwright';
3
4
 
4
5
  /**
5
6
  * Playwright E2E Testing Helpers
@@ -95,4 +96,9 @@ declare function takeA11yScreenshot(page: Page, name: string): Promise<void>;
95
96
  */
96
97
  declare function setupConsoleErrorTracking(page: Page): string[];
97
98
 
98
- export { blockExists, deleteBlock, getBlock, getBlockCount, getSelectedBlock, goToWPAdmin, insertBlock, isLoggedIn, moveBlockDown, moveBlockUp, openBlockEditor, previewPost, publishPost, saveDraft, selectBlock, setupConsoleErrorTracking, takeA11yScreenshot, typeInBlock, updateBlockAttribute, waitForWPAdmin, wpLogin, wpLogout };
99
+ /** WCAG 2.1 A/AA tag set for the blocking gate. */
100
+ declare const WCAG_AA_TAGS: readonly ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"];
101
+ /** axe scanner scoped to WCAG 2.1 A/AA, excluding the wp-env admin bar. */
102
+ declare function makeAxeBuilder(page: any): AxeBuilder;
103
+
104
+ export { WCAG_AA_TAGS, blockExists, deleteBlock, getBlock, getBlockCount, getSelectedBlock, goToWPAdmin, insertBlock, isLoggedIn, makeAxeBuilder, moveBlockDown, moveBlockUp, openBlockEditor, previewPost, publishPost, saveDraft, selectBlock, setupConsoleErrorTracking, takeA11yScreenshot, typeInBlock, updateBlockAttribute, waitForWPAdmin, wpLogin, wpLogout };
@@ -114,7 +114,15 @@ function setupConsoleErrorTracking(page) {
114
114
 
115
115
  // src/playwright.ts
116
116
  import { test, expect } from "@playwright/test";
117
+
118
+ // src/axe.ts
119
+ import AxeBuilder from "@axe-core/playwright";
120
+ var WCAG_AA_TAGS = ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"];
121
+ function makeAxeBuilder(page) {
122
+ return new AxeBuilder({ page }).withTags([...WCAG_AA_TAGS]).exclude("#wpadminbar");
123
+ }
117
124
  export {
125
+ WCAG_AA_TAGS,
118
126
  blockExists,
119
127
  deleteBlock,
120
128
  expect,
@@ -124,6 +132,7 @@ export {
124
132
  goToWPAdmin,
125
133
  insertBlock,
126
134
  isLoggedIn,
135
+ makeAxeBuilder,
127
136
  moveBlockDown,
128
137
  moveBlockUp,
129
138
  openBlockEditor,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/playwright-helpers.ts","../src/playwright.ts"],"sourcesContent":["/**\n * Playwright E2E Testing Helpers\n * Helper functions for WordPress E2E testing with Playwright\n */\n\nimport type { Page, Locator } from '@playwright/test'\n\n/**\n * WordPress login helper\n */\nexport async function wpLogin(\n page: Page,\n username: string = 'admin',\n password: string = 'password'\n) {\n await page.goto('/wp-login.php')\n await page.fill('#user_login', username)\n await page.fill('#user_pass', password)\n await page.click('#wp-submit')\n await page.waitForLoadState('networkidle')\n}\n\n/**\n * WordPress logout helper\n */\nexport async function wpLogout(page: Page) {\n await page.goto('/wp-login.php?action=logout')\n await page.click('a') // Confirm logout\n await page.waitForLoadState('networkidle')\n}\n\n/**\n * Navigate to block editor\n */\nexport async function openBlockEditor(page: Page, postType: string = 'post') {\n await page.goto(`/wp-admin/post-new.php?post_type=${postType}`)\n await page.waitForSelector('.block-editor')\n}\n\n/**\n * Insert a block in the editor\n */\nexport async function insertBlock(page: Page, blockName: string) {\n // Click the block inserter\n await page.click('.block-editor-inserter__toggle')\n\n // Search for the block\n await page.fill('.block-editor-inserter__search input', blockName)\n\n // Click the block\n await page.click(`button[aria-label*=\"${blockName}\"]`)\n\n // Wait for block to be inserted\n await page.waitForSelector(`[data-type*=\"${blockName}\"]`)\n}\n\n/**\n * Get block by type\n */\nexport function getBlock(page: Page, blockType: string): Locator {\n return page.locator(`[data-type=\"${blockType}\"]`)\n}\n\n/**\n * Get selected block\n */\nexport function getSelectedBlock(page: Page): Locator {\n return page.locator('.is-selected')\n}\n\n/**\n * Type in block\n */\nexport async function typeInBlock(page: Page, blockType: string, text: string) {\n const block = getBlock(page, blockType)\n await block.click()\n await block.type(text)\n}\n\n/**\n * Update block attribute via inspector\n */\nexport async function updateBlockAttribute(\n page: Page,\n attributeName: string,\n value: string | number | boolean\n) {\n // Open inspector if not open\n const inspectorButton = page.locator('button[aria-label=\"Settings\"]')\n if (await inspectorButton.isVisible()) {\n await inspectorButton.click()\n }\n\n // Find and update the control\n const control = page.locator(`[aria-label*=\"${attributeName}\"]`)\n await control.fill(String(value))\n}\n\n/**\n * Publish post\n */\nexport async function publishPost(page: Page) {\n const publishButton = page.locator('.editor-post-publish-button__button')\n\n // Click publish panel button\n await publishButton.first().click()\n\n // Click final publish button\n await publishButton.last().click()\n\n // Wait for success notice\n await page.waitForSelector('.components-snackbar')\n}\n\n/**\n * Save draft\n */\nexport async function saveDraft(page: Page) {\n await page.click('.editor-post-save-draft')\n await page.waitForSelector('.is-saved')\n}\n\n/**\n * Preview post\n */\nexport async function previewPost(page: Page): Promise<Page> {\n const [previewPage] = await Promise.all([\n page.context().waitForEvent('page'),\n page.click('.editor-post-preview'),\n ])\n\n await previewPage.waitForLoadState('networkidle')\n return previewPage\n}\n\n/**\n * Check if block exists\n */\nexport async function blockExists(page: Page, blockType: string): Promise<boolean> {\n return (await getBlock(page, blockType).count()) > 0\n}\n\n/**\n * Get block count\n */\nexport async function getBlockCount(page: Page, blockType?: string): Promise<number> {\n if (blockType) {\n return await getBlock(page, blockType).count()\n }\n return await page.locator('[data-type]').count()\n}\n\n/**\n * Select block\n */\nexport async function selectBlock(page: Page, blockType: string) {\n await getBlock(page, blockType).click()\n}\n\n/**\n * Delete block\n */\nexport async function deleteBlock(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.keyboard.press('Backspace')\n}\n\n/**\n * Move block up\n */\nexport async function moveBlockUp(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.click('button[aria-label=\"Move up\"]')\n}\n\n/**\n * Move block down\n */\nexport async function moveBlockDown(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.click('button[aria-label=\"Move down\"]')\n}\n\n/**\n * Wait for WordPress admin\n */\nexport async function waitForWPAdmin(page: Page) {\n await page.waitForSelector('#wpadminbar')\n}\n\n/**\n * Navigate to WordPress admin page\n */\nexport async function goToWPAdmin(page: Page, path: string = '') {\n await page.goto(`/wp-admin/${path}`)\n await waitForWPAdmin(page)\n}\n\n/**\n * Check if user is logged in\n */\nexport async function isLoggedIn(page: Page): Promise<boolean> {\n await page.goto('/wp-admin/')\n return page.url().includes('/wp-admin/')\n}\n\n/**\n * Take accessible screenshot\n */\nexport async function takeA11yScreenshot(page: Page, name: string) {\n await page.screenshot({\n path: `screenshots/${name}.png`,\n fullPage: true,\n })\n}\n\n/**\n * Check for console errors\n */\nexport function setupConsoleErrorTracking(page: Page): string[] {\n const errors: string[] = []\n\n page.on('console', (msg) => {\n if (msg.type() === 'error') {\n errors.push(msg.text())\n }\n })\n\n page.on('pageerror', (error) => {\n errors.push(error.message)\n })\n\n return errors\n}\n","/**\n * Playwright E2E Testing Utilities\n * Re-export everything needed for Playwright tests\n */\n\nexport * from './utils/playwright-helpers'\n\n// Re-export Playwright test utilities\nexport { test, expect } from '@playwright/test'\nexport type { Page, Locator, Browser, BrowserContext } from '@playwright/test'\n"],"mappings":";AAUA,eAAsB,QACpB,MACA,WAAmB,SACnB,WAAmB,YACnB;AACA,QAAM,KAAK,KAAK,eAAe;AAC/B,QAAM,KAAK,KAAK,eAAe,QAAQ;AACvC,QAAM,KAAK,KAAK,cAAc,QAAQ;AACtC,QAAM,KAAK,MAAM,YAAY;AAC7B,QAAM,KAAK,iBAAiB,aAAa;AAC3C;AAKA,eAAsB,SAAS,MAAY;AACzC,QAAM,KAAK,KAAK,6BAA6B;AAC7C,QAAM,KAAK,MAAM,GAAG;AACpB,QAAM,KAAK,iBAAiB,aAAa;AAC3C;AAKA,eAAsB,gBAAgB,MAAY,WAAmB,QAAQ;AAC3E,QAAM,KAAK,KAAK,oCAAoC,QAAQ,EAAE;AAC9D,QAAM,KAAK,gBAAgB,eAAe;AAC5C;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAE/D,QAAM,KAAK,MAAM,gCAAgC;AAGjD,QAAM,KAAK,KAAK,wCAAwC,SAAS;AAGjE,QAAM,KAAK,MAAM,uBAAuB,SAAS,IAAI;AAGrD,QAAM,KAAK,gBAAgB,gBAAgB,SAAS,IAAI;AAC1D;AAKO,SAAS,SAAS,MAAY,WAA4B;AAC/D,SAAO,KAAK,QAAQ,eAAe,SAAS,IAAI;AAClD;AAKO,SAAS,iBAAiB,MAAqB;AACpD,SAAO,KAAK,QAAQ,cAAc;AACpC;AAKA,eAAsB,YAAY,MAAY,WAAmB,MAAc;AAC7E,QAAM,QAAQ,SAAS,MAAM,SAAS;AACtC,QAAM,MAAM,MAAM;AAClB,QAAM,MAAM,KAAK,IAAI;AACvB;AAKA,eAAsB,qBACpB,MACA,eACA,OACA;AAEA,QAAM,kBAAkB,KAAK,QAAQ,+BAA+B;AACpE,MAAI,MAAM,gBAAgB,UAAU,GAAG;AACrC,UAAM,gBAAgB,MAAM;AAAA,EAC9B;AAGA,QAAM,UAAU,KAAK,QAAQ,iBAAiB,aAAa,IAAI;AAC/D,QAAM,QAAQ,KAAK,OAAO,KAAK,CAAC;AAClC;AAKA,eAAsB,YAAY,MAAY;AAC5C,QAAM,gBAAgB,KAAK,QAAQ,qCAAqC;AAGxE,QAAM,cAAc,MAAM,EAAE,MAAM;AAGlC,QAAM,cAAc,KAAK,EAAE,MAAM;AAGjC,QAAM,KAAK,gBAAgB,sBAAsB;AACnD;AAKA,eAAsB,UAAU,MAAY;AAC1C,QAAM,KAAK,MAAM,yBAAyB;AAC1C,QAAM,KAAK,gBAAgB,WAAW;AACxC;AAKA,eAAsB,YAAY,MAA2B;AAC3D,QAAM,CAAC,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtC,KAAK,QAAQ,EAAE,aAAa,MAAM;AAAA,IAClC,KAAK,MAAM,sBAAsB;AAAA,EACnC,CAAC;AAED,QAAM,YAAY,iBAAiB,aAAa;AAChD,SAAO;AACT;AAKA,eAAsB,YAAY,MAAY,WAAqC;AACjF,SAAQ,MAAM,SAAS,MAAM,SAAS,EAAE,MAAM,IAAK;AACrD;AAKA,eAAsB,cAAc,MAAY,WAAqC;AACnF,MAAI,WAAW;AACb,WAAO,MAAM,SAAS,MAAM,SAAS,EAAE,MAAM;AAAA,EAC/C;AACA,SAAO,MAAM,KAAK,QAAQ,aAAa,EAAE,MAAM;AACjD;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,SAAS,MAAM,SAAS,EAAE,MAAM;AACxC;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,SAAS,MAAM,WAAW;AACvC;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,MAAM,8BAA8B;AACjD;AAKA,eAAsB,cAAc,MAAY,WAAmB;AACjE,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,MAAM,gCAAgC;AACnD;AAKA,eAAsB,eAAe,MAAY;AAC/C,QAAM,KAAK,gBAAgB,aAAa;AAC1C;AAKA,eAAsB,YAAY,MAAY,OAAe,IAAI;AAC/D,QAAM,KAAK,KAAK,aAAa,IAAI,EAAE;AACnC,QAAM,eAAe,IAAI;AAC3B;AAKA,eAAsB,WAAW,MAA8B;AAC7D,QAAM,KAAK,KAAK,YAAY;AAC5B,SAAO,KAAK,IAAI,EAAE,SAAS,YAAY;AACzC;AAKA,eAAsB,mBAAmB,MAAY,MAAc;AACjE,QAAM,KAAK,WAAW;AAAA,IACpB,MAAM,eAAe,IAAI;AAAA,IACzB,UAAU;AAAA,EACZ,CAAC;AACH;AAKO,SAAS,0BAA0B,MAAsB;AAC9D,QAAM,SAAmB,CAAC;AAE1B,OAAK,GAAG,WAAW,CAAC,QAAQ;AAC1B,QAAI,IAAI,KAAK,MAAM,SAAS;AAC1B,aAAO,KAAK,IAAI,KAAK,CAAC;AAAA,IACxB;AAAA,EACF,CAAC;AAED,OAAK,GAAG,aAAa,CAAC,UAAU;AAC9B,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,CAAC;AAED,SAAO;AACT;;;ACjOA,SAAS,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/utils/playwright-helpers.ts","../src/playwright.ts","../src/axe.ts"],"sourcesContent":["/**\n * Playwright E2E Testing Helpers\n * Helper functions for WordPress E2E testing with Playwright\n */\n\nimport type { Page, Locator } from '@playwright/test'\n\n/**\n * WordPress login helper\n */\nexport async function wpLogin(\n page: Page,\n username: string = 'admin',\n password: string = 'password'\n) {\n await page.goto('/wp-login.php')\n await page.fill('#user_login', username)\n await page.fill('#user_pass', password)\n await page.click('#wp-submit')\n await page.waitForLoadState('networkidle')\n}\n\n/**\n * WordPress logout helper\n */\nexport async function wpLogout(page: Page) {\n await page.goto('/wp-login.php?action=logout')\n await page.click('a') // Confirm logout\n await page.waitForLoadState('networkidle')\n}\n\n/**\n * Navigate to block editor\n */\nexport async function openBlockEditor(page: Page, postType: string = 'post') {\n await page.goto(`/wp-admin/post-new.php?post_type=${postType}`)\n await page.waitForSelector('.block-editor')\n}\n\n/**\n * Insert a block in the editor\n */\nexport async function insertBlock(page: Page, blockName: string) {\n // Click the block inserter\n await page.click('.block-editor-inserter__toggle')\n\n // Search for the block\n await page.fill('.block-editor-inserter__search input', blockName)\n\n // Click the block\n await page.click(`button[aria-label*=\"${blockName}\"]`)\n\n // Wait for block to be inserted\n await page.waitForSelector(`[data-type*=\"${blockName}\"]`)\n}\n\n/**\n * Get block by type\n */\nexport function getBlock(page: Page, blockType: string): Locator {\n return page.locator(`[data-type=\"${blockType}\"]`)\n}\n\n/**\n * Get selected block\n */\nexport function getSelectedBlock(page: Page): Locator {\n return page.locator('.is-selected')\n}\n\n/**\n * Type in block\n */\nexport async function typeInBlock(page: Page, blockType: string, text: string) {\n const block = getBlock(page, blockType)\n await block.click()\n await block.type(text)\n}\n\n/**\n * Update block attribute via inspector\n */\nexport async function updateBlockAttribute(\n page: Page,\n attributeName: string,\n value: string | number | boolean\n) {\n // Open inspector if not open\n const inspectorButton = page.locator('button[aria-label=\"Settings\"]')\n if (await inspectorButton.isVisible()) {\n await inspectorButton.click()\n }\n\n // Find and update the control\n const control = page.locator(`[aria-label*=\"${attributeName}\"]`)\n await control.fill(String(value))\n}\n\n/**\n * Publish post\n */\nexport async function publishPost(page: Page) {\n const publishButton = page.locator('.editor-post-publish-button__button')\n\n // Click publish panel button\n await publishButton.first().click()\n\n // Click final publish button\n await publishButton.last().click()\n\n // Wait for success notice\n await page.waitForSelector('.components-snackbar')\n}\n\n/**\n * Save draft\n */\nexport async function saveDraft(page: Page) {\n await page.click('.editor-post-save-draft')\n await page.waitForSelector('.is-saved')\n}\n\n/**\n * Preview post\n */\nexport async function previewPost(page: Page): Promise<Page> {\n const [previewPage] = await Promise.all([\n page.context().waitForEvent('page'),\n page.click('.editor-post-preview'),\n ])\n\n await previewPage.waitForLoadState('networkidle')\n return previewPage\n}\n\n/**\n * Check if block exists\n */\nexport async function blockExists(page: Page, blockType: string): Promise<boolean> {\n return (await getBlock(page, blockType).count()) > 0\n}\n\n/**\n * Get block count\n */\nexport async function getBlockCount(page: Page, blockType?: string): Promise<number> {\n if (blockType) {\n return await getBlock(page, blockType).count()\n }\n return await page.locator('[data-type]').count()\n}\n\n/**\n * Select block\n */\nexport async function selectBlock(page: Page, blockType: string) {\n await getBlock(page, blockType).click()\n}\n\n/**\n * Delete block\n */\nexport async function deleteBlock(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.keyboard.press('Backspace')\n}\n\n/**\n * Move block up\n */\nexport async function moveBlockUp(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.click('button[aria-label=\"Move up\"]')\n}\n\n/**\n * Move block down\n */\nexport async function moveBlockDown(page: Page, blockType: string) {\n await selectBlock(page, blockType)\n await page.click('button[aria-label=\"Move down\"]')\n}\n\n/**\n * Wait for WordPress admin\n */\nexport async function waitForWPAdmin(page: Page) {\n await page.waitForSelector('#wpadminbar')\n}\n\n/**\n * Navigate to WordPress admin page\n */\nexport async function goToWPAdmin(page: Page, path: string = '') {\n await page.goto(`/wp-admin/${path}`)\n await waitForWPAdmin(page)\n}\n\n/**\n * Check if user is logged in\n */\nexport async function isLoggedIn(page: Page): Promise<boolean> {\n await page.goto('/wp-admin/')\n return page.url().includes('/wp-admin/')\n}\n\n/**\n * Take accessible screenshot\n */\nexport async function takeA11yScreenshot(page: Page, name: string) {\n await page.screenshot({\n path: `screenshots/${name}.png`,\n fullPage: true,\n })\n}\n\n/**\n * Check for console errors\n */\nexport function setupConsoleErrorTracking(page: Page): string[] {\n const errors: string[] = []\n\n page.on('console', (msg) => {\n if (msg.type() === 'error') {\n errors.push(msg.text())\n }\n })\n\n page.on('pageerror', (error) => {\n errors.push(error.message)\n })\n\n return errors\n}\n","/**\n * Playwright E2E Testing Utilities\n * Re-export everything needed for Playwright tests\n */\n\nexport * from './utils/playwright-helpers'\n\n// Re-export Playwright test utilities\nexport { test, expect } from '@playwright/test'\nexport type { Page, Locator, Browser, BrowserContext } from '@playwright/test'\n\n// Re-export axe a11y utilities\nexport { makeAxeBuilder, WCAG_AA_TAGS } from './axe'\n","import AxeBuilder from '@axe-core/playwright'\n\n/** WCAG 2.1 A/AA tag set for the blocking gate. */\nexport const WCAG_AA_TAGS = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'] as const\n\n/** axe scanner scoped to WCAG 2.1 A/AA, excluding the wp-env admin bar. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function makeAxeBuilder(page: any): AxeBuilder {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n return new AxeBuilder({ page }).withTags([...WCAG_AA_TAGS]).exclude('#wpadminbar')\n}\n"],"mappings":";AAUA,eAAsB,QACpB,MACA,WAAmB,SACnB,WAAmB,YACnB;AACA,QAAM,KAAK,KAAK,eAAe;AAC/B,QAAM,KAAK,KAAK,eAAe,QAAQ;AACvC,QAAM,KAAK,KAAK,cAAc,QAAQ;AACtC,QAAM,KAAK,MAAM,YAAY;AAC7B,QAAM,KAAK,iBAAiB,aAAa;AAC3C;AAKA,eAAsB,SAAS,MAAY;AACzC,QAAM,KAAK,KAAK,6BAA6B;AAC7C,QAAM,KAAK,MAAM,GAAG;AACpB,QAAM,KAAK,iBAAiB,aAAa;AAC3C;AAKA,eAAsB,gBAAgB,MAAY,WAAmB,QAAQ;AAC3E,QAAM,KAAK,KAAK,oCAAoC,QAAQ,EAAE;AAC9D,QAAM,KAAK,gBAAgB,eAAe;AAC5C;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAE/D,QAAM,KAAK,MAAM,gCAAgC;AAGjD,QAAM,KAAK,KAAK,wCAAwC,SAAS;AAGjE,QAAM,KAAK,MAAM,uBAAuB,SAAS,IAAI;AAGrD,QAAM,KAAK,gBAAgB,gBAAgB,SAAS,IAAI;AAC1D;AAKO,SAAS,SAAS,MAAY,WAA4B;AAC/D,SAAO,KAAK,QAAQ,eAAe,SAAS,IAAI;AAClD;AAKO,SAAS,iBAAiB,MAAqB;AACpD,SAAO,KAAK,QAAQ,cAAc;AACpC;AAKA,eAAsB,YAAY,MAAY,WAAmB,MAAc;AAC7E,QAAM,QAAQ,SAAS,MAAM,SAAS;AACtC,QAAM,MAAM,MAAM;AAClB,QAAM,MAAM,KAAK,IAAI;AACvB;AAKA,eAAsB,qBACpB,MACA,eACA,OACA;AAEA,QAAM,kBAAkB,KAAK,QAAQ,+BAA+B;AACpE,MAAI,MAAM,gBAAgB,UAAU,GAAG;AACrC,UAAM,gBAAgB,MAAM;AAAA,EAC9B;AAGA,QAAM,UAAU,KAAK,QAAQ,iBAAiB,aAAa,IAAI;AAC/D,QAAM,QAAQ,KAAK,OAAO,KAAK,CAAC;AAClC;AAKA,eAAsB,YAAY,MAAY;AAC5C,QAAM,gBAAgB,KAAK,QAAQ,qCAAqC;AAGxE,QAAM,cAAc,MAAM,EAAE,MAAM;AAGlC,QAAM,cAAc,KAAK,EAAE,MAAM;AAGjC,QAAM,KAAK,gBAAgB,sBAAsB;AACnD;AAKA,eAAsB,UAAU,MAAY;AAC1C,QAAM,KAAK,MAAM,yBAAyB;AAC1C,QAAM,KAAK,gBAAgB,WAAW;AACxC;AAKA,eAAsB,YAAY,MAA2B;AAC3D,QAAM,CAAC,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtC,KAAK,QAAQ,EAAE,aAAa,MAAM;AAAA,IAClC,KAAK,MAAM,sBAAsB;AAAA,EACnC,CAAC;AAED,QAAM,YAAY,iBAAiB,aAAa;AAChD,SAAO;AACT;AAKA,eAAsB,YAAY,MAAY,WAAqC;AACjF,SAAQ,MAAM,SAAS,MAAM,SAAS,EAAE,MAAM,IAAK;AACrD;AAKA,eAAsB,cAAc,MAAY,WAAqC;AACnF,MAAI,WAAW;AACb,WAAO,MAAM,SAAS,MAAM,SAAS,EAAE,MAAM;AAAA,EAC/C;AACA,SAAO,MAAM,KAAK,QAAQ,aAAa,EAAE,MAAM;AACjD;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,SAAS,MAAM,SAAS,EAAE,MAAM;AACxC;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,SAAS,MAAM,WAAW;AACvC;AAKA,eAAsB,YAAY,MAAY,WAAmB;AAC/D,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,MAAM,8BAA8B;AACjD;AAKA,eAAsB,cAAc,MAAY,WAAmB;AACjE,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,KAAK,MAAM,gCAAgC;AACnD;AAKA,eAAsB,eAAe,MAAY;AAC/C,QAAM,KAAK,gBAAgB,aAAa;AAC1C;AAKA,eAAsB,YAAY,MAAY,OAAe,IAAI;AAC/D,QAAM,KAAK,KAAK,aAAa,IAAI,EAAE;AACnC,QAAM,eAAe,IAAI;AAC3B;AAKA,eAAsB,WAAW,MAA8B;AAC7D,QAAM,KAAK,KAAK,YAAY;AAC5B,SAAO,KAAK,IAAI,EAAE,SAAS,YAAY;AACzC;AAKA,eAAsB,mBAAmB,MAAY,MAAc;AACjE,QAAM,KAAK,WAAW;AAAA,IACpB,MAAM,eAAe,IAAI;AAAA,IACzB,UAAU;AAAA,EACZ,CAAC;AACH;AAKO,SAAS,0BAA0B,MAAsB;AAC9D,QAAM,SAAmB,CAAC;AAE1B,OAAK,GAAG,WAAW,CAAC,QAAQ;AAC1B,QAAI,IAAI,KAAK,MAAM,SAAS;AAC1B,aAAO,KAAK,IAAI,KAAK,CAAC;AAAA,IACxB;AAAA,EACF,CAAC;AAED,OAAK,GAAG,aAAa,CAAC,UAAU;AAC9B,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,CAAC;AAED,SAAO;AACT;;;ACjOA,SAAS,MAAM,cAAc;;;ACR7B,OAAO,gBAAgB;AAGhB,IAAM,eAAe,CAAC,UAAU,WAAW,WAAW,UAAU;AAIhE,SAAS,eAAe,MAAuB;AAEpD,SAAO,IAAI,WAAW,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC,EAAE,QAAQ,aAAa;AACnF;","names":[]}
package/dist/vitest.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/utils/block-testing.ts
2
2
  import { render } from "@testing-library/react";
3
3
  import { createElement } from "react";
4
+ import { vi } from "vitest";
4
5
  function renderBlockEdit(EditComponent, props = {}, options) {
5
6
  const defaultProps = {
6
7
  attributes: {},
@@ -136,9 +137,7 @@ var mockWordPressData = {
136
137
  var mockWordPressi18n = {
137
138
  __: vi2.fn((text) => text),
138
139
  _x: vi2.fn((text) => text),
139
- _n: vi2.fn(
140
- (single, plural, number) => number === 1 ? single : plural
141
- ),
140
+ _n: vi2.fn((single, plural, number) => number === 1 ? single : plural),
142
141
  sprintf: vi2.fn((format, ..._args) => format),
143
142
  setLocaleData: vi2.fn()
144
143
  };
@@ -244,9 +243,7 @@ function toHaveBlockAttributes(received, expectedAttributes) {
244
243
  };
245
244
  }
246
245
  const actualAttributes = Object.keys(received.attributes);
247
- const missingAttributes = expectedAttributes.filter(
248
- (attr) => !actualAttributes.includes(attr)
249
- );
246
+ const missingAttributes = expectedAttributes.filter((attr) => !actualAttributes.includes(attr));
250
247
  const pass = missingAttributes.length === 0;
251
248
  return {
252
249
  pass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/block-testing.ts","../src/mocks/wordpress.ts","../src/matchers/index.ts","../src/vitest.ts"],"sourcesContent":["/**\n * Block Testing Utilities\n * Helper functions for testing Gutenberg blocks\n */\n\nimport { render, RenderOptions } from '@testing-library/react'\nimport { createElement } from 'react'\n\n/**\n * Block Configuration Interface\n */\nexport interface BlockConfiguration {\n name?: string\n title?: string\n description?: string\n category?: string\n icon?: string | Record<string, any>\n keywords?: string[]\n attributes?: Record<string, any>\n supports?: Record<string, any>\n [key: string]: any\n}\n\n/**\n * Render a block's edit component for testing\n */\nexport function renderBlockEdit(\n EditComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n setAttributes: vi.fn(),\n isSelected: false,\n clientId: 'test-block-id',\n ...props,\n }\n\n return render(createElement(EditComponent, defaultProps), options)\n}\n\n/**\n * Render a block's save component for testing\n */\nexport function renderBlockSave(\n SaveComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n ...props,\n }\n\n return render(createElement(SaveComponent, defaultProps), options)\n}\n\n/**\n * Test if a block is registered correctly\n */\nexport function testBlockRegistration(\n blockName: string,\n expectedConfig: Partial<BlockConfiguration>\n) {\n const { getBlockType } = wp.blocks\n\n const blockType = getBlockType(blockName)\n\n if (!blockType) {\n throw new Error(`Block ${blockName} is not registered`)\n }\n\n // Check required fields\n expect(blockType.name).toBe(blockName)\n\n if (expectedConfig.title) {\n expect(blockType.title).toBe(expectedConfig.title)\n }\n\n if (expectedConfig.category) {\n expect(blockType.category).toBe(expectedConfig.category)\n }\n\n if (expectedConfig.attributes) {\n expect(blockType.attributes).toEqual(expectedConfig.attributes)\n }\n\n return blockType\n}\n\n/**\n * Create mock block attributes\n */\nexport function createMockAttributes(\n blockType: string,\n overrides: Record<string, any> = {}\n): Record<string, any> {\n const defaults: Record<string, Record<string, any>> = {\n 'core/paragraph': {\n content: 'Test paragraph content',\n align: 'left',\n },\n 'core/heading': {\n content: 'Test heading',\n level: 2,\n },\n 'core/image': {\n url: 'https://example.com/image.jpg',\n alt: 'Test image',\n id: 123,\n },\n }\n\n return {\n ...(defaults[blockType] || {}),\n ...overrides,\n }\n}\n\n/**\n * Test block attribute validation\n */\nexport function testBlockAttributes(\n EditComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const setAttributes = vi.fn()\n\n const { rerender } = renderBlockEdit(EditComponent, {\n attributes,\n setAttributes,\n })\n\n return {\n setAttributes,\n rerender: (newAttributes: Record<string, any>) => {\n return rerender(\n createElement(EditComponent, {\n attributes: newAttributes,\n setAttributes,\n })\n )\n },\n }\n}\n\n/**\n * Test block save output\n */\nexport function testBlockSaveOutput(\n SaveComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const { container } = renderBlockSave(SaveComponent, { attributes })\n return container.innerHTML\n}\n\n/**\n * Mock block context\n */\nexport function createMockBlockContext(overrides: any = {}) {\n return {\n clientId: 'mock-client-id',\n isSelected: false,\n isMultiSelected: false,\n hasSelectedInnerBlock: false,\n index: 0,\n ...overrides,\n }\n}\n\n/**\n * Wait for block to render\n */\nexport async function waitForBlockRender(\n callback: () => void,\n options: { timeout?: number } = {}\n) {\n const timeout = options.timeout || 1000\n const startTime = Date.now()\n\n while (Date.now() - startTime < timeout) {\n try {\n callback()\n return\n } catch (error) {\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n }\n\n throw new Error('Block failed to render within timeout')\n}\n","/**\n * WordPress API Mocks\n * Mock implementations of WordPress JavaScript APIs for testing\n */\n\nimport { vi } from 'vitest'\n\n/**\n * Mock @wordpress/blocks\n */\nexport const mockWordPressBlocks = {\n registerBlockType: vi.fn((name: string, settings: any) => {\n return { name, ...settings }\n }),\n unregisterBlockType: vi.fn(),\n getBlockType: vi.fn((name: string) => ({\n name,\n title: 'Mock Block',\n category: 'common',\n icon: 'block-default',\n attributes: {},\n })),\n getBlockTypes: vi.fn(() => []),\n hasBlockSupport: vi.fn(() => true),\n createBlock: vi.fn((name: string, attributes?: any) => ({\n name,\n attributes: attributes || {},\n clientId: 'mock-client-id',\n })),\n}\n\n/**\n * Mock @wordpress/data\n */\nexport const mockWordPressData = {\n useSelect: vi.fn((callback: any) => callback(() => ({}))),\n useDispatch: vi.fn((_storeName: string) => ({})),\n select: vi.fn((_storeName: string) => ({})),\n dispatch: vi.fn((_storeName: string) => ({})),\n subscribe: vi.fn(),\n registerStore: vi.fn(),\n}\n\n/**\n * Mock @wordpress/i18n\n */\nexport const mockWordPressi18n = {\n __: vi.fn((text: string) => text),\n _x: vi.fn((text: string) => text),\n _n: vi.fn((single: string, plural: string, number: number) =>\n number === 1 ? single : plural\n ),\n sprintf: vi.fn((format: string, ..._args: any[]) => format),\n setLocaleData: vi.fn(),\n}\n\n/**\n * Mock @wordpress/components\n */\nexport const mockWordPressComponents = {\n Button: vi.fn(({ children }: any) => children),\n TextControl: vi.fn(() => null),\n SelectControl: vi.fn(() => null),\n RangeControl: vi.fn(() => null),\n ToggleControl: vi.fn(() => null),\n Panel: vi.fn(({ children }: any) => children),\n PanelBody: vi.fn(({ children }: any) => children),\n PanelRow: vi.fn(({ children }: any) => children),\n}\n\n/**\n * Mock @wordpress/block-editor\n */\nexport const mockWordPressBlockEditor = {\n InspectorControls: vi.fn(({ children }: any) => children),\n BlockControls: vi.fn(({ children }: any) => children),\n useBlockProps: vi.fn((props?: any) => ({\n className: 'wp-block',\n ...props,\n })),\n RichText: vi.fn(() => null),\n MediaUpload: vi.fn(() => null),\n MediaUploadCheck: vi.fn(({ children }: any) => children),\n ColorPalette: vi.fn(() => null),\n URLInput: vi.fn(() => null),\n}\n\n/**\n * Mock @wordpress/element\n */\nexport const mockWordPressElement = {\n createElement: vi.fn((type: any, props?: any, ...children: any[]) => ({\n type,\n props: { ...props, children },\n })),\n Fragment: vi.fn(({ children }: any) => children),\n useState: vi.fn((initialState: any) => [initialState, vi.fn()]),\n useEffect: vi.fn((_effect: () => void, _deps?: any[]) => undefined),\n useRef: vi.fn((initialValue: any) => ({ current: initialValue })),\n useMemo: vi.fn((factory: () => any, _deps?: any[]) => factory()),\n useCallback: vi.fn((callback: (...args: any[]) => any, _deps?: any[]) => callback),\n}\n\n/**\n * Mock @wordpress/api-fetch\n */\nexport const mockWordPressApiFetch = vi.fn(\n (_options: { path: string; method?: string; data?: any }) =>\n Promise.resolve({ ok: true, json: () => Promise.resolve({}) })\n)\n\n/**\n * Setup all WordPress mocks\n */\nexport function setupWordPressMocks() {\n vi.mock('@wordpress/blocks', () => mockWordPressBlocks)\n vi.mock('@wordpress/data', () => mockWordPressData)\n vi.mock('@wordpress/i18n', () => mockWordPressi18n)\n vi.mock('@wordpress/components', () => mockWordPressComponents)\n vi.mock('@wordpress/block-editor', () => mockWordPressBlockEditor)\n vi.mock('@wordpress/element', () => mockWordPressElement)\n vi.mock('@wordpress/api-fetch', () => ({ default: mockWordPressApiFetch }))\n\n // Setup global WordPress objects\n ;(global as any).wp = {\n blocks: mockWordPressBlocks,\n data: mockWordPressData,\n i18n: mockWordPressi18n,\n components: mockWordPressComponents,\n blockEditor: mockWordPressBlockEditor,\n element: mockWordPressElement,\n apiFetch: mockWordPressApiFetch,\n }\n}\n\n/**\n * Clear all WordPress mocks\n */\nexport function clearWordPressMocks() {\n Object.values(mockWordPressBlocks).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n Object.values(mockWordPressData).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n // Clear other mocks...\n}\n","/**\n * Custom Vitest Matchers\n * WordPress-specific test matchers\n */\n\nimport { expect } from 'vitest'\n\n/**\n * Check if element has WordPress block class\n */\nexport function toHaveBlockClass(received: HTMLElement, blockName: string) {\n const className = `wp-block-${blockName.replace('/', '-')}`\n const pass = received.classList.contains(className)\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected element not to have block class ${className}`\n : `Expected element to have block class ${className}`,\n }\n}\n\n/**\n * Check if block is registered\n */\nexport function toBeRegisteredBlock(received: string) {\n const { getBlockType } = (global as any).wp?.blocks || {}\n\n if (!getBlockType) {\n return {\n pass: false,\n message: () => 'WordPress blocks API is not available',\n }\n }\n\n const blockType = getBlockType(received)\n const pass = blockType !== undefined\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block ${received} not to be registered`\n : `Expected block ${received} to be registered`,\n }\n}\n\n/**\n * Check if block has required attributes\n */\nexport function toHaveBlockAttributes(\n received: any,\n expectedAttributes: string[]\n) {\n if (!received || !received.attributes) {\n return {\n pass: false,\n message: () => 'Block does not have attributes property',\n }\n }\n\n const actualAttributes = Object.keys(received.attributes)\n const missingAttributes = expectedAttributes.filter(\n (attr) => !actualAttributes.includes(attr)\n )\n\n const pass = missingAttributes.length === 0\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block not to have attributes: ${expectedAttributes.join(', ')}`\n : `Expected block to have attributes: ${missingAttributes.join(', ')}`,\n }\n}\n\n/**\n * Check if element is a valid WordPress block\n */\nexport function toBeValidWordPressBlock(received: HTMLElement) {\n const hasDataType = received.hasAttribute('data-type')\n const hasWpBlock = received.classList.contains('wp-block') ||\n Array.from(received.classList).some(c => c.startsWith('wp-block-'))\n\n const pass = hasDataType && hasWpBlock\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected element not to be a valid WordPress block'\n : 'Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)',\n }\n}\n\n/**\n * Check if string is valid block markup\n */\nexport function toBeValidBlockMarkup(received: string) {\n // Check for WordPress block comment syntax\n const hasStartComment = received.includes('<!-- wp:')\n const hasEndComment = received.includes('<!-- /wp:') || received.includes('/-->')\n\n const pass = hasStartComment && hasEndComment\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected string not to be valid block markup'\n : 'Expected string to be valid block markup (must include WordPress block comments)',\n }\n}\n\n/**\n * Extend Vitest matchers\n */\nexport function setupCustomMatchers() {\n expect.extend({\n toHaveBlockClass,\n toBeRegisteredBlock,\n toHaveBlockAttributes,\n toBeValidWordPressBlock,\n toBeValidBlockMarkup,\n })\n}\n\n// Type declarations for TypeScript\ndeclare global {\n namespace Vi {\n interface Matchers<R = any> {\n toHaveBlockClass(blockName: string): R\n toBeRegisteredBlock(): R\n toHaveBlockAttributes(attributes: string[]): R\n toBeValidWordPressBlock(): R\n toBeValidBlockMarkup(): R\n }\n }\n}\n","/**\n * Vitest Testing Utilities\n * Re-export everything needed for Vitest tests\n */\n\nexport * from './utils/block-testing'\nexport * from './mocks/wordpress'\nexport * from './matchers/index'\n\n// Re-export testing library utilities\nexport * from '@testing-library/react'\nexport * from '@testing-library/user-event'\nexport { expect, describe, it, test, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest'\n"],"mappings":"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","names":["vi","expect","expect","vi"]}
1
+ {"version":3,"sources":["../src/utils/block-testing.ts","../src/mocks/wordpress.ts","../src/matchers/index.ts","../src/vitest.ts"],"sourcesContent":["/**\n * Block Testing Utilities\n * Helper functions for testing Gutenberg blocks\n */\n\nimport { render, RenderOptions } from '@testing-library/react'\nimport { createElement } from 'react'\nimport { vi } from 'vitest'\n\n/**\n * Block Configuration Interface\n */\nexport interface BlockConfiguration {\n name?: string\n title?: string\n description?: string\n category?: string\n icon?: string | Record<string, any>\n keywords?: string[]\n attributes?: Record<string, any>\n supports?: Record<string, any>\n [key: string]: any\n}\n\n/**\n * Render a block's edit component for testing\n */\nexport function renderBlockEdit(\n EditComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n setAttributes: vi.fn(),\n isSelected: false,\n clientId: 'test-block-id',\n ...props,\n }\n\n return render(createElement(EditComponent, defaultProps), options)\n}\n\n/**\n * Render a block's save component for testing\n */\nexport function renderBlockSave(\n SaveComponent: React.ComponentType<any>,\n props: any = {},\n options?: RenderOptions\n): ReturnType<typeof render> {\n const defaultProps = {\n attributes: {},\n ...props,\n }\n\n return render(createElement(SaveComponent, defaultProps), options)\n}\n\n/**\n * Test if a block is registered correctly\n */\nexport function testBlockRegistration(\n blockName: string,\n expectedConfig: Partial<BlockConfiguration>\n) {\n const { getBlockType } = wp.blocks\n\n const blockType = getBlockType(blockName)\n\n if (!blockType) {\n throw new Error(`Block ${blockName} is not registered`)\n }\n\n // Check required fields\n expect(blockType.name).toBe(blockName)\n\n if (expectedConfig.title) {\n expect(blockType.title).toBe(expectedConfig.title)\n }\n\n if (expectedConfig.category) {\n expect(blockType.category).toBe(expectedConfig.category)\n }\n\n if (expectedConfig.attributes) {\n expect(blockType.attributes).toEqual(expectedConfig.attributes)\n }\n\n return blockType\n}\n\n/**\n * Create mock block attributes\n */\nexport function createMockAttributes(\n blockType: string,\n overrides: Record<string, any> = {}\n): Record<string, any> {\n const defaults: Record<string, Record<string, any>> = {\n 'core/paragraph': {\n content: 'Test paragraph content',\n align: 'left',\n },\n 'core/heading': {\n content: 'Test heading',\n level: 2,\n },\n 'core/image': {\n url: 'https://example.com/image.jpg',\n alt: 'Test image',\n id: 123,\n },\n }\n\n return {\n ...(defaults[blockType] || {}),\n ...overrides,\n }\n}\n\n/**\n * Test block attribute validation\n */\nexport function testBlockAttributes(\n EditComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const setAttributes = vi.fn()\n\n const { rerender } = renderBlockEdit(EditComponent, {\n attributes,\n setAttributes,\n })\n\n return {\n setAttributes,\n rerender: (newAttributes: Record<string, any>) => {\n return rerender(\n createElement(EditComponent, {\n attributes: newAttributes,\n setAttributes,\n })\n )\n },\n }\n}\n\n/**\n * Test block save output\n */\nexport function testBlockSaveOutput(\n SaveComponent: React.ComponentType<any>,\n attributes: Record<string, any>\n) {\n const { container } = renderBlockSave(SaveComponent, { attributes })\n return container.innerHTML\n}\n\n/**\n * Mock block context\n */\nexport function createMockBlockContext(overrides: any = {}) {\n return {\n clientId: 'mock-client-id',\n isSelected: false,\n isMultiSelected: false,\n hasSelectedInnerBlock: false,\n index: 0,\n ...overrides,\n }\n}\n\n/**\n * Wait for block to render\n */\nexport async function waitForBlockRender(callback: () => void, options: { timeout?: number } = {}) {\n const timeout = options.timeout || 1000\n const startTime = Date.now()\n\n while (Date.now() - startTime < timeout) {\n try {\n callback()\n return\n } catch (error) {\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n }\n\n throw new Error('Block failed to render within timeout')\n}\n","/**\n * WordPress API Mocks\n * Mock implementations of WordPress JavaScript APIs for testing\n */\n\nimport { vi } from 'vitest'\n\n/**\n * Mock @wordpress/blocks\n */\nexport const mockWordPressBlocks = {\n registerBlockType: vi.fn((name: string, settings: any) => {\n return { name, ...settings }\n }),\n unregisterBlockType: vi.fn(),\n getBlockType: vi.fn((name: string) => ({\n name,\n title: 'Mock Block',\n category: 'common',\n icon: 'block-default',\n attributes: {},\n })),\n getBlockTypes: vi.fn(() => []),\n hasBlockSupport: vi.fn(() => true),\n createBlock: vi.fn((name: string, attributes?: any) => ({\n name,\n attributes: attributes || {},\n clientId: 'mock-client-id',\n })),\n}\n\n/**\n * Mock @wordpress/data\n */\nexport const mockWordPressData = {\n useSelect: vi.fn((callback: any) => callback(() => ({}))),\n useDispatch: vi.fn((_storeName: string) => ({})),\n select: vi.fn((_storeName: string) => ({})),\n dispatch: vi.fn((_storeName: string) => ({})),\n subscribe: vi.fn(),\n registerStore: vi.fn(),\n}\n\n/**\n * Mock @wordpress/i18n\n */\nexport const mockWordPressi18n = {\n __: vi.fn((text: string) => text),\n _x: vi.fn((text: string) => text),\n _n: vi.fn((single: string, plural: string, number: number) => (number === 1 ? single : plural)),\n sprintf: vi.fn((format: string, ..._args: any[]) => format),\n setLocaleData: vi.fn(),\n}\n\n/**\n * Mock @wordpress/components\n */\nexport const mockWordPressComponents = {\n Button: vi.fn(({ children }: any) => children),\n TextControl: vi.fn(() => null),\n SelectControl: vi.fn(() => null),\n RangeControl: vi.fn(() => null),\n ToggleControl: vi.fn(() => null),\n Panel: vi.fn(({ children }: any) => children),\n PanelBody: vi.fn(({ children }: any) => children),\n PanelRow: vi.fn(({ children }: any) => children),\n}\n\n/**\n * Mock @wordpress/block-editor\n */\nexport const mockWordPressBlockEditor = {\n InspectorControls: vi.fn(({ children }: any) => children),\n BlockControls: vi.fn(({ children }: any) => children),\n useBlockProps: vi.fn((props?: any) => ({\n className: 'wp-block',\n ...props,\n })),\n RichText: vi.fn(() => null),\n MediaUpload: vi.fn(() => null),\n MediaUploadCheck: vi.fn(({ children }: any) => children),\n ColorPalette: vi.fn(() => null),\n URLInput: vi.fn(() => null),\n}\n\n/**\n * Mock @wordpress/element\n */\nexport const mockWordPressElement = {\n createElement: vi.fn((type: any, props?: any, ...children: any[]) => ({\n type,\n props: { ...props, children },\n })),\n Fragment: vi.fn(({ children }: any) => children),\n useState: vi.fn((initialState: any) => [initialState, vi.fn()]),\n useEffect: vi.fn((_effect: () => void, _deps?: any[]) => undefined),\n useRef: vi.fn((initialValue: any) => ({ current: initialValue })),\n useMemo: vi.fn((factory: () => any, _deps?: any[]) => factory()),\n useCallback: vi.fn((callback: (...args: any[]) => any, _deps?: any[]) => callback),\n}\n\n/**\n * Mock @wordpress/api-fetch\n */\nexport const mockWordPressApiFetch = vi.fn(\n (_options: { path: string; method?: string; data?: any }) =>\n Promise.resolve({ ok: true, json: () => Promise.resolve({}) })\n)\n\n/**\n * Setup all WordPress mocks\n */\nexport function setupWordPressMocks() {\n vi.mock('@wordpress/blocks', () => mockWordPressBlocks)\n vi.mock('@wordpress/data', () => mockWordPressData)\n vi.mock('@wordpress/i18n', () => mockWordPressi18n)\n vi.mock('@wordpress/components', () => mockWordPressComponents)\n vi.mock('@wordpress/block-editor', () => mockWordPressBlockEditor)\n vi.mock('@wordpress/element', () => mockWordPressElement)\n vi.mock('@wordpress/api-fetch', () => ({ default: mockWordPressApiFetch }))\n\n // Setup global WordPress objects\n ;(global as any).wp = {\n blocks: mockWordPressBlocks,\n data: mockWordPressData,\n i18n: mockWordPressi18n,\n components: mockWordPressComponents,\n blockEditor: mockWordPressBlockEditor,\n element: mockWordPressElement,\n apiFetch: mockWordPressApiFetch,\n }\n}\n\n/**\n * Clear all WordPress mocks\n */\nexport function clearWordPressMocks() {\n Object.values(mockWordPressBlocks).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n Object.values(mockWordPressData).forEach((mock: any) => {\n if (typeof mock?.mockClear === 'function') {\n mock.mockClear()\n }\n })\n\n // Clear other mocks...\n}\n","/**\n * Custom Vitest Matchers\n * WordPress-specific test matchers\n */\n\nimport { expect } from 'vitest'\n\n/**\n * Check if element has WordPress block class\n */\nexport function toHaveBlockClass(received: HTMLElement, blockName: string) {\n const className = `wp-block-${blockName.replace('/', '-')}`\n const pass = received.classList.contains(className)\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected element not to have block class ${className}`\n : `Expected element to have block class ${className}`,\n }\n}\n\n/**\n * Check if block is registered\n */\nexport function toBeRegisteredBlock(received: string) {\n const { getBlockType } = (global as any).wp?.blocks || {}\n\n if (!getBlockType) {\n return {\n pass: false,\n message: () => 'WordPress blocks API is not available',\n }\n }\n\n const blockType = getBlockType(received)\n const pass = blockType !== undefined\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block ${received} not to be registered`\n : `Expected block ${received} to be registered`,\n }\n}\n\n/**\n * Check if block has required attributes\n */\nexport function toHaveBlockAttributes(received: any, expectedAttributes: string[]) {\n if (!received || !received.attributes) {\n return {\n pass: false,\n message: () => 'Block does not have attributes property',\n }\n }\n\n const actualAttributes = Object.keys(received.attributes)\n const missingAttributes = expectedAttributes.filter((attr) => !actualAttributes.includes(attr))\n\n const pass = missingAttributes.length === 0\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected block not to have attributes: ${expectedAttributes.join(', ')}`\n : `Expected block to have attributes: ${missingAttributes.join(', ')}`,\n }\n}\n\n/**\n * Check if element is a valid WordPress block\n */\nexport function toBeValidWordPressBlock(received: HTMLElement) {\n const hasDataType = received.hasAttribute('data-type')\n const hasWpBlock =\n received.classList.contains('wp-block') ||\n Array.from(received.classList).some((c) => c.startsWith('wp-block-'))\n\n const pass = hasDataType && hasWpBlock\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected element not to be a valid WordPress block'\n : 'Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)',\n }\n}\n\n/**\n * Check if string is valid block markup\n */\nexport function toBeValidBlockMarkup(received: string) {\n // Check for WordPress block comment syntax\n const hasStartComment = received.includes('<!-- wp:')\n const hasEndComment = received.includes('<!-- /wp:') || received.includes('/-->')\n\n const pass = hasStartComment && hasEndComment\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected string not to be valid block markup'\n : 'Expected string to be valid block markup (must include WordPress block comments)',\n }\n}\n\n/**\n * Extend Vitest matchers\n */\nexport function setupCustomMatchers() {\n expect.extend({\n toHaveBlockClass,\n toBeRegisteredBlock,\n toHaveBlockAttributes,\n toBeValidWordPressBlock,\n toBeValidBlockMarkup,\n })\n}\n\n// Type declarations for TypeScript — namespace Vi is required by Vitest's type augmentation API\n/* eslint-disable @typescript-eslint/no-namespace */\ndeclare global {\n namespace Vi {\n interface Matchers<R = any> {\n toHaveBlockClass(blockName: string): R\n toBeRegisteredBlock(): R\n toHaveBlockAttributes(attributes: string[]): R\n toBeValidWordPressBlock(): R\n toBeValidBlockMarkup(): R\n }\n }\n}\n/* eslint-enable @typescript-eslint/no-namespace */\n","/**\n * Vitest Testing Utilities\n * Re-export everything needed for Vitest tests\n */\n\nexport * from './utils/block-testing'\nexport * from './mocks/wordpress'\nexport * from './matchers/index'\n\n// Re-export testing library utilities\nexport * from '@testing-library/react'\nexport * from '@testing-library/user-event'\nexport { expect, describe, it, test, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest'\n"],"mappings":"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","names":["vi","expect","expect","vi"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratawp/testing",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Comprehensive testing utilities for StrataWP themes",
5
5
  "author": "Jon Imms",
6
6
  "license": "GPL-3.0-or-later",
@@ -39,17 +39,18 @@
39
39
  "playwright.config.ts"
40
40
  ],
41
41
  "dependencies": {
42
- "@testing-library/react": "^14.1.2",
43
42
  "@testing-library/jest-dom": "^6.1.5",
43
+ "@testing-library/react": "^14.1.2",
44
44
  "@testing-library/user-event": "^14.5.1",
45
45
  "@vitest/ui": "^1.0.4",
46
- "vitest": "^1.0.4",
47
- "jsdom": "^23.0.1",
46
+ "@wordpress/jest-preset-default": "^11.21.0",
48
47
  "happy-dom": "^12.10.3",
49
- "@playwright/test": "^1.40.1",
50
- "@wordpress/jest-preset-default": "^11.21.0"
48
+ "jsdom": "^23.0.1",
49
+ "vitest": "^1.0.4"
51
50
  },
52
51
  "devDependencies": {
52
+ "@axe-core/playwright": "^4.11.3",
53
+ "@playwright/test": "^1.61.1",
53
54
  "@types/react": "^18.2.45",
54
55
  "@vitest/coverage-v8": "^1.0.4",
55
56
  "tsup": "^8.0.1",
@@ -62,9 +63,9 @@
62
63
  "scripts": {
63
64
  "dev": "tsup --watch",
64
65
  "build": "tsup",
65
- "test": "vitest",
66
- "test:coverage": "vitest --coverage",
67
- "test:e2e": "playwright test",
68
- "lint": "eslint src"
66
+ "typecheck": "tsc --noEmit",
67
+ "test": "vitest run",
68
+ "test:coverage": "vitest run --coverage",
69
+ "test:e2e": "playwright test"
69
70
  }
70
71
  }
package/vitest.config.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { defineConfig } from 'vitest/config'
2
- import react from '@vitejs/plugin-react'
3
2
  import path from 'path'
4
3
 
5
4
  export default defineConfig({
6
- plugins: [react()],
7
5
  test: {
8
6
  globals: true,
9
7
  environment: 'jsdom',
@@ -11,6 +9,7 @@ export default defineConfig({
11
9
  coverage: {
12
10
  provider: 'v8',
13
11
  reporter: ['text', 'json', 'html', 'lcov'],
12
+ include: ['src/matchers/**'],
14
13
  exclude: [
15
14
  'node_modules/',
16
15
  'dist/',
@@ -20,10 +19,10 @@ export default defineConfig({
20
19
  '**/*.config.{ts,js}',
21
20
  ],
22
21
  thresholds: {
23
- lines: 80,
24
- functions: 80,
25
- branches: 75,
26
- statements: 80,
22
+ lines: 65,
23
+ functions: 34,
24
+ branches: 88,
25
+ statements: 65,
27
26
  },
28
27
  },
29
28
  include: ['**/*.{test,spec}.{ts,tsx}'],