@coherent.js/testing 1.0.0-beta.3 → 1.0.0-beta.6

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.
Files changed (2) hide show
  1. package/package.json +3 -2
  2. package/types/index.d.ts +283 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/testing",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "Testing utilities for Coherent.js applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "author": "Coherent.js Team",
20
20
  "license": "MIT",
21
21
  "peerDependencies": {
22
- "@coherent.js/core": "1.0.0-beta.3"
22
+ "@coherent.js/core": "1.0.0-beta.6"
23
23
  },
24
24
  "devDependencies": {
25
25
  "vitest": "^1.0.0"
@@ -37,6 +37,7 @@
37
37
  "README.md",
38
38
  "types/"
39
39
  ],
40
+ "sideEffects": false,
40
41
  "scripts": {
41
42
  "build": "node build.mjs",
42
43
  "clean": "rm -rf dist",
package/types/index.d.ts CHANGED
@@ -3,76 +3,229 @@
3
3
  * @module @coherent.js/testing
4
4
  */
5
5
 
6
- // ===== Test Renderer Types =====
6
+ import type { CoherentNode, CoherentElement, CoherentComponent, ComponentProps } from '@coherent.js/core';
7
7
 
8
+ // ============================================================================
9
+ // Test Renderer Types
10
+ // ============================================================================
11
+
12
+ /**
13
+ * Render options for test utilities
14
+ */
8
15
  export interface RenderOptions {
9
- wrapper?: any;
10
- context?: Record<string, any>;
11
- props?: Record<string, any>;
16
+ /** Wrapper component */
17
+ wrapper?: CoherentComponent;
18
+ /** Context values to provide */
19
+ context?: Record<string, unknown>;
20
+ /** Props to pass to component */
21
+ props?: Record<string, unknown>;
22
+ /** Initial state */
23
+ initialState?: Record<string, unknown>;
12
24
  }
13
25
 
14
- export interface TestRendererResult {
26
+ /**
27
+ * Result from rendering a component
28
+ */
29
+ export interface RenderResult {
30
+ /** Rendered HTML string */
15
31
  html: string;
16
- component: any;
32
+ /** The rendered element structure */
33
+ element: CoherentElement;
34
+ /** Container element (if DOM is available) */
17
35
  container: HTMLElement | null;
18
- rerender(component: any): void;
36
+ /** Re-render with new props */
37
+ rerender(props?: Record<string, unknown>): void;
38
+ /** Unmount and cleanup */
19
39
  unmount(): void;
40
+ /** Debug output */
20
41
  debug(): void;
42
+ /** Query helpers */
43
+ getByText(text: string | RegExp): Element | null;
44
+ getByTestId(testId: string): Element | null;
45
+ getAllByText(text: string | RegExp): Element[];
21
46
  }
22
47
 
48
+ /**
49
+ * Test renderer class
50
+ */
23
51
  export class TestRenderer {
24
- render(component: any, options?: RenderOptions): TestRendererResult;
25
- renderAsync(component: any, options?: RenderOptions): Promise<TestRendererResult>;
26
- shallow(component: any): TestRendererResult;
52
+ /** Render a component */
53
+ render(component: CoherentNode, options?: RenderOptions): RenderResult;
54
+
55
+ /** Render a component asynchronously */
56
+ renderAsync(component: CoherentNode, options?: RenderOptions): Promise<RenderResult>;
57
+
58
+ /** Shallow render (no children) */
59
+ shallow(component: CoherentNode): RenderResult;
60
+
61
+ /** Cleanup all renders */
27
62
  cleanup(): void;
28
63
  }
29
64
 
30
- export function renderComponent(component: any, options?: RenderOptions): TestRendererResult;
31
- export function renderComponentAsync(component: any, options?: RenderOptions): Promise<TestRendererResult>;
65
+ /**
66
+ * Render a component for testing
67
+ */
68
+ export function renderComponent(
69
+ component: CoherentComponent | CoherentNode,
70
+ props?: Record<string, unknown>
71
+ ): RenderResult;
72
+
73
+ /**
74
+ * Render a component asynchronously
75
+ */
76
+ export function renderComponentAsync(
77
+ component: CoherentNode,
78
+ options?: RenderOptions
79
+ ): Promise<RenderResult>;
80
+
81
+ /**
82
+ * Create a new test renderer instance
83
+ */
32
84
  export function createTestRenderer(): TestRenderer;
33
- export function shallowRender(component: any): TestRendererResult;
34
85
 
35
- // ===== Test Utilities Types =====
86
+ /**
87
+ * Shallow render a component
88
+ */
89
+ export function shallowRender(component: CoherentNode): RenderResult;
36
90
 
91
+ /**
92
+ * Render a node to HTML string
93
+ */
94
+ export function renderToString(node: CoherentNode): string;
95
+
96
+ // ============================================================================
97
+ // Custom Matchers for Coherent.js
98
+ // ============================================================================
99
+
100
+ /**
101
+ * Coherent.js-specific test matchers
102
+ */
103
+ export interface CoherentMatchers<R = unknown> {
104
+ // Element structure matchers
105
+ /** Assert element has specific tag name */
106
+ toHaveTag(tagName: string): R;
107
+ /** Assert element contains text */
108
+ toHaveText(text: string): R;
109
+ /** Assert element has attribute (optionally with value) */
110
+ toHaveAttribute(name: string, value?: string): R;
111
+ /** Assert element has CSS class */
112
+ toHaveClassName(className: string): R;
113
+ /** Assert element has children (optionally specific count) */
114
+ toHaveChildren(count?: number): R;
115
+
116
+ // Component matchers
117
+ /** Assert component renders an element with tag */
118
+ toRenderElement(tagName: string): R;
119
+ /** Assert component renders text content */
120
+ toRenderText(text: string): R;
121
+ /** Assert component matches snapshot */
122
+ toMatchComponentSnapshot(): R;
123
+
124
+ // Hydration matchers
125
+ /** Assert hydration completes without mismatch */
126
+ toHydrateWithoutMismatch(): R;
127
+ /** Assert hydrated component has specific state */
128
+ toHaveState(state: Record<string, unknown>): R;
129
+
130
+ // Accessibility matchers
131
+ /** Assert element has accessible name */
132
+ toHaveAccessibleName(name: string): R;
133
+ /** Assert element has ARIA role */
134
+ toHaveRole(role: string): R;
135
+ }
136
+
137
+ // ============================================================================
138
+ // Test Utilities
139
+ // ============================================================================
140
+
141
+ /**
142
+ * Event simulation options
143
+ */
37
144
  export interface EventOptions {
38
145
  bubbles?: boolean;
39
146
  cancelable?: boolean;
40
147
  composed?: boolean;
41
- [key: string]: any;
148
+ [key: string]: unknown;
42
149
  }
43
150
 
151
+ /**
152
+ * Fire DOM events on elements
153
+ */
44
154
  export const fireEvent: {
155
+ /** Fire any event */
45
156
  (element: Element, event: Event): boolean;
157
+ /** Fire click event */
46
158
  click(element: Element, options?: EventOptions): boolean;
47
- change(element: Element, options?: EventOptions & { target?: { value?: any } }): boolean;
48
- input(element: Element, options?: EventOptions & { target?: { value?: any } }): boolean;
159
+ /** Fire change event */
160
+ change(element: Element, options?: EventOptions & { target?: { value?: unknown } }): boolean;
161
+ /** Fire input event */
162
+ input(element: Element, options?: EventOptions & { target?: { value?: unknown } }): boolean;
163
+ /** Fire submit event */
49
164
  submit(element: Element, options?: EventOptions): boolean;
165
+ /** Fire keydown event */
50
166
  keyDown(element: Element, options?: EventOptions & { key?: string; code?: string }): boolean;
167
+ /** Fire keyup event */
51
168
  keyUp(element: Element, options?: EventOptions & { key?: string; code?: string }): boolean;
169
+ /** Fire focus event */
52
170
  focus(element: Element, options?: EventOptions): boolean;
171
+ /** Fire blur event */
53
172
  blur(element: Element, options?: EventOptions): boolean;
173
+ /** Fire mouseenter event */
54
174
  mouseEnter(element: Element, options?: EventOptions): boolean;
175
+ /** Fire mouseleave event */
55
176
  mouseLeave(element: Element, options?: EventOptions): boolean;
56
- [key: string]: any;
177
+ [key: string]: unknown;
57
178
  };
58
179
 
180
+ /**
181
+ * Wait options
182
+ */
59
183
  export interface WaitOptions {
184
+ /** Timeout in ms */
60
185
  timeout?: number;
186
+ /** Check interval in ms */
61
187
  interval?: number;
62
188
  }
63
189
 
64
- export function waitFor<T>(callback: () => T | Promise<T>, options?: WaitOptions): Promise<T>;
190
+ /**
191
+ * Wait for a condition to be true
192
+ */
193
+ export function waitFor<T>(
194
+ callback: () => T | Promise<T>,
195
+ options?: WaitOptions
196
+ ): Promise<T>;
197
+
198
+ /**
199
+ * Wait for an element to appear
200
+ */
65
201
  export function waitForElement(selector: string, options?: WaitOptions): Promise<Element>;
66
- export function waitForElementToBeRemoved(selector: string | Element, options?: WaitOptions): Promise<void>;
67
202
 
203
+ /**
204
+ * Wait for an element to be removed
205
+ */
206
+ export function waitForElementToBeRemoved(
207
+ selector: string | Element,
208
+ options?: WaitOptions
209
+ ): Promise<void>;
210
+
211
+ /**
212
+ * Run a callback and flush pending state updates
213
+ */
68
214
  export function act<T>(callback: () => T | Promise<T>): Promise<T>;
69
215
 
70
- export interface Mock<T extends (...args: any[]) => any = (...args: any[]) => any> {
216
+ // ============================================================================
217
+ // Mock Utilities
218
+ // ============================================================================
219
+
220
+ /**
221
+ * Mock function interface
222
+ */
223
+ export interface Mock<T extends (...args: unknown[]) => unknown = (...args: unknown[]) => unknown> {
71
224
  (...args: Parameters<T>): ReturnType<T>;
72
225
  mock: {
73
226
  calls: Parameters<T>[];
74
- results: Array<{ type: 'return' | 'throw'; value: any }>;
75
- instances: any[];
227
+ results: Array<{ type: 'return' | 'throw'; value: unknown }>;
228
+ instances: unknown[];
76
229
  };
77
230
  mockClear(): void;
78
231
  mockReset(): void;
@@ -81,14 +234,55 @@ export interface Mock<T extends (...args: any[]) => any = (...args: any[]) => an
81
234
  mockReturnValue(value: ReturnType<T>): this;
82
235
  mockReturnValueOnce(value: ReturnType<T>): this;
83
236
  mockResolvedValue(value: ReturnType<T> extends Promise<infer U> ? U : never): this;
84
- mockRejectedValue(error: any): this;
237
+ mockRejectedValue(error: unknown): this;
85
238
  }
86
239
 
87
- export function createMock<T extends (...args: any[]) => any>(implementation?: T): Mock<T>;
88
- export function createSpy<T extends (...args: any[]) => any>(object: any, method: string): Mock<T>;
240
+ /**
241
+ * Create a mock function
242
+ */
243
+ export function createMock<T extends (...args: unknown[]) => unknown>(
244
+ implementation?: T
245
+ ): Mock<T>;
89
246
 
247
+ /**
248
+ * Create a spy on an object method
249
+ */
250
+ export function createSpy<T extends (...args: unknown[]) => unknown>(
251
+ object: object,
252
+ method: string
253
+ ): Mock<T>;
254
+
255
+ /**
256
+ * Mock a component
257
+ */
258
+ export function mockComponent<P extends ComponentProps = ComponentProps>(
259
+ name: string,
260
+ render?: (props: P) => CoherentNode
261
+ ): CoherentComponent<P>;
262
+
263
+ /**
264
+ * Create test state with reset capability
265
+ */
266
+ export function createTestState<T extends Record<string, unknown>>(
267
+ initial: T
268
+ ): {
269
+ getState: () => T;
270
+ setState: (updates: Partial<T>) => void;
271
+ reset: () => void;
272
+ };
273
+
274
+ /**
275
+ * Cleanup all mocks and rendered components
276
+ */
90
277
  export function cleanup(): void;
91
278
 
279
+ // ============================================================================
280
+ // Query Utilities
281
+ // ============================================================================
282
+
283
+ /**
284
+ * Query helper interface
285
+ */
92
286
  export interface Within {
93
287
  getByText(text: string | RegExp): Element;
94
288
  getByRole(role: string, options?: { name?: string | RegExp }): Element;
@@ -102,9 +296,19 @@ export interface Within {
102
296
  findAllByText(text: string | RegExp): Promise<Element[]>;
103
297
  }
104
298
 
299
+ /**
300
+ * Create query helpers scoped to an element
301
+ */
105
302
  export function within(element: Element): Within;
303
+
304
+ /**
305
+ * Global screen queries (document.body)
306
+ */
106
307
  export const screen: Within;
107
308
 
309
+ /**
310
+ * User event simulation
311
+ */
108
312
  export const userEvent: {
109
313
  click(element: Element): Promise<void>;
110
314
  dblClick(element: Element): Promise<void>;
@@ -118,8 +322,37 @@ export const userEvent: {
118
322
  paste(element: Element, text: string): Promise<void>;
119
323
  };
120
324
 
121
- // ===== Matchers Types =====
325
+ // ============================================================================
326
+ // Assertion Utilities
327
+ // ============================================================================
122
328
 
329
+ /**
330
+ * Assert element structure matches expected
331
+ */
332
+ export function assertElementStructure(
333
+ element: CoherentElement,
334
+ expected: Partial<CoherentElement>
335
+ ): void;
336
+
337
+ /**
338
+ * Standard assertions
339
+ */
340
+ export const assertions: {
341
+ assertElement(element: unknown): asserts element is Element;
342
+ assertHTMLElement(element: unknown): asserts element is HTMLElement;
343
+ assertInDocument(element: Element | null): asserts element is Element;
344
+ assertVisible(element: Element): void;
345
+ assertHasAttribute(element: Element, attr: string): void;
346
+ assertHasClass(element: Element, className: string): void;
347
+ };
348
+
349
+ // ============================================================================
350
+ // DOM Matchers (for Vitest/Jest)
351
+ // ============================================================================
352
+
353
+ /**
354
+ * Custom DOM matchers
355
+ */
123
356
  export interface CustomMatchers<R = void> {
124
357
  toHaveHTML(html: string): R;
125
358
  toContainHTML(html: string): R;
@@ -130,35 +363,42 @@ export interface CustomMatchers<R = void> {
130
363
  toBeVisible(): R;
131
364
  toBeDisabled(): R;
132
365
  toBeEnabled(): R;
133
- toHaveValue(value: any): R;
134
- toHaveStyle(style: Record<string, any>): R;
366
+ toHaveValue(value: unknown): R;
367
+ toHaveStyle(style: Record<string, unknown>): R;
135
368
  toHaveFocus(): R;
136
369
  toBeChecked(): R;
137
370
  toBeValid(): R;
138
371
  toBeInvalid(): R;
139
372
  }
140
373
 
374
+ /**
375
+ * Custom matchers object
376
+ */
141
377
  export const customMatchers: CustomMatchers;
142
378
 
143
- export function extendExpect(matchers: Record<string, (...args: any[]) => any>): void;
379
+ /**
380
+ * Extend test framework expect
381
+ */
382
+ export function extendExpect(matchers: Record<string, (...args: unknown[]) => unknown>): void;
144
383
 
145
- export const assertions: {
146
- assertElement(element: any): asserts element is Element;
147
- assertHTMLElement(element: any): asserts element is HTMLElement;
148
- assertInDocument(element: Element | null): asserts element is Element;
149
- assertVisible(element: Element): void;
150
- assertHasAttribute(element: Element, attr: string): void;
151
- assertHasClass(element: Element, className: string): void;
152
- };
384
+ // ============================================================================
385
+ // Vitest/Jest Module Extensions
386
+ // ============================================================================
387
+
388
+ // Extend Vitest matchers
389
+ declare module 'vitest' {
390
+ interface Assertion<T = unknown> extends CoherentMatchers<T>, CustomMatchers<T> {}
391
+ interface AsymmetricMatchersContaining extends CoherentMatchers, CustomMatchers {}
392
+ }
153
393
 
154
- // Extend Jest/Vitest expect
394
+ // Extend Jest matchers (for users using Jest)
155
395
  declare global {
156
396
  namespace Vi {
157
- interface Matchers<R = void> extends CustomMatchers<R> {}
158
- interface AsymmetricMatchers extends CustomMatchers {}
397
+ interface Matchers<R = void> extends CustomMatchers<R>, CoherentMatchers<R> {}
398
+ interface AsymmetricMatchers extends CustomMatchers, CoherentMatchers {}
159
399
  }
160
400
  namespace jest {
161
- interface Matchers<R = void> extends CustomMatchers<R> {}
162
- interface Expect extends CustomMatchers {}
401
+ interface Matchers<R = void> extends CustomMatchers<R>, CoherentMatchers<R> {}
402
+ interface Expect extends CustomMatchers, CoherentMatchers {}
163
403
  }
164
404
  }