@kiwa-test/component 0.2.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/README.md +153 -0
- package/dist/index.cjs +802 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +534 -0
- package/dist/index.d.ts +534 -0
- package/dist/index.js +757 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# `@kiwa-test/component`
|
|
2
|
+
|
|
3
|
+
Component test mock harness for kiwa — 3 unified integrations across Storybook 8, Playwright Component Testing, and Chromatic. One API for story registration + args resolution + play function runner, mount + interact, and visual regression baseline / diff / accept workflow.
|
|
4
|
+
|
|
5
|
+
v0.1 lays the foundation for the v1.16 milestone dogfood apps (design system + form CT + visual regression).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add -D @kiwa-test/component @kiwa-test/quality-metrics
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start — 3 unified mocks
|
|
14
|
+
|
|
15
|
+
### Storybook 8 story registry
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
createStoryRegistry,
|
|
20
|
+
buildButton,
|
|
21
|
+
} from '@kiwa-test/component';
|
|
22
|
+
|
|
23
|
+
const registry = createStoryRegistry();
|
|
24
|
+
registry.register({
|
|
25
|
+
title: 'Components/Button',
|
|
26
|
+
render: buildButton,
|
|
27
|
+
args: { label: 'default' },
|
|
28
|
+
stories: {
|
|
29
|
+
Primary: { args: { variant: 'primary' } },
|
|
30
|
+
Interactive: {
|
|
31
|
+
args: { label: 'Click me' },
|
|
32
|
+
play: async ({ canvasElement, step }) => {
|
|
33
|
+
await step('click the button', async () => {
|
|
34
|
+
const btn = canvasElement.getByRole('button');
|
|
35
|
+
btn.handlers['click']?.[0]?.({ type: 'click', target: btn });
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const { canvas } = registry.mount('Components/Button', 'Interactive');
|
|
43
|
+
const result = await registry.play('Components/Button', 'Interactive', canvas);
|
|
44
|
+
console.log(result.steps); // [{ label: 'click the button', ok: true }]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Supports `StoryObj.args + play + parameters`, meta / story deep merge, `parameters.chromatic` / `parameters.a11y` passthrough, and a heuristic a11y checker (button-name, image-alt, label rules).
|
|
48
|
+
|
|
49
|
+
### Playwright Component Testing
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import {
|
|
53
|
+
createPlaywrightCTMock,
|
|
54
|
+
buildForm,
|
|
55
|
+
} from '@kiwa-test/component';
|
|
56
|
+
|
|
57
|
+
const ct = createPlaywrightCTMock();
|
|
58
|
+
let submitted: Record<string, string> | null = null;
|
|
59
|
+
const form = ct.mount(buildForm, {
|
|
60
|
+
title: 'Signup',
|
|
61
|
+
fields: [
|
|
62
|
+
{ id: 'email', label: 'Email', type: 'email', required: true },
|
|
63
|
+
],
|
|
64
|
+
onSubmit: (data) => {
|
|
65
|
+
submitted = data;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// interact via Playwright API surface
|
|
70
|
+
await form.getByRole('textbox').fill('user@example.com');
|
|
71
|
+
await form.getByRole('button').click();
|
|
72
|
+
console.log(submitted); // { email: 'user@example.com' }
|
|
73
|
+
form.unmount();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Supports `mount + getByText + getByRole + click + fill + textContent + count`, framework agnostic, no browser process, deterministic in-memory DOM.
|
|
77
|
+
|
|
78
|
+
### Chromatic visual regression
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import {
|
|
82
|
+
createChromaticVisualMock,
|
|
83
|
+
createStoryRegistry,
|
|
84
|
+
buildCard,
|
|
85
|
+
} from '@kiwa-test/component';
|
|
86
|
+
|
|
87
|
+
const chromatic = createChromaticVisualMock();
|
|
88
|
+
const registry = createStoryRegistry();
|
|
89
|
+
registry.register({
|
|
90
|
+
title: 'Card',
|
|
91
|
+
render: buildCard,
|
|
92
|
+
parameters: { chromatic: { viewports: ['mobile', 'desktop'], diffThreshold: 0.01 } },
|
|
93
|
+
stories: { Default: { args: { title: 'Hello', body: 'World' } } },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const { canvas, entry } = registry.mount('Card', 'Default');
|
|
97
|
+
|
|
98
|
+
// 1st pass — new baseline captured per viewport
|
|
99
|
+
let diffs = chromatic.captureAll({ entry, canvas });
|
|
100
|
+
console.log(diffs.map((d) => d.status)); // ['new', 'new']
|
|
101
|
+
|
|
102
|
+
// 2nd pass — identical markup → passed
|
|
103
|
+
diffs = chromatic.captureAll({ entry, canvas });
|
|
104
|
+
console.log(diffs.every((d) => d.status === 'passed')); // true
|
|
105
|
+
|
|
106
|
+
// Simulate a design change
|
|
107
|
+
const { canvas: changed, entry: changedEntry } = registry.mount('Card', 'Default', { title: 'Hi' });
|
|
108
|
+
const changedDiffs = chromatic.captureAll({ entry: changedEntry, canvas: changed });
|
|
109
|
+
console.log(changedDiffs[0]?.status); // 'failed'
|
|
110
|
+
|
|
111
|
+
// Approve the design change
|
|
112
|
+
chromatic.review({ storyId: changedEntry.id, viewport: 'mobile', action: 'accept' });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Supports `capture + captureAll + review (accept/reject) + reviewHistory + seedBaseline + reset`, multi viewport, `parameters.chromatic.diffThreshold` and `disable` fields, deterministic SHA-256 hash comparison.
|
|
116
|
+
|
|
117
|
+
## 5 component fixtures
|
|
118
|
+
|
|
119
|
+
Framework agnostic renderers usable from stories / component tests / visual capture. Cover the five SaaS frontend primitives.
|
|
120
|
+
|
|
121
|
+
| fixture | description |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `buildButton` | text label + click handler + variant class + disabled state |
|
|
124
|
+
| `buildInput` | label + input pair with `for/id` association + change handler |
|
|
125
|
+
| `buildForm` | title + multi-field composition + submit validation |
|
|
126
|
+
| `buildModal` | open/close state + backdrop + close button + escape hooks |
|
|
127
|
+
| `buildCard` | title + body + optional footer + variant class |
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { componentFixtures } from '@kiwa-test/component';
|
|
131
|
+
|
|
132
|
+
// batch register every fixture
|
|
133
|
+
const registry = createStoryRegistry();
|
|
134
|
+
for (const [name, render] of Object.entries(componentFixtures)) {
|
|
135
|
+
registry.register({
|
|
136
|
+
title: `Fixtures/${name}`,
|
|
137
|
+
render,
|
|
138
|
+
stories: { Default: {} },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Design goals
|
|
144
|
+
|
|
145
|
+
- **1 API for 3 integrations** — the same `MockNode` tree is used for story rendering, Playwright CT mounting, and Chromatic capture.
|
|
146
|
+
- **Framework agnostic** — every component is a pure `(args) => MockNode` function, so React / Vue / Svelte / Solid tests share the harness.
|
|
147
|
+
- **Deterministic** — no jsdom, no browser process, no wall clock (all `now` sources injectable). Hashes and IDs are stable across runs.
|
|
148
|
+
- **Real-vs-mock parity** — API surface deliberately mirrors real Storybook 8 CSF3, Playwright CT `Locator`, and Chromatic baseline / diff / accept workflow, so a single test spec can run against the mock (fast) and a real environment (dogfood).
|
|
149
|
+
|
|
150
|
+
## Related packages
|
|
151
|
+
|
|
152
|
+
- `@kiwa-test/quality-metrics` — 11-axis release gate that this harness will feed component test results into (Chromatic `status=failed` and a11y violations map to axes).
|
|
153
|
+
- `@kiwa-test/e2e` — Playwright browser-driven end-to-end tests. Complement, not replacement — CT covers component surface, e2e covers page flows.
|