@hubspot/ui-extensions 0.11.1 → 0.11.2

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 (65) hide show
  1. package/dist/__synced__/experimental/types.synced.d.ts +3 -4
  2. package/dist/__synced__/remoteComponents.synced.d.ts +152 -70
  3. package/dist/__synced__/remoteComponents.synced.js +98 -0
  4. package/dist/__synced__/types/components/button.synced.d.ts +6 -0
  5. package/dist/__tests__/crm/hooks/useAssociations.spec.js +33 -29
  6. package/dist/__tests__/crm/hooks/useCrmProperties.spec.js +19 -18
  7. package/dist/__tests__/crm/utils/fetchAssociations.spec.js +8 -7
  8. package/dist/__tests__/crm/utils/fetchCrmProperties.spec.js +34 -33
  9. package/dist/crm/index.d.ts +1 -1
  10. package/dist/crm/index.js +1 -1
  11. package/dist/experimental/index.d.ts +1 -1
  12. package/dist/experimental/index.js +1 -1
  13. package/dist/experimental/testing/__tests__/debug.spec.d.ts +1 -0
  14. package/dist/experimental/testing/__tests__/debug.spec.js +43 -0
  15. package/dist/experimental/testing/__tests__/find.spec.d.ts +1 -0
  16. package/dist/experimental/testing/__tests__/find.spec.js +33 -0
  17. package/dist/experimental/testing/__tests__/findAll.spec.d.ts +1 -0
  18. package/dist/experimental/testing/__tests__/findAll.spec.js +12 -0
  19. package/dist/experimental/testing/__tests__/findAllChildren.spec.d.ts +1 -0
  20. package/dist/experimental/testing/__tests__/findAllChildren.spec.js +48 -0
  21. package/dist/experimental/testing/__tests__/findChild.spec.d.ts +1 -0
  22. package/dist/experimental/testing/__tests__/findChild.spec.js +29 -0
  23. package/dist/experimental/testing/__tests__/fragments.spec.d.ts +1 -0
  24. package/dist/experimental/testing/__tests__/fragments.spec.js +59 -0
  25. package/dist/experimental/testing/__tests__/invalid-components.spec.d.ts +1 -0
  26. package/dist/experimental/testing/__tests__/invalid-components.spec.js +88 -0
  27. package/dist/experimental/testing/__tests__/isMatch.spec.d.ts +1 -0
  28. package/dist/experimental/testing/__tests__/isMatch.spec.js +60 -0
  29. package/dist/experimental/testing/__tests__/maybeFind.spec.d.ts +1 -0
  30. package/dist/experimental/testing/__tests__/maybeFind.spec.js +58 -0
  31. package/dist/experimental/testing/__tests__/maybeFindChild.spec.d.ts +1 -0
  32. package/dist/experimental/testing/__tests__/maybeFindChild.spec.js +65 -0
  33. package/dist/experimental/testing/__tests__/trigger.spec.d.ts +1 -0
  34. package/dist/experimental/testing/__tests__/trigger.spec.js +40 -0
  35. package/dist/experimental/testing/__tests__/type-utils.spec.d.ts +1 -0
  36. package/dist/experimental/testing/__tests__/type-utils.spec.js +163 -0
  37. package/dist/experimental/testing/__tests__/waitFor.spec.d.ts +1 -0
  38. package/dist/experimental/testing/__tests__/waitFor.spec.js +55 -0
  39. package/dist/experimental/testing/index.d.ts +3 -0
  40. package/dist/experimental/testing/index.js +3 -0
  41. package/dist/experimental/testing/internal/convert.d.ts +10 -0
  42. package/dist/experimental/testing/internal/convert.js +131 -0
  43. package/dist/experimental/testing/internal/debug.d.ts +1 -1
  44. package/dist/experimental/testing/internal/debug.js +10 -1
  45. package/dist/experimental/testing/internal/document.d.ts +14 -0
  46. package/dist/experimental/testing/internal/document.js +37 -0
  47. package/dist/experimental/testing/internal/errors.d.ts +12 -0
  48. package/dist/experimental/testing/internal/errors.js +18 -0
  49. package/dist/experimental/testing/internal/match.d.ts +19 -0
  50. package/dist/experimental/testing/internal/match.js +42 -0
  51. package/dist/experimental/testing/internal/query.js +1 -19
  52. package/dist/experimental/testing/internal/utils/promise-utils.d.ts +14 -0
  53. package/dist/experimental/testing/internal/utils/promise-utils.js +14 -0
  54. package/dist/experimental/testing/render.d.ts +9 -0
  55. package/dist/experimental/testing/render.js +155 -0
  56. package/dist/experimental/testing/types.d.ts +1 -0
  57. package/dist/pages/home/index.d.ts +1 -1
  58. package/dist/pages/home/index.js +1 -1
  59. package/package.json +11 -13
  60. package/dist/__synced__/appHomeRemoteComponents.synced.d.ts +0 -28
  61. package/dist/__synced__/appHomeRemoteComponents.synced.js +0 -21
  62. package/dist/__synced__/crmRemoteComponents.synced.d.ts +0 -66
  63. package/dist/__synced__/crmRemoteComponents.synced.js +0 -15
  64. package/dist/__synced__/experimentalRemoteComponents.synced.d.ts +0 -94
  65. package/dist/__synced__/experimentalRemoteComponents.synced.js +0 -56
@@ -1,5 +1,6 @@
1
+ import { vi } from 'vitest';
1
2
  // Set up the mock before importing the module
2
- const mockFetchCrmProperties = jest.fn();
3
+ const mockFetchCrmProperties = vi.fn();
3
4
  const mockSelf = {
4
5
  fetchCrmProperties: mockFetchCrmProperties,
5
6
  };
@@ -12,7 +13,7 @@ import { fetchCrmProperties } from '../../../crm/utils/fetchCrmProperties';
12
13
  const DEFAULT_OPTIONS = {};
13
14
  describe('fetchCrmProperties', () => {
14
15
  beforeEach(() => {
15
- jest.clearAllMocks();
16
+ vi.clearAllMocks();
16
17
  });
17
18
  it('successfully fetches CRM properties', async () => {
18
19
  const mockApiResponse = {
@@ -20,15 +21,15 @@ describe('fetchCrmProperties', () => {
20
21
  firstname: 'Test value for firstname',
21
22
  lastname: 'Test value for lastname',
22
23
  },
23
- cleanup: jest.fn(),
24
+ cleanup: vi.fn(),
24
25
  };
25
26
  const mockResponse = {
26
27
  ok: true,
27
- json: jest.fn().mockResolvedValue(mockApiResponse),
28
+ json: vi.fn().mockResolvedValue(mockApiResponse),
28
29
  };
29
30
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
30
31
  const propertyNames = ['firstname', 'lastname'];
31
- const result = await fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS);
32
+ const result = await fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS);
32
33
  expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames, expect.any(Function), DEFAULT_OPTIONS);
33
34
  expect(result).toEqual({
34
35
  data: {
@@ -46,15 +47,15 @@ describe('fetchCrmProperties', () => {
46
47
  email: 'john@example.com',
47
48
  phone: null,
48
49
  },
49
- cleanup: jest.fn(),
50
+ cleanup: vi.fn(),
50
51
  };
51
52
  const mockResponse = {
52
53
  ok: true,
53
- json: jest.fn().mockResolvedValue(mockApiResponse),
54
+ json: vi.fn().mockResolvedValue(mockApiResponse),
54
55
  };
55
56
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
56
57
  const propertyNames = ['firstname', 'lastname', 'email', 'phone'];
57
- const result = await fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS);
58
+ const result = await fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS);
58
59
  expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames, expect.any(Function), DEFAULT_OPTIONS);
59
60
  expect(result).toEqual({
60
61
  data: {
@@ -70,29 +71,29 @@ describe('fetchCrmProperties', () => {
70
71
  const mockResponse = {
71
72
  ok: false,
72
73
  statusText: 'Not Found',
73
- json: jest.fn().mockResolvedValue({}),
74
+ json: vi.fn().mockResolvedValue({}),
74
75
  };
75
76
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
76
77
  const propertyNames = ['firstname'];
77
- await expect(fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Failed to fetch CRM properties: Not Found');
78
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Failed to fetch CRM properties: Not Found');
78
79
  });
79
80
  it('throws an error when fetch fails', async () => {
80
81
  mockFetchCrmProperties.mockRejectedValue(new Error('Network error'));
81
82
  const propertyNames = ['firstname'];
82
- await expect(fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Network error');
83
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Network error');
83
84
  });
84
85
  it('throws an error if the response is not an object', async () => {
85
86
  const mockApiResponse = {
86
87
  data: 'Invalid response',
87
- cleanup: jest.fn(),
88
+ cleanup: vi.fn(),
88
89
  };
89
90
  const mockResponse = {
90
91
  ok: true,
91
- json: jest.fn().mockResolvedValue(mockApiResponse),
92
+ json: vi.fn().mockResolvedValue(mockApiResponse),
92
93
  };
93
94
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
94
95
  const propertyNames = ['firstname'];
95
- await expect(fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Invalid response format');
96
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Invalid response format');
96
97
  });
97
98
  it('throws an error if response contains invalid property values', async () => {
98
99
  const mockApiResponse = {
@@ -101,15 +102,15 @@ describe('fetchCrmProperties', () => {
101
102
  lastname: 123,
102
103
  email: 'john@example.com',
103
104
  },
104
- cleanup: jest.fn(),
105
+ cleanup: vi.fn(),
105
106
  };
106
107
  const mockResponse = {
107
108
  ok: true,
108
- json: jest.fn().mockResolvedValue(mockApiResponse),
109
+ json: vi.fn().mockResolvedValue(mockApiResponse),
109
110
  };
110
111
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
111
112
  const propertyNames = ['firstname', 'lastname', 'email'];
112
- await expect(fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Invalid response format');
113
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS)).rejects.toThrow('Invalid response format');
113
114
  });
114
115
  it('passes the propertiesUpdatedCallback and allows it to be called', async () => {
115
116
  let capturedCallback;
@@ -118,18 +119,18 @@ describe('fetchCrmProperties', () => {
118
119
  firstname: 'Initial',
119
120
  lastname: 'Initial',
120
121
  },
121
- cleanup: jest.fn(),
122
+ cleanup: vi.fn(),
122
123
  };
123
124
  const mockResponse = {
124
125
  ok: true,
125
- json: jest.fn().mockResolvedValue(mockApiResponse),
126
+ json: vi.fn().mockResolvedValue(mockApiResponse),
126
127
  };
127
128
  mockFetchCrmProperties.mockImplementation((propertyNames, callback) => {
128
129
  capturedCallback = callback;
129
130
  return Promise.resolve(mockResponse);
130
131
  });
131
132
  const propertyNames = ['firstname', 'lastname'];
132
- const mockCallback = jest.fn();
133
+ const mockCallback = vi.fn();
133
134
  await fetchCrmProperties(propertyNames, mockCallback, DEFAULT_OPTIONS);
134
135
  expect(typeof capturedCallback).toBe('function');
135
136
  // Simulate the callback being called with new properties
@@ -146,18 +147,18 @@ describe('fetchCrmProperties', () => {
146
147
  firstname: 'Initial',
147
148
  lastname: null,
148
149
  },
149
- cleanup: jest.fn(),
150
+ cleanup: vi.fn(),
150
151
  };
151
152
  const mockResponse = {
152
153
  ok: true,
153
- json: jest.fn().mockResolvedValue(mockApiResponse),
154
+ json: vi.fn().mockResolvedValue(mockApiResponse),
154
155
  };
155
156
  mockFetchCrmProperties.mockImplementation((propertyNames, callback) => {
156
157
  capturedCallback = callback;
157
158
  return Promise.resolve(mockResponse);
158
159
  });
159
160
  const propertyNames = ['firstname', 'lastname'];
160
- const mockCallback = jest.fn();
161
+ const mockCallback = vi.fn();
161
162
  await fetchCrmProperties(propertyNames, mockCallback, DEFAULT_OPTIONS);
162
163
  expect(typeof capturedCallback).toBe('function');
163
164
  // Simulate the callback being called with new properties including null values
@@ -173,11 +174,11 @@ describe('fetchCrmProperties', () => {
173
174
  firstname: 'John',
174
175
  lastname: 'Doe',
175
176
  },
176
- cleanup: jest.fn(),
177
+ cleanup: vi.fn(),
177
178
  };
178
179
  const mockResponse = {
179
180
  ok: true,
180
- json: jest.fn().mockResolvedValue(mockApiResponse),
181
+ json: vi.fn().mockResolvedValue(mockApiResponse),
181
182
  };
182
183
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
183
184
  const propertyNames = ['firstname', 'lastname'];
@@ -192,7 +193,7 @@ describe('fetchCrmProperties', () => {
192
193
  },
193
194
  },
194
195
  };
195
- await fetchCrmProperties(propertyNames, jest.fn(), options);
196
+ await fetchCrmProperties(propertyNames, vi.fn(), options);
196
197
  expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames, expect.any(Function), options);
197
198
  });
198
199
  it('preserves error handling with formatting options', async () => {
@@ -206,27 +207,27 @@ describe('fetchCrmProperties', () => {
206
207
  },
207
208
  },
208
209
  };
209
- await expect(fetchCrmProperties(propertyNames, jest.fn(), options)).rejects.toThrow('Network error');
210
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), options)).rejects.toThrow('Network error');
210
211
  expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames, expect.any(Function), options);
211
212
  });
212
213
  it('preserves response validation with formatting options', async () => {
213
214
  const mockApiResponse = {
214
215
  data: 'Invalid response',
215
- cleanup: jest.fn(),
216
+ cleanup: vi.fn(),
216
217
  };
217
218
  const mockResponse = {
218
219
  ok: true,
219
- json: jest.fn().mockResolvedValue(mockApiResponse),
220
+ json: vi.fn().mockResolvedValue(mockApiResponse),
220
221
  };
221
222
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
222
223
  const propertyNames = ['firstname'];
223
224
  const options = {
224
225
  propertiesToFormat: 'all',
225
226
  };
226
- await expect(fetchCrmProperties(propertyNames, jest.fn(), options)).rejects.toThrow('Invalid response format');
227
+ await expect(fetchCrmProperties(propertyNames, vi.fn(), options)).rejects.toThrow('Invalid response format');
227
228
  });
228
229
  it('returns cleanup function that can be called', async () => {
229
- const mockCleanup = jest.fn();
230
+ const mockCleanup = vi.fn();
230
231
  const mockApiResponse = {
231
232
  data: {
232
233
  firstname: 'John',
@@ -235,11 +236,11 @@ describe('fetchCrmProperties', () => {
235
236
  };
236
237
  const mockResponse = {
237
238
  ok: true,
238
- json: jest.fn().mockResolvedValue(mockApiResponse),
239
+ json: vi.fn().mockResolvedValue(mockApiResponse),
239
240
  };
240
241
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
241
242
  const propertyNames = ['firstname'];
242
- const result = await fetchCrmProperties(propertyNames, jest.fn(), DEFAULT_OPTIONS);
243
+ const result = await fetchCrmProperties(propertyNames, vi.fn(), DEFAULT_OPTIONS);
243
244
  expect(result.cleanup).toBe(mockCleanup);
244
245
  expect(typeof result.cleanup).toBe('function');
245
246
  // Verify cleanup can be called without errors
@@ -1,4 +1,4 @@
1
- export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, } from '../__synced__/crmRemoteComponents.synced';
1
+ export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, } from '../__synced__/remoteComponents.synced';
2
2
  export { useCrmProperties } from './hooks/useCrmProperties';
3
3
  export { useAssociations } from './hooks/useAssociations';
4
4
  export type * from '../__synced__/types/crm.synced';
package/dist/crm/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, } from '../__synced__/crmRemoteComponents.synced';
1
+ export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, } from '../__synced__/remoteComponents.synced';
2
2
  export { useCrmProperties } from './hooks/useCrmProperties';
3
3
  export { useAssociations } from './hooks/useAssociations';
@@ -1,4 +1,4 @@
1
1
  export { useCrmProperties } from '../crm/hooks/useCrmProperties';
2
2
  export { useAssociations } from '../crm/hooks/useAssociations';
3
- export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from '../__synced__/experimentalRemoteComponents.synced';
3
+ export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from '../__synced__/remoteComponents.synced';
4
4
  export type * from '../__synced__/experimental/types.synced';
@@ -1,3 +1,3 @@
1
1
  export { useCrmProperties } from '../crm/hooks/useCrmProperties';
2
2
  export { useAssociations } from '../crm/hooks/useAssociations';
3
- export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from '../__synced__/experimentalRemoteComponents.synced';
3
+ export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from '../__synced__/remoteComponents.synced';
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow, List, Text } from '../../../index';
4
+ import { render } from '../index';
5
+ function MyComponent() {
6
+ return (_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: _jsx(List, { children: _jsx(Text, { children: "Item 1" }) }), children: "Click me!" }) }), _jsx(Alert, { title: "My Alert" })] }));
7
+ }
8
+ describe('debugging', () => {
9
+ describe('toString', () => {
10
+ it('should allow the root node to be converted to a string', () => {
11
+ const { getRootNode } = render(_jsx(MyComponent, {}));
12
+ expect(getRootNode().toString()).toMatchSnapshot();
13
+ });
14
+ it('should allow a component node to be converted to a string', () => {
15
+ const { find } = render(_jsx(MyComponent, {}));
16
+ expect(find(Button).toString()).toMatchSnapshot();
17
+ });
18
+ });
19
+ describe('debugLog()', () => {
20
+ let consoleLogSpy;
21
+ beforeEach(() => {
22
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
23
+ });
24
+ afterEach(() => {
25
+ consoleLogSpy.mockRestore();
26
+ });
27
+ it('should allow debug logging the root node', () => {
28
+ const { debugLog } = render(_jsx(MyComponent, {}));
29
+ debugLog('MY COMPONENT TREE');
30
+ expect(consoleLogSpy.mock.calls).toMatchSnapshot();
31
+ });
32
+ it('should allow debug logging a component element node', () => {
33
+ const { find } = render(_jsx(MyComponent, {}));
34
+ find(Button).debugLog('MY BUTTON');
35
+ expect(consoleLogSpy.mock.calls).toMatchSnapshot();
36
+ });
37
+ it('should allow debug logging a component element node without a label', () => {
38
+ const { find } = render(_jsx(MyComponent, {}));
39
+ find(Button).debugLog();
40
+ expect(consoleLogSpy.mock.calls).toMatchSnapshot();
41
+ });
42
+ });
43
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow, Text } from '../../../index';
4
+ import { render } from '../index';
5
+ import { ComponentNotFoundError, FindInvalidComponentError, } from '../internal/errors';
6
+ describe('find()', () => {
7
+ it('should allow assertions against initial rendered output', () => {
8
+ const buttonLabel = 'Click me!';
9
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
10
+ const button = find(Button);
11
+ expect(button.props).toEqual({ variant: 'primary' });
12
+ expect(button.text).toEqual(buttonLabel);
13
+ });
14
+ it('should allow finding a matching component based on props object', () => {
15
+ const { find } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" })] }));
16
+ const primaryButton = find(Button, { variant: 'primary' });
17
+ expect(primaryButton.props).toMatchObject({ variant: 'primary' });
18
+ });
19
+ it('should allow finding a matching component based on a predicate function', () => {
20
+ const { find } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" })] }));
21
+ const primaryButton = find(Button, (node) => node.props.variant === 'primary');
22
+ expect(primaryButton.props).toMatchObject({ variant: 'primary' });
23
+ });
24
+ it('should throw an error when no match is found', () => {
25
+ const { find } = render(_jsx(Alert, { title: "My Alert" }));
26
+ expect(() => find(Text)).toThrow(ComponentNotFoundError);
27
+ });
28
+ it('should throw an error when target component is invalid', () => {
29
+ const { find } = render(_jsx(Alert, { title: "My Alert" }));
30
+ const MyFakeComponent = () => { };
31
+ expect(() => find(MyFakeComponent)).toThrow(FindInvalidComponentError);
32
+ });
33
+ });
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow } from '../../../index';
4
+ import { render } from '../index';
5
+ describe('findAll()', () => {
6
+ it('should allow finding all matching components', () => {
7
+ const { findAll } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" })] }));
8
+ const buttons = findAll(Button);
9
+ expect(buttons[0]?.props).toMatchObject({ variant: 'secondary' });
10
+ expect(buttons[1]?.props).toMatchObject({ variant: 'primary' });
11
+ });
12
+ });
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow, Text } from '../../../index';
4
+ import { render } from '../index';
5
+ describe('findAllChildren()', () => {
6
+ it('should allow finding all direct children from the root node', () => {
7
+ const { findAllChildren } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" }), _jsx(Alert, { title: "Another Alert" })] }));
8
+ const alerts = findAllChildren(Alert);
9
+ expect(alerts).toHaveLength(2);
10
+ expect(alerts[0]?.props).toMatchObject({ title: 'My Alert' });
11
+ expect(alerts[1]?.props).toMatchObject({ title: 'Another Alert' });
12
+ });
13
+ it('should only find direct children, not nested descendants', () => {
14
+ const { findAllChildren } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" })] }));
15
+ const buttons = findAllChildren(Button);
16
+ expect(buttons).toHaveLength(0);
17
+ });
18
+ it('should allow finding all direct children from a nested component', () => {
19
+ const { find } = render(_jsxs(_Fragment, { children: [_jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" })] }), _jsx(Alert, { title: "My Alert" })] }));
20
+ const buttonRow = find(ButtonRow);
21
+ const buttons = buttonRow.findAllChildren(Button);
22
+ expect(buttons).toHaveLength(2);
23
+ expect(buttons[0]?.props).toMatchObject({ variant: 'secondary' });
24
+ expect(buttons[1]?.props).toMatchObject({ variant: 'primary' });
25
+ });
26
+ it('should return empty array when no direct children match', () => {
27
+ const { findAllChildren } = render(_jsx(_Fragment, { children: _jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: "Button 1" }) }) }));
28
+ const texts = findAllChildren(Text);
29
+ expect(texts).toHaveLength(0);
30
+ });
31
+ it('should support matcher function', () => {
32
+ const { find } = render(_jsx(_Fragment, { children: _jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" }), _jsx(Button, { variant: "secondary", children: "Button 3" })] }) }));
33
+ const buttonRow = find(ButtonRow);
34
+ const secondaryButtons = buttonRow.findAllChildren(Button, (node) => node.props.variant === 'secondary');
35
+ expect(secondaryButtons).toHaveLength(2);
36
+ expect(secondaryButtons[0]?.props).toMatchObject({ variant: 'secondary' });
37
+ expect(secondaryButtons[1]?.props).toMatchObject({ variant: 'secondary' });
38
+ });
39
+ it('should support matcher object', () => {
40
+ const { find } = render(_jsx(_Fragment, { children: _jsxs(ButtonRow, { children: [_jsx(Button, { variant: "secondary", children: "Button 1" }), _jsx(Button, { variant: "primary", children: "Button 2" }), _jsx(Button, { variant: "secondary", children: "Button 3" })] }) }));
41
+ const buttonRow = find(ButtonRow);
42
+ const primaryButtons = buttonRow.findAllChildren(Button, {
43
+ variant: 'primary',
44
+ });
45
+ expect(primaryButtons).toHaveLength(1);
46
+ expect(primaryButtons[0]?.props).toMatchObject({ variant: 'primary' });
47
+ });
48
+ });
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow } from '../../../index';
4
+ import { render } from '../index';
5
+ import { ComponentNotFoundError, FindInvalidComponentError, } from '../internal/errors';
6
+ describe('findChild()', () => {
7
+ it('should allow finding a child from the root node', () => {
8
+ const buttonLabel = '';
9
+ const { findChild } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
10
+ expect(findChild(ButtonRow)).toBeDefined();
11
+ });
12
+ it('should allow finding a child from a nested component', () => {
13
+ const buttonLabel = '';
14
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
15
+ const buttonRow = find(ButtonRow);
16
+ expect(buttonRow.findChild(Button).props).toEqual({ variant: 'primary' });
17
+ });
18
+ it('should throw an error when no match is found', () => {
19
+ const { find } = render(_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: "Click me!" }) }));
20
+ const buttonRow = find(ButtonRow);
21
+ expect(() => buttonRow.findChild(Alert)).toThrow(ComponentNotFoundError);
22
+ });
23
+ it('should throw an error when target component is invalid', () => {
24
+ const { find } = render(_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: "Click me!" }) }));
25
+ const MyFakeComponent = () => { };
26
+ const buttonRow = find(ButtonRow);
27
+ expect(() => buttonRow.findChild(MyFakeComponent)).toThrow(FindInvalidComponentError);
28
+ });
29
+ });
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from 'vitest';
3
+ import { Alert, Button, ButtonRow, List, Text } from '../../../index';
4
+ import { render } from '../index';
5
+ import { InvalidFragmentPropArrayError } from '../internal/errors';
6
+ describe('fragments', () => {
7
+ it('should allow assertions against components with fragment props', () => {
8
+ const buttonLabel = 'Click me!';
9
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: _jsx(List, { children: _jsx(Text, { children: "Item 1" }) }), children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
10
+ expect(find(Text).text).toEqual('Item 1');
11
+ expect(find(Button).text).toEqual(buttonLabel);
12
+ });
13
+ it('should handle undefined fragment props', () => {
14
+ const buttonLabel = 'Click me!';
15
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
16
+ const button = find(Button);
17
+ const { props: buttonProps } = button;
18
+ expect(buttonProps.overlay).toBeUndefined();
19
+ expect(find(Alert).props).toEqual({ title: 'My Alert' });
20
+ });
21
+ it('should handle null fragment props', () => {
22
+ const buttonLabel = 'Click me!';
23
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: null, children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
24
+ const button = find(Button);
25
+ const { props: buttonProps } = button;
26
+ expect(buttonProps.overlay?.children).toHaveLength(0);
27
+ expect(find(Alert).props).toEqual({ title: 'My Alert' });
28
+ });
29
+ it('should throw an error if an array fragment prop is passed', () => {
30
+ const buttonLabel = 'Click me!';
31
+ const arrayProp = [1, 2, 3];
32
+ expect(() => {
33
+ render(_jsx(Button, { variant: "primary", overlay: arrayProp, children: buttonLabel }));
34
+ }).toThrow(InvalidFragmentPropArrayError);
35
+ });
36
+ it('should handle string fragment props', () => {
37
+ const buttonLabel = 'Click me!';
38
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: "My Overlay", children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
39
+ const button = find(Button);
40
+ expect(button.props.overlay?.text).toEqual('My Overlay');
41
+ expect(find(Alert).props).toEqual({ title: 'My Alert' });
42
+ });
43
+ it('should handle fragment props that are a JSX fragment', () => {
44
+ const buttonLabel = 'Click me!';
45
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: _jsx(_Fragment, { children: "My Overlay" }), children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
46
+ const button = find(Button);
47
+ expect(button.props.overlay?.text).toEqual('My Overlay');
48
+ expect(find(Alert).props).toEqual({ title: 'My Alert' });
49
+ });
50
+ it('should allow assertions against fragments rendered by a custom component', () => {
51
+ const buttonLabel = 'Click me!';
52
+ function MyOverlay() {
53
+ return (_jsx(List, { children: _jsx(Text, { children: "Item 1" }) }));
54
+ }
55
+ const { find } = render(_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: _jsx(MyOverlay, {}), children: buttonLabel }) }), _jsx(Alert, { title: "My Alert" })] }));
56
+ expect(find(Text).text).toEqual('Item 1');
57
+ expect(find(Button).text).toEqual(buttonLabel);
58
+ });
59
+ });
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { Alert, Button, ButtonRow, Text } from '../../../index';
5
+ import { render } from '../index';
6
+ import { InvalidComponentsError } from '../internal/errors';
7
+ import { createDeferred } from '../internal/utils/promise-utils';
8
+ const setTimeoutPromise = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
9
+ describe('handling invalid components', () => {
10
+ it('should throw an error if initial render contains invalid components', () => {
11
+ function MyComponent() {
12
+ return (_jsx("div", { children: _jsx("span", { children: _jsx(Button, { variant: "primary", children: "Click me!" }) }) }));
13
+ }
14
+ expect(() => render(_jsx(MyComponent, {}))).toThrow(InvalidComponentsError);
15
+ let errorMessage;
16
+ try {
17
+ render(_jsx(MyComponent, {}));
18
+ }
19
+ catch (error) {
20
+ errorMessage = String(error);
21
+ }
22
+ expect(errorMessage).toMatchSnapshot();
23
+ });
24
+ it('should throw an error if initial render contains invalid components inside a fragment', () => {
25
+ function MyComponent() {
26
+ return (_jsxs(_Fragment, { children: [_jsx(ButtonRow, { children: _jsx(Button, { variant: "primary", overlay: _jsx("div", { children: "Overlay" }), children: "Click me!" }) }), _jsx(Alert, { title: "My Alert" })] }));
27
+ }
28
+ expect(() => render(_jsx(MyComponent, {}))).toThrow(InvalidComponentsError);
29
+ });
30
+ it('should throw an error if triggering an event results in an invalid component to be rendered', () => {
31
+ function Counter() {
32
+ const [count, setCount] = useState(0);
33
+ const handleClick = () => {
34
+ setCount(count + 1);
35
+ };
36
+ return (_jsx(Button, { variant: "primary", onClick: handleClick, children: count === 0 ? 'Click me!' : _jsxs("div", { children: ["Clicked ", count, " times"] }) }));
37
+ }
38
+ const { find } = render(_jsx(Counter, {}));
39
+ expect(find(Button).text).toEqual('Click me!');
40
+ // Should throw an error because the component is invalid
41
+ expect(() => {
42
+ find(Button).trigger('onClick');
43
+ }).toThrow(InvalidComponentsError);
44
+ });
45
+ it('should throw an error if the component is invalid after an asynchronous update', async () => {
46
+ function AsyncCounter() {
47
+ const [count, setCount] = useState(0);
48
+ useEffect(() => {
49
+ setTimeout(() => {
50
+ setCount((currentCount) => currentCount + 1);
51
+ }, 10);
52
+ }, []);
53
+ return (_jsx(Button, { children: count === 0 ? 'Click me!' : _jsxs("div", { children: ["Clicked ", count, " times"] }) }));
54
+ }
55
+ const { find, waitFor } = render(_jsx(AsyncCounter, {}));
56
+ expect(find(Button).text).toEqual('Click me!');
57
+ await expect(waitFor(() => {
58
+ expect(find(Button).text).toEqual('Clicked 1 times');
59
+ })).rejects.toThrow(InvalidComponentsError);
60
+ });
61
+ it('should throw an error if the component is invalid after an asynchronous update when not using waitFor', async () => {
62
+ const usePromise = (promise) => {
63
+ const [isPending, setIsPending] = useState(true);
64
+ useEffect(() => {
65
+ setIsPending(true);
66
+ promise
67
+ .then(() => setIsPending(false))
68
+ .catch(() => setIsPending(false));
69
+ }, [promise]);
70
+ return { isPending };
71
+ };
72
+ function AsyncCounter({ promise }) {
73
+ const { isPending } = usePromise(promise);
74
+ if (isPending) {
75
+ return _jsx(Text, { children: "Loading..." });
76
+ }
77
+ return _jsx("div", { children: "Loaded!" });
78
+ }
79
+ const loadingDeferred = createDeferred();
80
+ const { find } = render(_jsx(AsyncCounter, { promise: loadingDeferred.promise }));
81
+ expect(find(Text).text).toEqual('Loading...');
82
+ loadingDeferred.resolve();
83
+ await setTimeoutPromise(0);
84
+ expect(() => {
85
+ find(Button);
86
+ }).toThrow(InvalidComponentsError);
87
+ });
88
+ });