@times-design-system/components-wordpress 1.3.0 → 1.5.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/BLOCK_CREATION_CHECKLIST.md +124 -12
- package/CHANGELOG.md +12 -0
- package/README.md +57 -40
- package/TESTING.md +152 -0
- package/dist/blocks/dialog-box/block.json +24 -0
- package/dist/blocks/dialog-box/edit.js +41 -0
- package/dist/blocks/dialog-box/index.js +17 -0
- package/dist/blocks/dialog-box/render.php +24 -0
- package/dist/blocks/dialog-box/save.js +23 -0
- package/dist/blocks/dialog-box/style.css +23 -0
- package/dist/blocks/icon/block.json +24 -0
- package/dist/blocks/icon/edit.js +41 -0
- package/dist/blocks/icon/index.js +17 -0
- package/dist/blocks/icon/render.php +24 -0
- package/dist/blocks/icon/save.js +23 -0
- package/dist/blocks/icon/style.css +23 -0
- package/dist/blocks/input-helper-message/block.json +24 -0
- package/dist/blocks/input-helper-message/edit.js +42 -0
- package/dist/blocks/input-helper-message/index.js +17 -0
- package/dist/blocks/input-helper-message/render.php +24 -0
- package/dist/blocks/input-helper-message/save.js +23 -0
- package/dist/blocks/input-helper-message/style.css +23 -0
- package/dist/blocks/tab/block.json +24 -0
- package/dist/blocks/tab/edit.js +41 -0
- package/dist/blocks/tab/index.js +17 -0
- package/dist/blocks/tab/render.php +24 -0
- package/dist/blocks/tab/save.js +23 -0
- package/dist/blocks/tab/style.css +23 -0
- package/dist/blocks/tab-group/block.json +24 -0
- package/dist/blocks/tab-group/edit.js +41 -0
- package/dist/blocks/tab-group/index.js +17 -0
- package/dist/blocks/tab-group/render.php +24 -0
- package/dist/blocks/tab-group/save.js +23 -0
- package/dist/blocks/tab-group/style.css +23 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.setup.d.ts +1 -0
- package/package.json +21 -5
- package/scripts/create-wordpress-block-tests.cjs +438 -0
- package/scripts/create-wordpress-block.cjs +681 -0
- package/src/blocks/dialog-box/block.json +24 -0
- package/src/blocks/dialog-box/edit.js +41 -0
- package/src/blocks/dialog-box/index.js +17 -0
- package/src/blocks/dialog-box/render.php +24 -0
- package/src/blocks/dialog-box/save.js +23 -0
- package/src/blocks/dialog-box/style.css +23 -0
- package/src/blocks/icon/block.json +24 -0
- package/src/blocks/icon/edit.js +41 -0
- package/src/blocks/icon/index.js +17 -0
- package/src/blocks/icon/render.php +24 -0
- package/src/blocks/icon/save.js +23 -0
- package/src/blocks/icon/style.css +23 -0
- package/src/blocks/input-helper-message/block.json +24 -0
- package/src/blocks/input-helper-message/edit.js +42 -0
- package/src/blocks/input-helper-message/index.js +17 -0
- package/src/blocks/input-helper-message/render.php +24 -0
- package/src/blocks/input-helper-message/save.js +23 -0
- package/src/blocks/input-helper-message/style.css +23 -0
- package/src/blocks/tab/block.json +24 -0
- package/src/blocks/tab/edit.js +41 -0
- package/src/blocks/tab/index.js +17 -0
- package/src/blocks/tab/render.php +24 -0
- package/src/blocks/tab/save.js +23 -0
- package/src/blocks/tab/style.css +23 -0
- package/src/blocks/tab-group/block.json +24 -0
- package/src/blocks/tab-group/edit.js +41 -0
- package/src/blocks/tab-group/index.js +17 -0
- package/src/blocks/tab-group/render.php +24 -0
- package/src/blocks/tab-group/save.js +23 -0
- package/src/blocks/tab-group/style.css +23 -0
- package/vitest.config.js +28 -0
- package/vitest.config.ts +28 -0
- package/vitest.setup.ts +129 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@times-design-system/components-wordpress",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Times Design System Gutenberg blocks for WordPress Full Site Editing",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,17 @@
|
|
|
38
38
|
"clean": "rimraf dist",
|
|
39
39
|
"dev": "rollup -c --watch",
|
|
40
40
|
"prepublishOnly": "npm run build",
|
|
41
|
-
"test": "
|
|
41
|
+
"test": "vitest --run",
|
|
42
|
+
"test:coverage": "vitest --run --coverage",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:ui": "vitest --ui",
|
|
45
|
+
"block:create": "node scripts/create-wordpress-block.cjs",
|
|
46
|
+
"block:create:all": "node scripts/create-wordpress-block.cjs --all",
|
|
47
|
+
"block:test": "node scripts/create-wordpress-block-tests.cjs",
|
|
48
|
+
"block:checklist": "node scripts/create-wordpress-block.cjs --checklist",
|
|
49
|
+
"block:checklist:all": "node scripts/create-wordpress-block.cjs --checklist:all",
|
|
50
|
+
"block:status": "node scripts/create-wordpress-block.cjs --status",
|
|
51
|
+
"block:list": "node scripts/create-wordpress-block.cjs --list"
|
|
42
52
|
},
|
|
43
53
|
"bugs": {
|
|
44
54
|
"url": "https://github.com/newsuk/times-design-system/issues"
|
|
@@ -48,7 +58,7 @@
|
|
|
48
58
|
"access": "public"
|
|
49
59
|
},
|
|
50
60
|
"dependencies": {
|
|
51
|
-
"@times-design-system/theme-scss": "^2.
|
|
61
|
+
"@times-design-system/theme-scss": "^2.3.0",
|
|
52
62
|
"@wordpress/block-editor": "^12.0.0",
|
|
53
63
|
"@wordpress/blocks": "^12.0.0",
|
|
54
64
|
"@wordpress/components": "^25.0.0",
|
|
@@ -63,13 +73,19 @@
|
|
|
63
73
|
"@rollup/plugin-json": "^6.0.0",
|
|
64
74
|
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
65
75
|
"@rollup/plugin-typescript": "^11.0.0",
|
|
76
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
77
|
+
"@testing-library/react": "^14.1.2",
|
|
78
|
+
"@testing-library/user-event": "^14.5.1",
|
|
66
79
|
"@types/react": "19.2.5",
|
|
80
|
+
"@vitest/ui": "^1.0.4",
|
|
81
|
+
"jsdom": "^23.0.0",
|
|
67
82
|
"postcss": "^8.4.31",
|
|
68
83
|
"rimraf": "^5.0.0",
|
|
69
84
|
"rollup": "^3.0.0",
|
|
70
85
|
"rollup-plugin-copy": "^3.4.0",
|
|
71
86
|
"rollup-plugin-postcss": "^4.0.2",
|
|
72
|
-
"typescript": "^5.0.0"
|
|
87
|
+
"typescript": "^5.0.0",
|
|
88
|
+
"vitest": "^1.0.4"
|
|
73
89
|
},
|
|
74
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "f57ef06b52f90bc4c57a8e0cefd9be6c54b8f7bf"
|
|
75
91
|
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WordPress Block Test Generator
|
|
5
|
+
*
|
|
6
|
+
* Generates test files for WordPress blocks based on component patterns
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node scripts/create-wordpress-block-tests.cjs ComponentName
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// Colors for console output
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
bright: '\x1b[1m',
|
|
19
|
+
dim: '\x1b[2m',
|
|
20
|
+
green: '\x1b[32m',
|
|
21
|
+
yellow: '\x1b[33m',
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function log(message, color = 'reset') {
|
|
27
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function componentToBlockName(componentName) {
|
|
31
|
+
return componentName
|
|
32
|
+
.replace(/([A-Z])/g, '-$1')
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/^-/, '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Template for basic component test
|
|
39
|
+
*/
|
|
40
|
+
function generateBasicTest(componentName, blockName) {
|
|
41
|
+
return `import { describe, it, expect } from 'vitest';
|
|
42
|
+
import { render } from '@testing-library/react';
|
|
43
|
+
import '@testing-library/jest-dom';
|
|
44
|
+
import React from 'react';
|
|
45
|
+
|
|
46
|
+
// Simplified Save component for testing
|
|
47
|
+
const ${componentName}Save = ({ attributes }) => {
|
|
48
|
+
const {} = attributes || {};
|
|
49
|
+
|
|
50
|
+
return React.createElement(
|
|
51
|
+
'div',
|
|
52
|
+
{ className: 'wp-block tds-${blockName}-wrapper' },
|
|
53
|
+
React.createElement('div', { className: 'tds-${blockName}' },
|
|
54
|
+
'TODO: Implement component rendering'
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
describe('${componentName} Block', () => {
|
|
60
|
+
describe('Basic Rendering', () => {
|
|
61
|
+
it('should render wrapper element', () => {
|
|
62
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
63
|
+
expect(container.querySelector('.tds-${blockName}-wrapper')).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should render component element', () => {
|
|
67
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
68
|
+
expect(container.querySelector('.tds-${blockName}')).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should render with no attributes', () => {
|
|
72
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
73
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('Attribute Handling', () => {
|
|
78
|
+
it('should handle empty attributes gracefully', () => {
|
|
79
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
80
|
+
expect(container.querySelector('.tds-${blockName}')).toBeInTheDocument();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should handle missing attributes object', () => {
|
|
84
|
+
const { container } = render(React.createElement(${componentName}Save, {}));
|
|
85
|
+
expect(container.querySelector('.tds-${blockName}')).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Accessibility', () => {
|
|
90
|
+
it('should render semantic HTML', () => {
|
|
91
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
92
|
+
expect(container.querySelector('.tds-${blockName}')).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// TODO: Add variant tests based on component props
|
|
97
|
+
// Reference: packages/components-react/src/${componentName}/${componentName}.tsx
|
|
98
|
+
// Patterns to implement:
|
|
99
|
+
// - Enum variants (intent, size, etc.)
|
|
100
|
+
// - Boolean flags (disabled, channel, etc.)
|
|
101
|
+
// - String content (label, content, etc.)
|
|
102
|
+
});
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Template for button-like components (with intent, size, behaviour, state)
|
|
108
|
+
*/
|
|
109
|
+
function generateButtonLikeTest(componentName, blockName) {
|
|
110
|
+
return `import { describe, it, expect, vi } from 'vitest';
|
|
111
|
+
import { render } from '@testing-library/react';
|
|
112
|
+
import '@testing-library/jest-dom';
|
|
113
|
+
import React from 'react';
|
|
114
|
+
|
|
115
|
+
// Mock the class builder
|
|
116
|
+
vi.mock('../../src/utils/classBuilder.js', () => ({
|
|
117
|
+
build${componentName}Class: ({ intent = 'primary', size = 'medium', disabled = false }) => {
|
|
118
|
+
const classes = [
|
|
119
|
+
'tds-${blockName}',
|
|
120
|
+
\`tds-${blockName}--intent-\${intent}\`,
|
|
121
|
+
\`tds-${blockName}--size-\${size}\`,
|
|
122
|
+
];
|
|
123
|
+
if (disabled) classes.push('tds-${blockName}--disabled');
|
|
124
|
+
return classes.join(' ');
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
// Simplified Save component for testing
|
|
129
|
+
const ${componentName}Save = ({ attributes }) => {
|
|
130
|
+
const {
|
|
131
|
+
label = '${componentName}',
|
|
132
|
+
intent = 'primary',
|
|
133
|
+
size = 'medium',
|
|
134
|
+
disabled = false,
|
|
135
|
+
ariaLabel,
|
|
136
|
+
} = attributes || {};
|
|
137
|
+
|
|
138
|
+
const classes = [
|
|
139
|
+
'tds-${blockName}',
|
|
140
|
+
\`tds-${blockName}--intent-\${intent}\`,
|
|
141
|
+
\`tds-${blockName}--size-\${size}\`,
|
|
142
|
+
disabled ? 'tds-${blockName}--disabled' : '',
|
|
143
|
+
].filter(Boolean).join(' ');
|
|
144
|
+
|
|
145
|
+
return React.createElement(
|
|
146
|
+
'div',
|
|
147
|
+
{ className: 'wp-block tds-${blockName}-wrapper' },
|
|
148
|
+
React.createElement(
|
|
149
|
+
'div',
|
|
150
|
+
{
|
|
151
|
+
className: classes,
|
|
152
|
+
'aria-label': ariaLabel,
|
|
153
|
+
},
|
|
154
|
+
label
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
describe('${componentName} Block', () => {
|
|
160
|
+
describe('Basic Rendering', () => {
|
|
161
|
+
it('should render wrapper element', () => {
|
|
162
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
163
|
+
expect(container.querySelector('.tds-${blockName}-wrapper')).toBeInTheDocument();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should render component with default label', () => {
|
|
167
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
168
|
+
expect(container.textContent).toContain('${componentName}');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should render component with custom label', () => {
|
|
172
|
+
const { container } = render(
|
|
173
|
+
React.createElement(${componentName}Save, { attributes: { label: 'Custom' } })
|
|
174
|
+
);
|
|
175
|
+
expect(container.textContent).toContain('Custom');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('Intent Variants', () => {
|
|
180
|
+
['primary', 'secondary', 'negative'].forEach(intent => {
|
|
181
|
+
it(\`should render \${intent} intent\`, () => {
|
|
182
|
+
const { container } = render(
|
|
183
|
+
React.createElement(${componentName}Save, { attributes: { intent } })
|
|
184
|
+
);
|
|
185
|
+
expect(container.querySelector(\`.tds-${blockName}--intent-\${intent}\`)).toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('Size Variants', () => {
|
|
191
|
+
['small', 'medium', 'large'].forEach(size => {
|
|
192
|
+
it(\`should render \${size} size\`, () => {
|
|
193
|
+
const { container } = render(
|
|
194
|
+
React.createElement(${componentName}Save, { attributes: { size } })
|
|
195
|
+
);
|
|
196
|
+
expect(container.querySelector(\`.tds-${blockName}--size-\${size}\`)).toBeInTheDocument();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('Disabled State', () => {
|
|
202
|
+
it('should add disabled class when disabled is true', () => {
|
|
203
|
+
const { container } = render(
|
|
204
|
+
React.createElement(${componentName}Save, { attributes: { disabled: true } })
|
|
205
|
+
);
|
|
206
|
+
expect(container.querySelector('.tds-${blockName}--disabled')).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should not add disabled class when disabled is false', () => {
|
|
210
|
+
const { container } = render(
|
|
211
|
+
React.createElement(${componentName}Save, { attributes: { disabled: false } })
|
|
212
|
+
);
|
|
213
|
+
expect(container.querySelector('.tds-${blockName}--disabled')).not.toBeInTheDocument();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('Accessibility', () => {
|
|
218
|
+
it('should include aria-label when provided', () => {
|
|
219
|
+
const { container } = render(
|
|
220
|
+
React.createElement(${componentName}Save, { attributes: { ariaLabel: 'Custom label' } })
|
|
221
|
+
);
|
|
222
|
+
expect(container.querySelector('.tds-${blockName}')).toHaveAttribute('aria-label', 'Custom label');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('Multiple Attributes', () => {
|
|
227
|
+
it('should combine intent, size, and disabled', () => {
|
|
228
|
+
const { container } = render(
|
|
229
|
+
React.createElement(${componentName}Save, {
|
|
230
|
+
attributes: {
|
|
231
|
+
intent: 'negative',
|
|
232
|
+
size: 'large',
|
|
233
|
+
disabled: true
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
const element = container.querySelector('.tds-${blockName}');
|
|
238
|
+
expect(element).toHaveClass('tds-${blockName}--intent-negative');
|
|
239
|
+
expect(element).toHaveClass('tds-${blockName}--size-large');
|
|
240
|
+
expect(element).toHaveClass('tds-${blockName}--disabled');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Template for text-like components (typography, semantic elements)
|
|
249
|
+
*/
|
|
250
|
+
function generateTextLikeTest(componentName, blockName) {
|
|
251
|
+
return `import { describe, it, expect } from 'vitest';
|
|
252
|
+
import { render } from '@testing-library/react';
|
|
253
|
+
import '@testing-library/jest-dom';
|
|
254
|
+
import React from 'react';
|
|
255
|
+
|
|
256
|
+
// Simplified Save component for testing
|
|
257
|
+
const ${componentName}Save = ({ attributes }) => {
|
|
258
|
+
const {
|
|
259
|
+
content = 'Default text',
|
|
260
|
+
typography = 'body',
|
|
261
|
+
semanticElement = 'p',
|
|
262
|
+
} = attributes || {};
|
|
263
|
+
|
|
264
|
+
const classes = [
|
|
265
|
+
'tds-${blockName}',
|
|
266
|
+
\`tds-${blockName}--typography-\${typography}\`,
|
|
267
|
+
].filter(Boolean).join(' ');
|
|
268
|
+
|
|
269
|
+
return React.createElement(
|
|
270
|
+
'div',
|
|
271
|
+
{ className: 'wp-block tds-${blockName}-wrapper' },
|
|
272
|
+
React.createElement(
|
|
273
|
+
semanticElement,
|
|
274
|
+
{ className: classes },
|
|
275
|
+
content
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
describe('${componentName} Block', () => {
|
|
281
|
+
describe('Basic Rendering', () => {
|
|
282
|
+
it('should render wrapper element', () => {
|
|
283
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
284
|
+
expect(container.querySelector('.tds-${blockName}-wrapper')).toBeInTheDocument();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should render with default content', () => {
|
|
288
|
+
const { container } = render(React.createElement(${componentName}Save, { attributes: {} }));
|
|
289
|
+
expect(container.textContent).toContain('Default text');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should render with custom content', () => {
|
|
293
|
+
const { container } = render(
|
|
294
|
+
React.createElement(${componentName}Save, { attributes: { content: 'Custom content' } })
|
|
295
|
+
);
|
|
296
|
+
expect(container.textContent).toContain('Custom content');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('Typography Variants', () => {
|
|
301
|
+
['heading1', 'heading2', 'heading3', 'body', 'caption'].forEach(typography => {
|
|
302
|
+
it(\`should render \${typography} typography\`, () => {
|
|
303
|
+
const { container } = render(
|
|
304
|
+
React.createElement(${componentName}Save, { attributes: { typography } })
|
|
305
|
+
);
|
|
306
|
+
expect(container.querySelector(\`.tds-${blockName}--typography-\${typography}\`)).toBeInTheDocument();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('Semantic Elements', () => {
|
|
312
|
+
['p', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach(element => {
|
|
313
|
+
it(\`should render as \${element} element\`, () => {
|
|
314
|
+
const { container } = render(
|
|
315
|
+
React.createElement(${componentName}Save, { attributes: { semanticElement: element } })
|
|
316
|
+
);
|
|
317
|
+
expect(container.querySelector(element)).toBeInTheDocument();
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('Accessibility', () => {
|
|
323
|
+
it('should render heading as semantic element', () => {
|
|
324
|
+
const { container } = render(
|
|
325
|
+
React.createElement(${componentName}Save, { attributes: { semanticElement: 'h1' } })
|
|
326
|
+
);
|
|
327
|
+
expect(container.querySelector('h1')).toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should render paragraph as semantic element', () => {
|
|
331
|
+
const { container } = render(
|
|
332
|
+
React.createElement(${componentName}Save, { attributes: { semanticElement: 'p' } })
|
|
333
|
+
);
|
|
334
|
+
expect(container.querySelector('p')).toBeInTheDocument();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('Multiple Attributes', () => {
|
|
339
|
+
it('should combine typography and semantic element', () => {
|
|
340
|
+
const { container } = render(
|
|
341
|
+
React.createElement(${componentName}Save, {
|
|
342
|
+
attributes: {
|
|
343
|
+
typography: 'heading1',
|
|
344
|
+
semanticElement: 'h1',
|
|
345
|
+
content: 'Heading'
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
expect(container.querySelector('h1')).toHaveClass('tds-${blockName}--typography-heading1');
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Determine component type and generate appropriate template
|
|
358
|
+
*/
|
|
359
|
+
function generateTestFile(componentName, blockName) {
|
|
360
|
+
// Simple heuristic: check component type by name or attributes
|
|
361
|
+
// This can be improved by reading the React component file
|
|
362
|
+
|
|
363
|
+
const buttonLikeComponents = ['Button', 'IconButton', 'Flag', 'Toast'];
|
|
364
|
+
const textLikeComponents = ['Text', 'InputHelperMessage'];
|
|
365
|
+
const dividerLikeComponents = ['Divider', 'AdContainer'];
|
|
366
|
+
|
|
367
|
+
if (buttonLikeComponents.includes(componentName)) {
|
|
368
|
+
return generateButtonLikeTest(componentName, blockName);
|
|
369
|
+
} else if (textLikeComponents.includes(componentName)) {
|
|
370
|
+
return generateTextLikeTest(componentName, blockName);
|
|
371
|
+
} else if (dividerLikeComponents.includes(componentName)) {
|
|
372
|
+
return generateBasicTest(componentName, blockName);
|
|
373
|
+
} else {
|
|
374
|
+
// Default: basic test
|
|
375
|
+
return generateBasicTest(componentName, blockName);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Create test file for a component
|
|
381
|
+
*/
|
|
382
|
+
function createBlockTest(componentName) {
|
|
383
|
+
const blockName = componentToBlockName(componentName);
|
|
384
|
+
const testDir = path.join(__dirname, '../__tests__/blocks');
|
|
385
|
+
const testFile = path.join(testDir, `${blockName}.test.js`);
|
|
386
|
+
|
|
387
|
+
// Check if test already exists
|
|
388
|
+
if (fs.existsSync(testFile)) {
|
|
389
|
+
log(`\n⚠️ Test file already exists: ${blockName}.test.js`, 'yellow');
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Create directory if it doesn't exist
|
|
394
|
+
if (!fs.existsSync(testDir)) {
|
|
395
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Generate and write test file
|
|
399
|
+
const testContent = generateTestFile(componentName, blockName);
|
|
400
|
+
fs.writeFileSync(testFile, testContent);
|
|
401
|
+
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function main() {
|
|
406
|
+
const args = process.argv.slice(2);
|
|
407
|
+
|
|
408
|
+
if (args.length === 0) {
|
|
409
|
+
log(`\n❌ Component name required`, 'red');
|
|
410
|
+
log(`\nUsage: node scripts/create-wordpress-block-tests.cjs ComponentName`, 'dim');
|
|
411
|
+
log(`\nExample: node scripts/create-wordpress-block-tests.cjs Button\n`, 'dim');
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const componentName = args[0];
|
|
416
|
+
const blockName = componentToBlockName(componentName);
|
|
417
|
+
|
|
418
|
+
if (createBlockTest(componentName)) {
|
|
419
|
+
log(`\n✅ Test file created successfully!`, 'green');
|
|
420
|
+
log(`\n📝 Test Details:`, 'bright');
|
|
421
|
+
log(` Component: ${componentName}`);
|
|
422
|
+
log(` Block: tds/${blockName}`);
|
|
423
|
+
log(` File: __tests__/blocks/${blockName}.test.js`);
|
|
424
|
+
log(`\n💡 Next Steps:`, 'cyan');
|
|
425
|
+
log(`\n 1. Review the generated test file`);
|
|
426
|
+
log(` 2. Implement the Save component rendering`);
|
|
427
|
+
log(` 3. Add variant and attribute tests based on component props`);
|
|
428
|
+
log(` 4. Run tests: npm test`);
|
|
429
|
+
log(`\n📖 Reference: __tests__/blocks/button.test.js for detailed example\n`);
|
|
430
|
+
process.exit(0);
|
|
431
|
+
} else {
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
module.exports = { createBlockTest, componentToBlockName };
|
|
437
|
+
|
|
438
|
+
main();
|