@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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2024 Jon Imms
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ ---
20
+
21
+ For the full GPL-3.0 license text, see: https://www.gnu.org/licenses/gpl-3.0.txt
package/README.md ADDED
@@ -0,0 +1,579 @@
1
+ # @stratawp/testing
2
+
3
+ Comprehensive testing utilities for StrataWP WordPress themes. Includes unit testing with Vitest, E2E testing with Playwright, WordPress mocks, and custom matchers.
4
+
5
+ ## Features
6
+
7
+ - **Unit Testing**: Vitest with WordPress mocks and utilities
8
+ - **E2E Testing**: Playwright for full user workflows
9
+ - **WordPress Mocks**: Complete mocks for WordPress JavaScript APIs
10
+ - **Custom Matchers**: WordPress-specific test assertions
11
+ - **Coverage Reporting**: Built-in code coverage with thresholds
12
+ - **Testing Utilities**: Block testing helpers and utilities
13
+ - **CI/CD Ready**: Pre-configured for GitHub Actions
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add -D @stratawp/testing
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Unit Testing with Vitest
24
+
25
+ Create a test file (`__tests__/my-block.test.tsx`):
26
+
27
+ ```typescript
28
+ import { describe, it, expect, beforeAll } from 'vitest'
29
+ import {
30
+ renderBlockEdit,
31
+ testBlockRegistration,
32
+ setupWordPressMocks,
33
+ setupCustomMatchers,
34
+ } from '@stratawp/testing/vitest'
35
+
36
+ beforeAll(() => {
37
+ setupWordPressMocks()
38
+ setupCustomMatchers()
39
+ })
40
+
41
+ describe('My Block', () => {
42
+ it('should register correctly', () => {
43
+ testBlockRegistration('my-theme/my-block', {
44
+ title: 'My Block',
45
+ category: 'common',
46
+ })
47
+ })
48
+
49
+ it('should render edit component', () => {
50
+ const EditComponent = ({ attributes }: any) => (
51
+ <div className="wp-block-my-theme-my-block">
52
+ {attributes.content}
53
+ </div>
54
+ )
55
+
56
+ const { getByText } = renderBlockEdit(EditComponent, {
57
+ attributes: { content: 'Hello World' },
58
+ })
59
+
60
+ expect(getByText('Hello World')).toBeInTheDocument()
61
+ })
62
+ })
63
+ ```
64
+
65
+ Run tests:
66
+ ```bash
67
+ pnpm test
68
+ ```
69
+
70
+ ### E2E Testing with Playwright
71
+
72
+ Create an E2E test (`e2e/my-block.spec.ts`):
73
+
74
+ ```typescript
75
+ import {
76
+ test,
77
+ expect,
78
+ wpLogin,
79
+ openBlockEditor,
80
+ insertBlock,
81
+ publishPost,
82
+ } from '@stratawp/testing/playwright'
83
+
84
+ test.describe('My Block E2E', () => {
85
+ test.beforeEach(async ({ page }) => {
86
+ await wpLogin(page)
87
+ })
88
+
89
+ test('should insert and publish block', async ({ page }) => {
90
+ await openBlockEditor(page, 'post')
91
+ await insertBlock(page, 'My Block')
92
+
93
+ // Interact with your block
94
+ await page.fill('[data-type="my-theme/my-block"] input', 'Test content')
95
+
96
+ await publishPost(page)
97
+
98
+ await expect(page.locator('.components-snackbar'))
99
+ .toContainText('published')
100
+ })
101
+ })
102
+ ```
103
+
104
+ Run E2E tests:
105
+ ```bash
106
+ pnpm test:e2e
107
+ ```
108
+
109
+ ## Unit Testing
110
+
111
+ ### WordPress Mocks
112
+
113
+ The package includes comprehensive mocks for WordPress JavaScript APIs:
114
+
115
+ ```typescript
116
+ import { setupWordPressMocks } from '@stratawp/testing/vitest'
117
+
118
+ beforeAll(() => {
119
+ setupWordPressMocks()
120
+ })
121
+ ```
122
+
123
+ **Mocked APIs:**
124
+ - `@wordpress/blocks` - Block registration and manipulation
125
+ - `@wordpress/data` - Data stores and state management
126
+ - `@wordpress/i18n` - Internationalization functions
127
+ - `@wordpress/components` - UI components
128
+ - `@wordpress/block-editor` - Block editor components
129
+ - `@wordpress/element` - React utilities
130
+ - `@wordpress/api-fetch` - REST API requests
131
+
132
+ ### Block Testing Utilities
133
+
134
+ #### renderBlockEdit()
135
+
136
+ Render a block's edit component:
137
+
138
+ ```typescript
139
+ import { renderBlockEdit } from '@stratawp/testing/vitest'
140
+
141
+ const { getByText, getByRole } = renderBlockEdit(EditComponent, {
142
+ attributes: { content: 'Test' },
143
+ setAttributes: vi.fn(),
144
+ })
145
+ ```
146
+
147
+ #### renderBlockSave()
148
+
149
+ Render a block's save component:
150
+
151
+ ```typescript
152
+ import { renderBlockSave } from '@stratawp/testing/vitest'
153
+
154
+ const { container } = renderBlockSave(SaveComponent, {
155
+ attributes: { content: 'Test' },
156
+ })
157
+ ```
158
+
159
+ #### testBlockRegistration()
160
+
161
+ Test if a block is registered correctly:
162
+
163
+ ```typescript
164
+ import { testBlockRegistration } from '@stratawp/testing/vitest'
165
+
166
+ testBlockRegistration('my-theme/my-block', {
167
+ title: 'My Block',
168
+ category: 'design',
169
+ attributes: {
170
+ content: { type: 'string', default: '' },
171
+ },
172
+ })
173
+ ```
174
+
175
+ #### createMockAttributes()
176
+
177
+ Create mock block attributes:
178
+
179
+ ```typescript
180
+ import { createMockAttributes } from '@stratawp/testing/vitest'
181
+
182
+ const attributes = createMockAttributes('core/paragraph', {
183
+ content: 'Custom content',
184
+ })
185
+ ```
186
+
187
+ ### Custom Matchers
188
+
189
+ WordPress-specific test assertions:
190
+
191
+ ```typescript
192
+ import { setupCustomMatchers } from '@stratawp/testing/vitest'
193
+
194
+ beforeAll(() => {
195
+ setupCustomMatchers()
196
+ })
197
+
198
+ // Check if element has block class
199
+ expect(element).toHaveBlockClass('my-theme/my-block')
200
+
201
+ // Check if block is registered
202
+ expect('my-theme/my-block').toBeRegisteredBlock()
203
+
204
+ // Check if block has attributes
205
+ expect(blockType).toHaveBlockAttributes(['content', 'align'])
206
+
207
+ // Check if element is valid WordPress block
208
+ expect(element).toBeValidWordPressBlock()
209
+
210
+ // Check if string is valid block markup
211
+ expect(markup).toBeValidBlockMarkup()
212
+ ```
213
+
214
+ ## E2E Testing
215
+
216
+ ### WordPress Helpers
217
+
218
+ #### wpLogin()
219
+
220
+ Login to WordPress:
221
+
222
+ ```typescript
223
+ import { wpLogin } from '@stratawp/testing/playwright'
224
+
225
+ await wpLogin(page, 'admin', 'password')
226
+ ```
227
+
228
+ #### openBlockEditor()
229
+
230
+ Open the block editor:
231
+
232
+ ```typescript
233
+ import { openBlockEditor } from '@stratawp/testing/playwright'
234
+
235
+ await openBlockEditor(page, 'post') // or 'page', 'custom-post-type'
236
+ ```
237
+
238
+ #### insertBlock()
239
+
240
+ Insert a block:
241
+
242
+ ```typescript
243
+ import { insertBlock } from '@stratawp/testing/playwright'
244
+
245
+ await insertBlock(page, 'Paragraph')
246
+ await insertBlock(page, 'My Custom Block')
247
+ ```
248
+
249
+ #### publishPost()
250
+
251
+ Publish the current post:
252
+
253
+ ```typescript
254
+ import { publishPost } from '@stratawp/testing/playwright'
255
+
256
+ await publishPost(page)
257
+ ```
258
+
259
+ #### previewPost()
260
+
261
+ Preview the post in a new tab:
262
+
263
+ ```typescript
264
+ import { previewPost } from '@stratawp/testing/playwright'
265
+
266
+ const previewPage = await previewPost(page)
267
+ await expect(previewPage.locator('h1')).toContainText('My Heading')
268
+ await previewPage.close()
269
+ ```
270
+
271
+ ### Block Interaction
272
+
273
+ #### selectBlock()
274
+
275
+ Select a specific block:
276
+
277
+ ```typescript
278
+ import { selectBlock } from '@stratawp/testing/playwright'
279
+
280
+ await selectBlock(page, 'core/paragraph')
281
+ ```
282
+
283
+ #### deleteBlock()
284
+
285
+ Delete a block:
286
+
287
+ ```typescript
288
+ import { deleteBlock } from '@stratawp/testing/playwright'
289
+
290
+ await deleteBlock(page, 'core/paragraph')
291
+ ```
292
+
293
+ #### moveBlockUp() / moveBlockDown()
294
+
295
+ Move blocks:
296
+
297
+ ```typescript
298
+ import { moveBlockUp, moveBlockDown } from '@stratawp/testing/playwright'
299
+
300
+ await moveBlockUp(page, 'core/paragraph')
301
+ await moveBlockDown(page, 'core/heading')
302
+ ```
303
+
304
+ #### updateBlockAttribute()
305
+
306
+ Update a block attribute via inspector:
307
+
308
+ ```typescript
309
+ import { updateBlockAttribute } from '@stratawp/testing/playwright'
310
+
311
+ await updateBlockAttribute(page, 'Alignment', 'center')
312
+ ```
313
+
314
+ ### Utilities
315
+
316
+ #### setupConsoleErrorTracking()
317
+
318
+ Track console errors during tests:
319
+
320
+ ```typescript
321
+ import { setupConsoleErrorTracking } from '@stratawp/testing/playwright'
322
+
323
+ test('should not have console errors', async ({ page }) => {
324
+ const errors = setupConsoleErrorTracking(page)
325
+
326
+ // Run your test...
327
+
328
+ expect(errors.length).toBe(0)
329
+ })
330
+ ```
331
+
332
+ ## Configuration
333
+
334
+ ### Vitest Configuration
335
+
336
+ Create `vitest.config.ts` in your theme:
337
+
338
+ ```typescript
339
+ import { defineConfig } from 'vitest/config'
340
+ import { vitestConfig } from '@stratawp/testing/vitest'
341
+
342
+ export default defineConfig({
343
+ ...vitestConfig,
344
+ test: {
345
+ ...vitestConfig.test,
346
+ // Your custom test configuration
347
+ },
348
+ })
349
+ ```
350
+
351
+ ### Playwright Configuration
352
+
353
+ Create `playwright.config.ts` in your theme:
354
+
355
+ ```typescript
356
+ import { defineConfig } from '@playwright/test'
357
+ import { playwrightConfig } from '@stratawp/testing/playwright'
358
+
359
+ export default defineConfig({
360
+ ...playwrightConfig,
361
+ use: {
362
+ ...playwrightConfig.use,
363
+ baseURL: 'http://localhost:8888', // Your WordPress URL
364
+ },
365
+ })
366
+ ```
367
+
368
+ ## Code Coverage
369
+
370
+ Run tests with coverage:
371
+
372
+ ```bash
373
+ pnpm test:coverage
374
+ ```
375
+
376
+ **Coverage Thresholds:**
377
+ - Lines: 80%
378
+ - Functions: 80%
379
+ - Branches: 75%
380
+ - Statements: 80%
381
+
382
+ View coverage report:
383
+ ```bash
384
+ open coverage/index.html
385
+ ```
386
+
387
+ ## Best Practices
388
+
389
+ ### Unit Tests
390
+
391
+ 1. **Mock WordPress APIs**
392
+ ```typescript
393
+ beforeAll(() => {
394
+ setupWordPressMocks()
395
+ setupCustomMatchers()
396
+ })
397
+ ```
398
+
399
+ 2. **Test Block Registration**
400
+ ```typescript
401
+ it('should register with correct configuration', () => {
402
+ testBlockRegistration('my-theme/my-block', expectedConfig)
403
+ })
404
+ ```
405
+
406
+ 3. **Test Component Rendering**
407
+ ```typescript
408
+ it('should render with attributes', () => {
409
+ const { getByText } = renderBlockEdit(EditComponent, {
410
+ attributes: { content: 'Test' },
411
+ })
412
+ expect(getByText('Test')).toBeInTheDocument()
413
+ })
414
+ ```
415
+
416
+ 4. **Test Attribute Updates**
417
+ ```typescript
418
+ it('should update attributes', () => {
419
+ const setAttributes = vi.fn()
420
+ const { getByRole } = renderBlockEdit(EditComponent, { setAttributes })
421
+
422
+ userEvent.click(getByRole('button'))
423
+ expect(setAttributes).toHaveBeenCalledWith({ clicked: true })
424
+ })
425
+ ```
426
+
427
+ ### E2E Tests
428
+
429
+ 1. **Login Before Each Test**
430
+ ```typescript
431
+ test.beforeEach(async ({ page }) => {
432
+ await wpLogin(page)
433
+ })
434
+ ```
435
+
436
+ 2. **Use Descriptive Test Names**
437
+ ```typescript
438
+ test('should insert heading block and change level to H3', async ({ page }) => {
439
+ // ...
440
+ })
441
+ ```
442
+
443
+ 3. **Wait for Network Idle**
444
+ ```typescript
445
+ await page.waitForLoadState('networkidle')
446
+ ```
447
+
448
+ 4. **Take Screenshots on Failure**
449
+ ```typescript
450
+ test('visual test', async ({ page }) => {
451
+ await page.screenshot({ path: 'screenshots/my-block.png' })
452
+ })
453
+ ```
454
+
455
+ 5. **Test User Workflows**
456
+ ```typescript
457
+ test('complete blog post creation workflow', async ({ page }) => {
458
+ await openBlockEditor(page, 'post')
459
+ await insertBlock(page, 'Heading')
460
+ await page.keyboard.type('My Post Title')
461
+ await insertBlock(page, 'Paragraph')
462
+ await page.keyboard.type('Post content...')
463
+ await publishPost(page)
464
+ // Verify post is published
465
+ })
466
+ ```
467
+
468
+ ## CI/CD Integration
469
+
470
+ ### GitHub Actions
471
+
472
+ Create `.github/workflows/test.yml`:
473
+
474
+ ```yaml
475
+ name: Test
476
+
477
+ on: [push, pull_request]
478
+
479
+ jobs:
480
+ test:
481
+ runs-on: ubuntu-latest
482
+
483
+ steps:
484
+ - uses: actions/checkout@v3
485
+
486
+ - uses: pnpm/action-setup@v2
487
+ with:
488
+ version: 8
489
+
490
+ - uses: actions/setup-node@v3
491
+ with:
492
+ node-version: 18
493
+ cache: 'pnpm'
494
+
495
+ - name: Install dependencies
496
+ run: pnpm install
497
+
498
+ - name: Run unit tests
499
+ run: pnpm test:coverage
500
+
501
+ - name: Install Playwright
502
+ run: pnpm exec playwright install --with-deps
503
+
504
+ - name: Run E2E tests
505
+ run: pnpm test:e2e
506
+
507
+ - name: Upload coverage
508
+ uses: codecov/codecov-action@v3
509
+ with:
510
+ files: ./coverage/lcov.info
511
+ ```
512
+
513
+ ## Examples
514
+
515
+ See the `examples/basic-theme/__tests__` and `examples/basic-theme/e2e` directories for complete examples.
516
+
517
+ ## Troubleshooting
518
+
519
+ ### WordPress API Not Mocked
520
+
521
+ **Problem:** Tests fail with "wp is not defined"
522
+
523
+ **Solution:** Call `setupWordPressMocks()` before your tests:
524
+ ```typescript
525
+ beforeAll(() => {
526
+ setupWordPressMocks()
527
+ })
528
+ ```
529
+
530
+ ### Custom Matchers Not Working
531
+
532
+ **Problem:** Custom matchers like `toHaveBlockClass` are not recognized
533
+
534
+ **Solution:** Call `setupCustomMatchers()`:
535
+ ```typescript
536
+ beforeAll(() => {
537
+ setupCustomMatchers()
538
+ })
539
+ ```
540
+
541
+ ### Playwright Tests Timeout
542
+
543
+ **Problem:** E2E tests timeout
544
+
545
+ **Solution:** Increase timeout in `playwright.config.ts`:
546
+ ```typescript
547
+ export default defineConfig({
548
+ timeout: 60 * 1000, // 60 seconds
549
+ })
550
+ ```
551
+
552
+ ### Block Editor Not Loading
553
+
554
+ **Problem:** Block editor doesn't load in E2E tests
555
+
556
+ **Solution:** Ensure WordPress is running and the URL is correct:
557
+ ```typescript
558
+ use: {
559
+ baseURL: process.env.WP_BASE_URL || 'http://localhost:8888',
560
+ }
561
+ ```
562
+
563
+ ## Contributing
564
+
565
+ Contributions are welcome! Please see the [main StrataWP repository](https://github.com/JonImmsWordpressDev/StrataWP) for contribution guidelines.
566
+
567
+ ## License
568
+
569
+ GPL-3.0-or-later
570
+
571
+ ## Support
572
+
573
+ - **Issues**: https://github.com/JonImmsWordpressDev/StrataWP/issues
574
+ - **Discussions**: https://github.com/JonImmsWordpressDev/StrataWP/discussions
575
+ - **Documentation**: https://github.com/JonImmsWordpressDev/StrataWP#readme
576
+
577
+ ---
578
+
579
+ **Test with Confidence!** Write comprehensive tests for your WordPress themes with StrataWP testing utilities.