@stratawp/testing 0.1.0

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.
@@ -0,0 +1,227 @@
1
+ import { RenderOptions, render } from '@testing-library/react';
2
+ export { RenderOptions } from '@testing-library/react';
3
+ import * as vitest from 'vitest';
4
+ import '@testing-library/user-event';
5
+
6
+ /**
7
+ * Block Testing Utilities
8
+ * Helper functions for testing Gutenberg blocks
9
+ */
10
+
11
+ /**
12
+ * Block Configuration Interface
13
+ */
14
+ interface BlockConfiguration {
15
+ name?: string;
16
+ title?: string;
17
+ description?: string;
18
+ category?: string;
19
+ icon?: string | Record<string, any>;
20
+ keywords?: string[];
21
+ attributes?: Record<string, any>;
22
+ supports?: Record<string, any>;
23
+ [key: string]: any;
24
+ }
25
+ /**
26
+ * Render a block's edit component for testing
27
+ */
28
+ declare function renderBlockEdit(EditComponent: React.ComponentType<any>, props?: any, options?: RenderOptions): ReturnType<typeof render>;
29
+ /**
30
+ * Render a block's save component for testing
31
+ */
32
+ declare function renderBlockSave(SaveComponent: React.ComponentType<any>, props?: any, options?: RenderOptions): ReturnType<typeof render>;
33
+ /**
34
+ * Test if a block is registered correctly
35
+ */
36
+ declare function testBlockRegistration(blockName: string, expectedConfig: Partial<BlockConfiguration>): any;
37
+ /**
38
+ * Create mock block attributes
39
+ */
40
+ declare function createMockAttributes(blockType: string, overrides?: Record<string, any>): Record<string, any>;
41
+ /**
42
+ * Test block attribute validation
43
+ */
44
+ declare function testBlockAttributes(EditComponent: React.ComponentType<any>, attributes: Record<string, any>): {
45
+ setAttributes: any;
46
+ rerender: (newAttributes: Record<string, any>) => void;
47
+ };
48
+ /**
49
+ * Test block save output
50
+ */
51
+ declare function testBlockSaveOutput(SaveComponent: React.ComponentType<any>, attributes: Record<string, any>): string;
52
+ /**
53
+ * Mock block context
54
+ */
55
+ declare function createMockBlockContext(overrides?: any): any;
56
+ /**
57
+ * Wait for block to render
58
+ */
59
+ declare function waitForBlockRender(callback: () => void, options?: {
60
+ timeout?: number;
61
+ }): Promise<void>;
62
+
63
+ /**
64
+ * WordPress API Mocks
65
+ * Mock implementations of WordPress JavaScript APIs for testing
66
+ */
67
+ /**
68
+ * Mock @wordpress/blocks
69
+ */
70
+ declare const mockWordPressBlocks: {
71
+ registerBlockType: vitest.Mock<[name: string, settings: any], any>;
72
+ unregisterBlockType: vitest.Mock<any, any>;
73
+ getBlockType: vitest.Mock<[name: string], {
74
+ name: string;
75
+ title: string;
76
+ category: string;
77
+ icon: string;
78
+ attributes: {};
79
+ }>;
80
+ getBlockTypes: vitest.Mock<[], never[]>;
81
+ hasBlockSupport: vitest.Mock<[], boolean>;
82
+ createBlock: vitest.Mock<[name: string, attributes?: any], {
83
+ name: string;
84
+ attributes: any;
85
+ clientId: string;
86
+ }>;
87
+ };
88
+ /**
89
+ * Mock @wordpress/data
90
+ */
91
+ declare const mockWordPressData: {
92
+ useSelect: vitest.Mock<[callback: any], any>;
93
+ useDispatch: vitest.Mock<[_storeName: string], {}>;
94
+ select: vitest.Mock<[_storeName: string], {}>;
95
+ dispatch: vitest.Mock<[_storeName: string], {}>;
96
+ subscribe: vitest.Mock<any, any>;
97
+ registerStore: vitest.Mock<any, any>;
98
+ };
99
+ /**
100
+ * Mock @wordpress/i18n
101
+ */
102
+ declare const mockWordPressi18n: {
103
+ __: vitest.Mock<[text: string], string>;
104
+ _x: vitest.Mock<[text: string], string>;
105
+ _n: vitest.Mock<[single: string, plural: string, number: number], string>;
106
+ sprintf: vitest.Mock<[format: string, ..._args: any[]], string>;
107
+ setLocaleData: vitest.Mock<any, any>;
108
+ };
109
+ /**
110
+ * Mock @wordpress/components
111
+ */
112
+ declare const mockWordPressComponents: {
113
+ Button: vitest.Mock<[any], any>;
114
+ TextControl: vitest.Mock<[], null>;
115
+ SelectControl: vitest.Mock<[], null>;
116
+ RangeControl: vitest.Mock<[], null>;
117
+ ToggleControl: vitest.Mock<[], null>;
118
+ Panel: vitest.Mock<[any], any>;
119
+ PanelBody: vitest.Mock<[any], any>;
120
+ PanelRow: vitest.Mock<[any], any>;
121
+ };
122
+ /**
123
+ * Mock @wordpress/block-editor
124
+ */
125
+ declare const mockWordPressBlockEditor: {
126
+ InspectorControls: vitest.Mock<[any], any>;
127
+ BlockControls: vitest.Mock<[any], any>;
128
+ useBlockProps: vitest.Mock<[props?: any], any>;
129
+ RichText: vitest.Mock<[], null>;
130
+ MediaUpload: vitest.Mock<[], null>;
131
+ MediaUploadCheck: vitest.Mock<[any], any>;
132
+ ColorPalette: vitest.Mock<[], null>;
133
+ URLInput: vitest.Mock<[], null>;
134
+ };
135
+ /**
136
+ * Mock @wordpress/element
137
+ */
138
+ declare const mockWordPressElement: {
139
+ createElement: vitest.Mock<[type: any, props?: any, ...children: any[]], {
140
+ type: any;
141
+ props: any;
142
+ }>;
143
+ Fragment: vitest.Mock<[any], any>;
144
+ useState: vitest.Mock<[initialState: any], any[]>;
145
+ useEffect: vitest.Mock<[_effect: () => void, _deps?: any[] | undefined], undefined>;
146
+ useRef: vitest.Mock<[initialValue: any], {
147
+ current: any;
148
+ }>;
149
+ useMemo: vitest.Mock<[factory: () => any, _deps?: any[] | undefined], any>;
150
+ useCallback: vitest.Mock<[callback: (...args: any[]) => any, _deps?: any[] | undefined], (...args: any[]) => any>;
151
+ };
152
+ /**
153
+ * Mock @wordpress/api-fetch
154
+ */
155
+ declare const mockWordPressApiFetch: vitest.Mock<[_options: {
156
+ path: string;
157
+ method?: string;
158
+ data?: any;
159
+ }], Promise<{
160
+ ok: boolean;
161
+ json: () => Promise<{}>;
162
+ }>>;
163
+ /**
164
+ * Setup all WordPress mocks
165
+ */
166
+ declare function setupWordPressMocks(): void;
167
+ /**
168
+ * Clear all WordPress mocks
169
+ */
170
+ declare function clearWordPressMocks(): void;
171
+
172
+ /**
173
+ * Custom Vitest Matchers
174
+ * WordPress-specific test matchers
175
+ */
176
+ /**
177
+ * Check if element has WordPress block class
178
+ */
179
+ declare function toHaveBlockClass(received: HTMLElement, blockName: string): {
180
+ pass: boolean;
181
+ message: () => string;
182
+ };
183
+ /**
184
+ * Check if block is registered
185
+ */
186
+ declare function toBeRegisteredBlock(received: string): {
187
+ pass: boolean;
188
+ message: () => string;
189
+ };
190
+ /**
191
+ * Check if block has required attributes
192
+ */
193
+ declare function toHaveBlockAttributes(received: any, expectedAttributes: string[]): {
194
+ pass: boolean;
195
+ message: () => string;
196
+ };
197
+ /**
198
+ * Check if element is a valid WordPress block
199
+ */
200
+ declare function toBeValidWordPressBlock(received: HTMLElement): {
201
+ pass: boolean;
202
+ message: () => "Expected element not to be a valid WordPress block" | "Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)";
203
+ };
204
+ /**
205
+ * Check if string is valid block markup
206
+ */
207
+ declare function toBeValidBlockMarkup(received: string): {
208
+ pass: boolean;
209
+ message: () => "Expected string not to be valid block markup" | "Expected string to be valid block markup (must include WordPress block comments)";
210
+ };
211
+ /**
212
+ * Extend Vitest matchers
213
+ */
214
+ declare function setupCustomMatchers(): void;
215
+ declare global {
216
+ namespace Vi {
217
+ interface Matchers<R = any> {
218
+ toHaveBlockClass(blockName: string): R;
219
+ toBeRegisteredBlock(): R;
220
+ toHaveBlockAttributes(attributes: string[]): R;
221
+ toBeValidWordPressBlock(): R;
222
+ toBeValidBlockMarkup(): R;
223
+ }
224
+ }
225
+ }
226
+
227
+ export { type BlockConfiguration, clearWordPressMocks, createMockAttributes, createMockBlockContext, mockWordPressApiFetch, mockWordPressBlockEditor, mockWordPressBlocks, mockWordPressComponents, mockWordPressData, mockWordPressElement, mockWordPressi18n, renderBlockEdit, renderBlockSave, setupCustomMatchers, setupWordPressMocks, testBlockAttributes, testBlockRegistration, testBlockSaveOutput, toBeRegisteredBlock, toBeValidBlockMarkup, toBeValidWordPressBlock, toHaveBlockAttributes, toHaveBlockClass, waitForBlockRender };
package/dist/index.js ADDED
@@ -0,0 +1,308 @@
1
+ // src/utils/block-testing.ts
2
+ import { render } from "@testing-library/react";
3
+ import { createElement } from "react";
4
+ function renderBlockEdit(EditComponent, props = {}, options) {
5
+ const defaultProps = {
6
+ attributes: {},
7
+ setAttributes: vi.fn(),
8
+ isSelected: false,
9
+ clientId: "test-block-id",
10
+ ...props
11
+ };
12
+ return render(createElement(EditComponent, defaultProps), options);
13
+ }
14
+ function renderBlockSave(SaveComponent, props = {}, options) {
15
+ const defaultProps = {
16
+ attributes: {},
17
+ ...props
18
+ };
19
+ return render(createElement(SaveComponent, defaultProps), options);
20
+ }
21
+ function testBlockRegistration(blockName, expectedConfig) {
22
+ const { getBlockType } = wp.blocks;
23
+ const blockType = getBlockType(blockName);
24
+ if (!blockType) {
25
+ throw new Error(`Block ${blockName} is not registered`);
26
+ }
27
+ expect(blockType.name).toBe(blockName);
28
+ if (expectedConfig.title) {
29
+ expect(blockType.title).toBe(expectedConfig.title);
30
+ }
31
+ if (expectedConfig.category) {
32
+ expect(blockType.category).toBe(expectedConfig.category);
33
+ }
34
+ if (expectedConfig.attributes) {
35
+ expect(blockType.attributes).toEqual(expectedConfig.attributes);
36
+ }
37
+ return blockType;
38
+ }
39
+ function createMockAttributes(blockType, overrides = {}) {
40
+ const defaults = {
41
+ "core/paragraph": {
42
+ content: "Test paragraph content",
43
+ align: "left"
44
+ },
45
+ "core/heading": {
46
+ content: "Test heading",
47
+ level: 2
48
+ },
49
+ "core/image": {
50
+ url: "https://example.com/image.jpg",
51
+ alt: "Test image",
52
+ id: 123
53
+ }
54
+ };
55
+ return {
56
+ ...defaults[blockType] || {},
57
+ ...overrides
58
+ };
59
+ }
60
+ function testBlockAttributes(EditComponent, attributes) {
61
+ const setAttributes = vi.fn();
62
+ const { rerender } = renderBlockEdit(EditComponent, {
63
+ attributes,
64
+ setAttributes
65
+ });
66
+ return {
67
+ setAttributes,
68
+ rerender: (newAttributes) => {
69
+ return rerender(
70
+ createElement(EditComponent, {
71
+ attributes: newAttributes,
72
+ setAttributes
73
+ })
74
+ );
75
+ }
76
+ };
77
+ }
78
+ function testBlockSaveOutput(SaveComponent, attributes) {
79
+ const { container } = renderBlockSave(SaveComponent, { attributes });
80
+ return container.innerHTML;
81
+ }
82
+ function createMockBlockContext(overrides = {}) {
83
+ return {
84
+ clientId: "mock-client-id",
85
+ isSelected: false,
86
+ isMultiSelected: false,
87
+ hasSelectedInnerBlock: false,
88
+ index: 0,
89
+ ...overrides
90
+ };
91
+ }
92
+ async function waitForBlockRender(callback, options = {}) {
93
+ const timeout = options.timeout || 1e3;
94
+ const startTime = Date.now();
95
+ while (Date.now() - startTime < timeout) {
96
+ try {
97
+ callback();
98
+ return;
99
+ } catch (error) {
100
+ await new Promise((resolve) => setTimeout(resolve, 50));
101
+ }
102
+ }
103
+ throw new Error("Block failed to render within timeout");
104
+ }
105
+
106
+ // src/mocks/wordpress.ts
107
+ import { vi as vi2 } from "vitest";
108
+ var mockWordPressBlocks = {
109
+ registerBlockType: vi2.fn((name, settings) => {
110
+ return { name, ...settings };
111
+ }),
112
+ unregisterBlockType: vi2.fn(),
113
+ getBlockType: vi2.fn((name) => ({
114
+ name,
115
+ title: "Mock Block",
116
+ category: "common",
117
+ icon: "block-default",
118
+ attributes: {}
119
+ })),
120
+ getBlockTypes: vi2.fn(() => []),
121
+ hasBlockSupport: vi2.fn(() => true),
122
+ createBlock: vi2.fn((name, attributes) => ({
123
+ name,
124
+ attributes: attributes || {},
125
+ clientId: "mock-client-id"
126
+ }))
127
+ };
128
+ var mockWordPressData = {
129
+ useSelect: vi2.fn((callback) => callback(() => ({}))),
130
+ useDispatch: vi2.fn((_storeName) => ({})),
131
+ select: vi2.fn((_storeName) => ({})),
132
+ dispatch: vi2.fn((_storeName) => ({})),
133
+ subscribe: vi2.fn(),
134
+ registerStore: vi2.fn()
135
+ };
136
+ var mockWordPressi18n = {
137
+ __: vi2.fn((text) => text),
138
+ _x: vi2.fn((text) => text),
139
+ _n: vi2.fn(
140
+ (single, plural, number) => number === 1 ? single : plural
141
+ ),
142
+ sprintf: vi2.fn((format, ..._args) => format),
143
+ setLocaleData: vi2.fn()
144
+ };
145
+ var mockWordPressComponents = {
146
+ Button: vi2.fn(({ children }) => children),
147
+ TextControl: vi2.fn(() => null),
148
+ SelectControl: vi2.fn(() => null),
149
+ RangeControl: vi2.fn(() => null),
150
+ ToggleControl: vi2.fn(() => null),
151
+ Panel: vi2.fn(({ children }) => children),
152
+ PanelBody: vi2.fn(({ children }) => children),
153
+ PanelRow: vi2.fn(({ children }) => children)
154
+ };
155
+ var mockWordPressBlockEditor = {
156
+ InspectorControls: vi2.fn(({ children }) => children),
157
+ BlockControls: vi2.fn(({ children }) => children),
158
+ useBlockProps: vi2.fn((props) => ({
159
+ className: "wp-block",
160
+ ...props
161
+ })),
162
+ RichText: vi2.fn(() => null),
163
+ MediaUpload: vi2.fn(() => null),
164
+ MediaUploadCheck: vi2.fn(({ children }) => children),
165
+ ColorPalette: vi2.fn(() => null),
166
+ URLInput: vi2.fn(() => null)
167
+ };
168
+ var mockWordPressElement = {
169
+ createElement: vi2.fn((type, props, ...children) => ({
170
+ type,
171
+ props: { ...props, children }
172
+ })),
173
+ Fragment: vi2.fn(({ children }) => children),
174
+ useState: vi2.fn((initialState) => [initialState, vi2.fn()]),
175
+ useEffect: vi2.fn((_effect, _deps) => void 0),
176
+ useRef: vi2.fn((initialValue) => ({ current: initialValue })),
177
+ useMemo: vi2.fn((factory, _deps) => factory()),
178
+ useCallback: vi2.fn((callback, _deps) => callback)
179
+ };
180
+ var mockWordPressApiFetch = vi2.fn(
181
+ (_options) => Promise.resolve({ ok: true, json: () => Promise.resolve({}) })
182
+ );
183
+ function setupWordPressMocks() {
184
+ vi2.mock("@wordpress/blocks", () => mockWordPressBlocks);
185
+ vi2.mock("@wordpress/data", () => mockWordPressData);
186
+ vi2.mock("@wordpress/i18n", () => mockWordPressi18n);
187
+ vi2.mock("@wordpress/components", () => mockWordPressComponents);
188
+ vi2.mock("@wordpress/block-editor", () => mockWordPressBlockEditor);
189
+ vi2.mock("@wordpress/element", () => mockWordPressElement);
190
+ vi2.mock("@wordpress/api-fetch", () => ({ default: mockWordPressApiFetch }));
191
+ global.wp = {
192
+ blocks: mockWordPressBlocks,
193
+ data: mockWordPressData,
194
+ i18n: mockWordPressi18n,
195
+ components: mockWordPressComponents,
196
+ blockEditor: mockWordPressBlockEditor,
197
+ element: mockWordPressElement,
198
+ apiFetch: mockWordPressApiFetch
199
+ };
200
+ }
201
+ function clearWordPressMocks() {
202
+ Object.values(mockWordPressBlocks).forEach((mock) => {
203
+ if (typeof mock?.mockClear === "function") {
204
+ mock.mockClear();
205
+ }
206
+ });
207
+ Object.values(mockWordPressData).forEach((mock) => {
208
+ if (typeof mock?.mockClear === "function") {
209
+ mock.mockClear();
210
+ }
211
+ });
212
+ }
213
+
214
+ // src/matchers/index.ts
215
+ import { expect as expect2 } from "vitest";
216
+ function toHaveBlockClass(received, blockName) {
217
+ const className = `wp-block-${blockName.replace("/", "-")}`;
218
+ const pass = received.classList.contains(className);
219
+ return {
220
+ pass,
221
+ message: () => pass ? `Expected element not to have block class ${className}` : `Expected element to have block class ${className}`
222
+ };
223
+ }
224
+ function toBeRegisteredBlock(received) {
225
+ const { getBlockType } = global.wp?.blocks || {};
226
+ if (!getBlockType) {
227
+ return {
228
+ pass: false,
229
+ message: () => "WordPress blocks API is not available"
230
+ };
231
+ }
232
+ const blockType = getBlockType(received);
233
+ const pass = blockType !== void 0;
234
+ return {
235
+ pass,
236
+ message: () => pass ? `Expected block ${received} not to be registered` : `Expected block ${received} to be registered`
237
+ };
238
+ }
239
+ function toHaveBlockAttributes(received, expectedAttributes) {
240
+ if (!received || !received.attributes) {
241
+ return {
242
+ pass: false,
243
+ message: () => "Block does not have attributes property"
244
+ };
245
+ }
246
+ const actualAttributes = Object.keys(received.attributes);
247
+ const missingAttributes = expectedAttributes.filter(
248
+ (attr) => !actualAttributes.includes(attr)
249
+ );
250
+ const pass = missingAttributes.length === 0;
251
+ return {
252
+ pass,
253
+ message: () => pass ? `Expected block not to have attributes: ${expectedAttributes.join(", ")}` : `Expected block to have attributes: ${missingAttributes.join(", ")}`
254
+ };
255
+ }
256
+ function toBeValidWordPressBlock(received) {
257
+ const hasDataType = received.hasAttribute("data-type");
258
+ const hasWpBlock = received.classList.contains("wp-block") || Array.from(received.classList).some((c) => c.startsWith("wp-block-"));
259
+ const pass = hasDataType && hasWpBlock;
260
+ return {
261
+ pass,
262
+ message: () => pass ? "Expected element not to be a valid WordPress block" : "Expected element to be a valid WordPress block (must have data-type attribute and wp-block class)"
263
+ };
264
+ }
265
+ function toBeValidBlockMarkup(received) {
266
+ const hasStartComment = received.includes("<!-- wp:");
267
+ const hasEndComment = received.includes("<!-- /wp:") || received.includes("/-->");
268
+ const pass = hasStartComment && hasEndComment;
269
+ return {
270
+ pass,
271
+ message: () => pass ? "Expected string not to be valid block markup" : "Expected string to be valid block markup (must include WordPress block comments)"
272
+ };
273
+ }
274
+ function setupCustomMatchers() {
275
+ expect2.extend({
276
+ toHaveBlockClass,
277
+ toBeRegisteredBlock,
278
+ toHaveBlockAttributes,
279
+ toBeValidWordPressBlock,
280
+ toBeValidBlockMarkup
281
+ });
282
+ }
283
+ export {
284
+ clearWordPressMocks,
285
+ createMockAttributes,
286
+ createMockBlockContext,
287
+ mockWordPressApiFetch,
288
+ mockWordPressBlockEditor,
289
+ mockWordPressBlocks,
290
+ mockWordPressComponents,
291
+ mockWordPressData,
292
+ mockWordPressElement,
293
+ mockWordPressi18n,
294
+ renderBlockEdit,
295
+ renderBlockSave,
296
+ setupCustomMatchers,
297
+ setupWordPressMocks,
298
+ testBlockAttributes,
299
+ testBlockRegistration,
300
+ testBlockSaveOutput,
301
+ toBeRegisteredBlock,
302
+ toBeValidBlockMarkup,
303
+ toBeValidWordPressBlock,
304
+ toHaveBlockAttributes,
305
+ toHaveBlockClass,
306
+ waitForBlockRender
307
+ };
308
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":";AAKA,SAAS,cAA6B;AACtC,SAAS,qBAAqB;AAoBvB,SAAS,gBACd,eACA,QAAa,CAAC,GACd,SAC2B;AAC3B,QAAM,eAAe;AAAA,IACnB,YAAY,CAAC;AAAA,IACb,eAAe,GAAG,GAAG;AAAA,IACrB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,GAAG;AAAA,EACL;AAEA,SAAO,OAAO,cAAc,eAAe,YAAY,GAAG,OAAO;AACnE;AAKO,SAAS,gBACd,eACA,QAAa,CAAC,GACd,SAC2B;AAC3B,QAAM,eAAe;AAAA,IACnB,YAAY,CAAC;AAAA,IACb,GAAG;AAAA,EACL;AAEA,SAAO,OAAO,cAAc,eAAe,YAAY,GAAG,OAAO;AACnE;AAKO,SAAS,sBACd,WACA,gBACA;AACA,QAAM,EAAE,aAAa,IAAI,GAAG;AAE5B,QAAM,YAAY,aAAa,SAAS;AAExC,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,SAAS,SAAS,oBAAoB;AAAA,EACxD;AAGA,SAAO,UAAU,IAAI,EAAE,KAAK,SAAS;AAErC,MAAI,eAAe,OAAO;AACxB,WAAO,UAAU,KAAK,EAAE,KAAK,eAAe,KAAK;AAAA,EACnD;AAEA,MAAI,eAAe,UAAU;AAC3B,WAAO,UAAU,QAAQ,EAAE,KAAK,eAAe,QAAQ;AAAA,EACzD;AAEA,MAAI,eAAe,YAAY;AAC7B,WAAO,UAAU,UAAU,EAAE,QAAQ,eAAe,UAAU;AAAA,EAChE;AAEA,SAAO;AACT;AAKO,SAAS,qBACd,WACA,YAAiC,CAAC,GACb;AACrB,QAAM,WAAgD;AAAA,IACpD,kBAAkB;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA,gBAAgB;AAAA,MACd,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA,cAAc;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AAAA,MACL,IAAI;AAAA,IACN;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAI,SAAS,SAAS,KAAK,CAAC;AAAA,IAC5B,GAAG;AAAA,EACL;AACF;AAKO,SAAS,oBACd,eACA,YACA;AACA,QAAM,gBAAgB,GAAG,GAAG;AAE5B,QAAM,EAAE,SAAS,IAAI,gBAAgB,eAAe;AAAA,IAClD;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,UAAU,CAAC,kBAAuC;AAChD,aAAO;AAAA,QACL,cAAc,eAAe;AAAA,UAC3B,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,oBACd,eACA,YACA;AACA,QAAM,EAAE,UAAU,IAAI,gBAAgB,eAAe,EAAE,WAAW,CAAC;AACnE,SAAO,UAAU;AACnB;AAKO,SAAS,uBAAuB,YAAiB,CAAC,GAAG;AAC1D,SAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,uBAAuB;AAAA,IACvB,OAAO;AAAA,IACP,GAAG;AAAA,EACL;AACF;AAKA,eAAsB,mBACpB,UACA,UAAgC,CAAC,GACjC;AACA,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,YAAY,KAAK,IAAI;AAE3B,SAAO,KAAK,IAAI,IAAI,YAAY,SAAS;AACvC,QAAI;AACF,eAAS;AACT;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,uCAAuC;AACzD;;;AC3LA,SAAS,MAAAA,WAAU;AAKZ,IAAM,sBAAsB;AAAA,EACjC,mBAAmBA,IAAG,GAAG,CAAC,MAAc,aAAkB;AACxD,WAAO,EAAE,MAAM,GAAG,SAAS;AAAA,EAC7B,CAAC;AAAA,EACD,qBAAqBA,IAAG,GAAG;AAAA,EAC3B,cAAcA,IAAG,GAAG,CAAC,UAAkB;AAAA,IACrC;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM;AAAA,IACN,YAAY,CAAC;AAAA,EACf,EAAE;AAAA,EACF,eAAeA,IAAG,GAAG,MAAM,CAAC,CAAC;AAAA,EAC7B,iBAAiBA,IAAG,GAAG,MAAM,IAAI;AAAA,EACjC,aAAaA,IAAG,GAAG,CAAC,MAAc,gBAAsB;AAAA,IACtD;AAAA,IACA,YAAY,cAAc,CAAC;AAAA,IAC3B,UAAU;AAAA,EACZ,EAAE;AACJ;AAKO,IAAM,oBAAoB;AAAA,EAC/B,WAAWA,IAAG,GAAG,CAAC,aAAkB,SAAS,OAAO,CAAC,EAAE,CAAC;AAAA,EACxD,aAAaA,IAAG,GAAG,CAAC,gBAAwB,CAAC,EAAE;AAAA,EAC/C,QAAQA,IAAG,GAAG,CAAC,gBAAwB,CAAC,EAAE;AAAA,EAC1C,UAAUA,IAAG,GAAG,CAAC,gBAAwB,CAAC,EAAE;AAAA,EAC5C,WAAWA,IAAG,GAAG;AAAA,EACjB,eAAeA,IAAG,GAAG;AACvB;AAKO,IAAM,oBAAoB;AAAA,EAC/B,IAAIA,IAAG,GAAG,CAAC,SAAiB,IAAI;AAAA,EAChC,IAAIA,IAAG,GAAG,CAAC,SAAiB,IAAI;AAAA,EAChC,IAAIA,IAAG;AAAA,IAAG,CAAC,QAAgB,QAAgB,WACzC,WAAW,IAAI,SAAS;AAAA,EAC1B;AAAA,EACA,SAASA,IAAG,GAAG,CAAC,WAAmB,UAAiB,MAAM;AAAA,EAC1D,eAAeA,IAAG,GAAG;AACvB;AAKO,IAAM,0BAA0B;AAAA,EACrC,QAAQA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EAC7C,aAAaA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC7B,eAAeA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC/B,cAAcA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC9B,eAAeA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC/B,OAAOA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EAC5C,WAAWA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EAChD,UAAUA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AACjD;AAKO,IAAM,2BAA2B;AAAA,EACtC,mBAAmBA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EACxD,eAAeA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EACpD,eAAeA,IAAG,GAAG,CAAC,WAAiB;AAAA,IACrC,WAAW;AAAA,IACX,GAAG;AAAA,EACL,EAAE;AAAA,EACF,UAAUA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC1B,aAAaA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC7B,kBAAkBA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EACvD,cAAcA,IAAG,GAAG,MAAM,IAAI;AAAA,EAC9B,UAAUA,IAAG,GAAG,MAAM,IAAI;AAC5B;AAKO,IAAM,uBAAuB;AAAA,EAClC,eAAeA,IAAG,GAAG,CAAC,MAAW,UAAgB,cAAqB;AAAA,IACpE;AAAA,IACA,OAAO,EAAE,GAAG,OAAO,SAAS;AAAA,EAC9B,EAAE;AAAA,EACF,UAAUA,IAAG,GAAG,CAAC,EAAE,SAAS,MAAW,QAAQ;AAAA,EAC/C,UAAUA,IAAG,GAAG,CAAC,iBAAsB,CAAC,cAAcA,IAAG,GAAG,CAAC,CAAC;AAAA,EAC9D,WAAWA,IAAG,GAAG,CAAC,SAAqB,UAAkB,MAAS;AAAA,EAClE,QAAQA,IAAG,GAAG,CAAC,kBAAuB,EAAE,SAAS,aAAa,EAAE;AAAA,EAChE,SAASA,IAAG,GAAG,CAAC,SAAoB,UAAkB,QAAQ,CAAC;AAAA,EAC/D,aAAaA,IAAG,GAAG,CAAC,UAAmC,UAAkB,QAAQ;AACnF;AAKO,IAAM,wBAAwBA,IAAG;AAAA,EACtC,CAAC,aACC,QAAQ,QAAQ,EAAE,IAAI,MAAM,MAAM,MAAM,QAAQ,QAAQ,CAAC,CAAC,EAAE,CAAC;AACjE;AAKO,SAAS,sBAAsB;AACpC,EAAAA,IAAG,KAAK,qBAAqB,MAAM,mBAAmB;AACtD,EAAAA,IAAG,KAAK,mBAAmB,MAAM,iBAAiB;AAClD,EAAAA,IAAG,KAAK,mBAAmB,MAAM,iBAAiB;AAClD,EAAAA,IAAG,KAAK,yBAAyB,MAAM,uBAAuB;AAC9D,EAAAA,IAAG,KAAK,2BAA2B,MAAM,wBAAwB;AACjE,EAAAA,IAAG,KAAK,sBAAsB,MAAM,oBAAoB;AACxD,EAAAA,IAAG,KAAK,wBAAwB,OAAO,EAAE,SAAS,sBAAsB,EAAE;AAGzE,EAAC,OAAe,KAAK;AAAA,IACpB,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AACF;AAKO,SAAS,sBAAsB;AACpC,SAAO,OAAO,mBAAmB,EAAE,QAAQ,CAAC,SAAc;AACxD,QAAI,OAAO,MAAM,cAAc,YAAY;AACzC,WAAK,UAAU;AAAA,IACjB;AAAA,EACF,CAAC;AAED,SAAO,OAAO,iBAAiB,EAAE,QAAQ,CAAC,SAAc;AACtD,QAAI,OAAO,MAAM,cAAc,YAAY;AACzC,WAAK,UAAU;AAAA,IACjB;AAAA,EACF,CAAC;AAGH;;;ACnJA,SAAS,UAAAC,eAAc;AAKhB,SAAS,iBAAiB,UAAuB,WAAmB;AACzE,QAAM,YAAY,YAAY,UAAU,QAAQ,KAAK,GAAG,CAAC;AACzD,QAAM,OAAO,SAAS,UAAU,SAAS,SAAS;AAElD,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MACP,OACI,4CAA4C,SAAS,KACrD,wCAAwC,SAAS;AAAA,EACzD;AACF;AAKO,SAAS,oBAAoB,UAAkB;AACpD,QAAM,EAAE,aAAa,IAAK,OAAe,IAAI,UAAU,CAAC;AAExD,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,YAAY,aAAa,QAAQ;AACvC,QAAM,OAAO,cAAc;AAE3B,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MACP,OACI,kBAAkB,QAAQ,0BAC1B,kBAAkB,QAAQ;AAAA,EAClC;AACF;AAKO,SAAS,sBACd,UACA,oBACA;AACA,MAAI,CAAC,YAAY,CAAC,SAAS,YAAY;AACrC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,mBAAmB,OAAO,KAAK,SAAS,UAAU;AACxD,QAAM,oBAAoB,mBAAmB;AAAA,IAC3C,CAAC,SAAS,CAAC,iBAAiB,SAAS,IAAI;AAAA,EAC3C;AAEA,QAAM,OAAO,kBAAkB,WAAW;AAE1C,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MACP,OACI,0CAA0C,mBAAmB,KAAK,IAAI,CAAC,KACvE,sCAAsC,kBAAkB,KAAK,IAAI,CAAC;AAAA,EAC1E;AACF;AAKO,SAAS,wBAAwB,UAAuB;AAC7D,QAAM,cAAc,SAAS,aAAa,WAAW;AACrD,QAAM,aAAa,SAAS,UAAU,SAAS,UAAU,KACtC,MAAM,KAAK,SAAS,SAAS,EAAE,KAAK,OAAK,EAAE,WAAW,WAAW,CAAC;AAErF,QAAM,OAAO,eAAe;AAE5B,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MACP,OACI,uDACA;AAAA,EACR;AACF;AAKO,SAAS,qBAAqB,UAAkB;AAErD,QAAM,kBAAkB,SAAS,SAAS,UAAU;AACpD,QAAM,gBAAgB,SAAS,SAAS,WAAW,KAAK,SAAS,SAAS,MAAM;AAEhF,QAAM,OAAO,mBAAmB;AAEhC,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MACP,OACI,iDACA;AAAA,EACR;AACF;AAKO,SAAS,sBAAsB;AACpC,EAAAA,QAAO,OAAO;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;","names":["vi","expect"]}
@@ -0,0 +1,98 @@
1
+ import { Page, Locator } from '@playwright/test';
2
+ export { Browser, BrowserContext, Locator, Page, expect, test } from '@playwright/test';
3
+
4
+ /**
5
+ * Playwright E2E Testing Helpers
6
+ * Helper functions for WordPress E2E testing with Playwright
7
+ */
8
+
9
+ /**
10
+ * WordPress login helper
11
+ */
12
+ declare function wpLogin(page: Page, username?: string, password?: string): Promise<void>;
13
+ /**
14
+ * WordPress logout helper
15
+ */
16
+ declare function wpLogout(page: Page): Promise<void>;
17
+ /**
18
+ * Navigate to block editor
19
+ */
20
+ declare function openBlockEditor(page: Page, postType?: string): Promise<void>;
21
+ /**
22
+ * Insert a block in the editor
23
+ */
24
+ declare function insertBlock(page: Page, blockName: string): Promise<void>;
25
+ /**
26
+ * Get block by type
27
+ */
28
+ declare function getBlock(page: Page, blockType: string): Locator;
29
+ /**
30
+ * Get selected block
31
+ */
32
+ declare function getSelectedBlock(page: Page): Locator;
33
+ /**
34
+ * Type in block
35
+ */
36
+ declare function typeInBlock(page: Page, blockType: string, text: string): Promise<void>;
37
+ /**
38
+ * Update block attribute via inspector
39
+ */
40
+ declare function updateBlockAttribute(page: Page, attributeName: string, value: string | number | boolean): Promise<void>;
41
+ /**
42
+ * Publish post
43
+ */
44
+ declare function publishPost(page: Page): Promise<void>;
45
+ /**
46
+ * Save draft
47
+ */
48
+ declare function saveDraft(page: Page): Promise<void>;
49
+ /**
50
+ * Preview post
51
+ */
52
+ declare function previewPost(page: Page): Promise<Page>;
53
+ /**
54
+ * Check if block exists
55
+ */
56
+ declare function blockExists(page: Page, blockType: string): Promise<boolean>;
57
+ /**
58
+ * Get block count
59
+ */
60
+ declare function getBlockCount(page: Page, blockType?: string): Promise<number>;
61
+ /**
62
+ * Select block
63
+ */
64
+ declare function selectBlock(page: Page, blockType: string): Promise<void>;
65
+ /**
66
+ * Delete block
67
+ */
68
+ declare function deleteBlock(page: Page, blockType: string): Promise<void>;
69
+ /**
70
+ * Move block up
71
+ */
72
+ declare function moveBlockUp(page: Page, blockType: string): Promise<void>;
73
+ /**
74
+ * Move block down
75
+ */
76
+ declare function moveBlockDown(page: Page, blockType: string): Promise<void>;
77
+ /**
78
+ * Wait for WordPress admin
79
+ */
80
+ declare function waitForWPAdmin(page: Page): Promise<void>;
81
+ /**
82
+ * Navigate to WordPress admin page
83
+ */
84
+ declare function goToWPAdmin(page: Page, path?: string): Promise<void>;
85
+ /**
86
+ * Check if user is logged in
87
+ */
88
+ declare function isLoggedIn(page: Page): Promise<boolean>;
89
+ /**
90
+ * Take accessible screenshot
91
+ */
92
+ declare function takeA11yScreenshot(page: Page, name: string): Promise<void>;
93
+ /**
94
+ * Check for console errors
95
+ */
96
+ declare function setupConsoleErrorTracking(page: Page): string[];
97
+
98
+ export { blockExists, deleteBlock, getBlock, getBlockCount, getSelectedBlock, goToWPAdmin, insertBlock, isLoggedIn, moveBlockDown, moveBlockUp, openBlockEditor, previewPost, publishPost, saveDraft, selectBlock, setupConsoleErrorTracking, takeA11yScreenshot, typeInBlock, updateBlockAttribute, waitForWPAdmin, wpLogin, wpLogout };