@pie-lib/test-utils 0.22.2-next.0 → 0.22.3-next.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/CHANGELOG.md +6 -64
- package/README.md +463 -0
- package/lib/__tests__/index.test.js +107 -0
- package/lib/__tests__/keyboard.test.js +146 -0
- package/lib/index.js +256 -24
- package/lib/index.js.map +1 -1
- package/lib/keyboard.js +173 -0
- package/lib/keyboard.js.map +1 -0
- package/lib/web-components.js +248 -0
- package/lib/web-components.js.map +1 -0
- package/package.json +13 -5
- package/src/__tests__/index.test.js +88 -41
- package/src/__tests__/keyboard.test.js +116 -0
- package/src/index.js +132 -11
- package/src/keyboard.js +126 -0
- package/src/web-components.js +200 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Note: These helpers are for light DOM web components (no Shadow DOM)
|
|
2
|
+
// Standard React Testing Library queries work directly on these components
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wait for a custom element to be defined
|
|
6
|
+
* Custom elements are registered asynchronously
|
|
7
|
+
*
|
|
8
|
+
* @param {string} tagName - Custom element tag name (e.g., 'my-component')
|
|
9
|
+
* @param {number} timeout - Timeout in milliseconds
|
|
10
|
+
* @returns {Promise<void>}
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* await waitForCustomElement('pie-chart');
|
|
14
|
+
* const chart = document.createElement('pie-chart');
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* await waitForCustomElement('my-component', 5000);
|
|
18
|
+
*/
|
|
19
|
+
export async function waitForCustomElement(tagName, timeout = 3000) {
|
|
20
|
+
if (customElements.get(tagName)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
reject(
|
|
27
|
+
new Error(
|
|
28
|
+
`Custom element '${tagName}' not defined within ${timeout}ms. ` +
|
|
29
|
+
'Make sure the element is registered with customElements.define().'
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
}, timeout);
|
|
33
|
+
|
|
34
|
+
customElements.whenDefined(tagName).then(() => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
resolve();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Render a web component and wait for it to be ready
|
|
43
|
+
* Handles the full lifecycle: wait for definition, create, append, wait for render
|
|
44
|
+
*
|
|
45
|
+
* @param {string} tagName - Custom element tag name
|
|
46
|
+
* @param {Object} attributes - Attributes to set on the element
|
|
47
|
+
* @param {Object} properties - Properties to set on the element
|
|
48
|
+
* @param {HTMLElement} container - Container to append to (defaults to document.body)
|
|
49
|
+
* @returns {Promise<HTMLElement>} The custom element
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const chart = await renderWebComponent('pie-chart', {
|
|
53
|
+
* type: 'bar',
|
|
54
|
+
* 'data-testid': 'my-chart'
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const button = await renderWebComponent('custom-button',
|
|
59
|
+
* { 'aria-label': 'Submit' },
|
|
60
|
+
* { onClick: jest.fn() }
|
|
61
|
+
* );
|
|
62
|
+
*/
|
|
63
|
+
export async function renderWebComponent(
|
|
64
|
+
tagName,
|
|
65
|
+
attributes = {},
|
|
66
|
+
properties = {},
|
|
67
|
+
container = document.body
|
|
68
|
+
) {
|
|
69
|
+
await waitForCustomElement(tagName);
|
|
70
|
+
|
|
71
|
+
const element = document.createElement(tagName);
|
|
72
|
+
|
|
73
|
+
// Set attributes (strings)
|
|
74
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
75
|
+
element.setAttribute(key, value);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Set properties (objects, functions, etc.)
|
|
79
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
80
|
+
element[key] = value;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
container.appendChild(element);
|
|
84
|
+
|
|
85
|
+
// Wait for component to render (custom elements may be async)
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
87
|
+
|
|
88
|
+
return element;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Dispatch a custom event on an element
|
|
93
|
+
* Web components often use custom events for communication
|
|
94
|
+
*
|
|
95
|
+
* @param {HTMLElement} element - Element to dispatch event from
|
|
96
|
+
* @param {string} eventName - Event name (e.g., 'change', 'custom-event')
|
|
97
|
+
* @param {*} detail - Event detail data
|
|
98
|
+
* @param {Object} options - Event options (bubbles, composed, etc.)
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* dispatchCustomEvent(chart, 'data-changed', { value: [1, 2, 3] });
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* dispatchCustomEvent(button, 'custom-click', null, { bubbles: false });
|
|
105
|
+
*/
|
|
106
|
+
export function dispatchCustomEvent(
|
|
107
|
+
element,
|
|
108
|
+
eventName,
|
|
109
|
+
detail = null,
|
|
110
|
+
options = {}
|
|
111
|
+
) {
|
|
112
|
+
const event = new CustomEvent(eventName, {
|
|
113
|
+
detail,
|
|
114
|
+
bubbles: true,
|
|
115
|
+
composed: true, // Allow event to cross shadow DOM boundary
|
|
116
|
+
...options,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
element.dispatchEvent(event);
|
|
120
|
+
return event;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Listen for a custom event and return a promise that resolves when fired
|
|
125
|
+
* Useful for testing event emissions
|
|
126
|
+
*
|
|
127
|
+
* @param {HTMLElement} element - Element to listen to
|
|
128
|
+
* @param {string} eventName - Event name to wait for
|
|
129
|
+
* @param {number} timeout - Timeout in milliseconds
|
|
130
|
+
* @returns {Promise<CustomEvent>} Promise that resolves with the event
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* const promise = waitForEvent(chart, 'data-loaded');
|
|
134
|
+
* chart.loadData();
|
|
135
|
+
* const event = await promise;
|
|
136
|
+
* expect(event.detail).toEqual({ loaded: true });
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* await waitForEvent(component, 'ready', 5000);
|
|
140
|
+
*/
|
|
141
|
+
export function waitForEvent(element, eventName, timeout = 3000) {
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const timer = setTimeout(() => {
|
|
144
|
+
element.removeEventListener(eventName, handler);
|
|
145
|
+
reject(
|
|
146
|
+
new Error(`Event '${eventName}' not fired within ${timeout}ms`)
|
|
147
|
+
);
|
|
148
|
+
}, timeout);
|
|
149
|
+
|
|
150
|
+
const handler = (event) => {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
element.removeEventListener(eventName, handler);
|
|
153
|
+
resolve(event);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
element.addEventListener(eventName, handler);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if a custom element is defined
|
|
162
|
+
* Useful for verifying element registration
|
|
163
|
+
*
|
|
164
|
+
* @param {string} tagName - Custom element tag name
|
|
165
|
+
* @returns {boolean} True if element is defined
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* if (isCustomElementDefined('pie-chart')) {
|
|
169
|
+
* // Element is ready to use
|
|
170
|
+
* }
|
|
171
|
+
*/
|
|
172
|
+
export function isCustomElementDefined(tagName) {
|
|
173
|
+
return typeof customElements !== 'undefined' && customElements.get(tagName) !== undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Helper to create and configure a custom element
|
|
178
|
+
* For light DOM web components that render React
|
|
179
|
+
*
|
|
180
|
+
* @param {string} tagName - Custom element tag name
|
|
181
|
+
* @param {Object} props - Props to pass to the element
|
|
182
|
+
* @returns {HTMLElement} The custom element
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* const chart = createCustomElement('pie-chart', {
|
|
186
|
+
* data: [1, 2, 3],
|
|
187
|
+
* type: 'bar'
|
|
188
|
+
* });
|
|
189
|
+
* document.body.appendChild(chart);
|
|
190
|
+
*/
|
|
191
|
+
export function createCustomElement(tagName, props = {}) {
|
|
192
|
+
const element = document.createElement(tagName);
|
|
193
|
+
|
|
194
|
+
// Set properties directly on the element
|
|
195
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
196
|
+
element[key] = value;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return element;
|
|
200
|
+
}
|