@jmruthers/pace-core 0.5.1 → 0.5.4

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 (210) hide show
  1. package/dist/{DataTable-GX3XERFJ.js → DataTable-ZQDRE46Q.js} +7 -6
  2. package/dist/{PublicLoadingSpinner-DztrzuJr.d.ts → PublicLoadingSpinner-Bq_-BeK-.d.ts} +1 -1
  3. package/dist/RBACProvider-BO4ilsQB.d.ts +63 -0
  4. package/dist/{UnifiedAuthProvider-w66zSCUf.d.ts → UnifiedAuthProvider-DGQsy-vY.d.ts} +2 -59
  5. package/dist/{api-ETQ6YJ3C.js → api-H5A3H4IR.js} +2 -2
  6. package/dist/{chunk-T3XIA4AJ.js → chunk-5H3C2SWM.js} +14 -16
  7. package/dist/chunk-5H3C2SWM.js.map +1 -0
  8. package/dist/chunk-5SIXIV7R.js +1925 -0
  9. package/dist/chunk-5SIXIV7R.js.map +1 -0
  10. package/dist/chunk-GNTALZV3.js +17 -0
  11. package/dist/chunk-GNTALZV3.js.map +1 -0
  12. package/dist/{chunk-C5G2A4PO.js → chunk-GWSBHC4J.js} +6 -6
  13. package/dist/{chunk-XJK2J4N6.js → chunk-HD7PYDUV.js} +4 -6
  14. package/dist/{chunk-XJK2J4N6.js.map → chunk-HD7PYDUV.js.map} +1 -1
  15. package/dist/{chunk-TGDCLPP2.js → chunk-HXX35Q2M.js} +6 -21
  16. package/dist/chunk-HXX35Q2M.js.map +1 -0
  17. package/dist/{chunk-5EL3KHOQ.js → chunk-K6B7BLSE.js} +2 -2
  18. package/dist/{chunk-GSNM5D6H.js → chunk-M4RW7PIP.js} +4 -4
  19. package/dist/{chunk-U6JDHVC2.js → chunk-PVMYVQSM.js} +6 -8
  20. package/dist/{chunk-U6JDHVC2.js.map → chunk-PVMYVQSM.js.map} +1 -1
  21. package/dist/{chunk-6CR3MRZN.js → chunk-QKHFMQ5R.js} +372 -11
  22. package/dist/{chunk-6CR3MRZN.js.map → chunk-QKHFMQ5R.js.map} +1 -1
  23. package/dist/chunk-QVYBYGT2.js +428 -0
  24. package/dist/chunk-QVYBYGT2.js.map +1 -0
  25. package/dist/{chunk-OEGRKULD.js → chunk-WJARTBCT.js} +56 -1
  26. package/dist/chunk-WJARTBCT.js.map +1 -0
  27. package/dist/components.d.ts +4 -3
  28. package/dist/components.js +16 -162
  29. package/dist/components.js.map +1 -1
  30. package/dist/hooks.d.ts +2 -2
  31. package/dist/hooks.js +7 -9
  32. package/dist/hooks.js.map +1 -1
  33. package/dist/index.d.ts +8 -6
  34. package/dist/index.js +152 -17
  35. package/dist/index.js.map +1 -1
  36. package/dist/providers.d.ts +3 -2
  37. package/dist/providers.js +6 -12
  38. package/dist/rbac/index.d.ts +167 -98
  39. package/dist/rbac/index.js +48 -1881
  40. package/dist/rbac/index.js.map +1 -1
  41. package/dist/styles/core.css +0 -55
  42. package/dist/types.d.ts +2 -2
  43. package/dist/{unified-CM7T0aTK.d.ts → unified-CMPjE_fv.d.ts} +1 -1
  44. package/dist/{usePublicRouteParams-B6i0KtXW.d.ts → usePublicRouteParams-B2OcAsur.d.ts} +1 -1
  45. package/dist/utils.js +12 -14
  46. package/dist/utils.js.map +1 -1
  47. package/docs/api/classes/ErrorBoundary.md +1 -1
  48. package/docs/api/classes/InvalidScopeError.md +73 -0
  49. package/docs/api/classes/MissingUserContextError.md +66 -0
  50. package/docs/api/classes/OrganisationContextRequiredError.md +66 -0
  51. package/docs/api/classes/PermissionDeniedError.md +73 -0
  52. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  53. package/docs/api/classes/RBACAuditManager.md +270 -0
  54. package/docs/api/classes/RBACCache.md +284 -0
  55. package/docs/api/classes/RBACEngine.md +141 -0
  56. package/docs/api/classes/RBACError.md +76 -0
  57. package/docs/api/classes/RBACNotInitializedError.md +66 -0
  58. package/docs/api/classes/SecureSupabaseClient.md +135 -0
  59. package/docs/api/interfaces/AggregateConfig.md +1 -1
  60. package/docs/api/interfaces/ButtonProps.md +1 -1
  61. package/docs/api/interfaces/CardProps.md +1 -1
  62. package/docs/api/interfaces/ColorPalette.md +1 -1
  63. package/docs/api/interfaces/ColorShade.md +1 -1
  64. package/docs/api/interfaces/DataAccessRecord.md +96 -0
  65. package/docs/api/interfaces/DataTableAction.md +1 -1
  66. package/docs/api/interfaces/DataTableColumn.md +1 -1
  67. package/docs/api/interfaces/DataTableProps.md +1 -1
  68. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  69. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  70. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +235 -0
  71. package/docs/api/interfaces/EventContextType.md +1 -1
  72. package/docs/api/interfaces/EventLogoProps.md +1 -1
  73. package/docs/api/interfaces/EventProviderProps.md +1 -1
  74. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  75. package/docs/api/interfaces/FileUploadProps.md +1 -1
  76. package/docs/api/interfaces/FooterProps.md +1 -1
  77. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  78. package/docs/api/interfaces/InputProps.md +1 -1
  79. package/docs/api/interfaces/LabelProps.md +1 -1
  80. package/docs/api/interfaces/LoginFormProps.md +1 -1
  81. package/docs/api/interfaces/NavigationAccessRecord.md +107 -0
  82. package/docs/api/interfaces/NavigationContextType.md +164 -0
  83. package/docs/api/interfaces/NavigationGuardProps.md +139 -0
  84. package/docs/api/interfaces/NavigationItem.md +1 -1
  85. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  86. package/docs/api/interfaces/NavigationProviderProps.md +117 -0
  87. package/docs/api/interfaces/Organisation.md +1 -1
  88. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  89. package/docs/api/interfaces/OrganisationMembership.md +2 -2
  90. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  91. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  92. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  93. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  94. package/docs/api/interfaces/PageAccessRecord.md +85 -0
  95. package/docs/api/interfaces/PagePermissionContextType.md +140 -0
  96. package/docs/api/interfaces/PagePermissionGuardProps.md +153 -0
  97. package/docs/api/interfaces/PagePermissionProviderProps.md +119 -0
  98. package/docs/api/interfaces/PaletteData.md +1 -1
  99. package/docs/api/interfaces/PermissionEnforcerProps.md +153 -0
  100. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  101. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  102. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  103. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  104. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  105. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  106. package/docs/api/interfaces/RBACConfig.md +99 -0
  107. package/docs/api/interfaces/RBACContextType.md +474 -0
  108. package/docs/api/interfaces/RBACLogger.md +112 -0
  109. package/docs/api/interfaces/RBACProviderProps.md +107 -0
  110. package/docs/api/interfaces/RoleBasedRouterContextType.md +151 -0
  111. package/docs/api/interfaces/RoleBasedRouterProps.md +156 -0
  112. package/docs/api/interfaces/RouteAccessRecord.md +107 -0
  113. package/docs/api/interfaces/RouteConfig.md +121 -0
  114. package/docs/api/interfaces/SecureDataContextType.md +168 -0
  115. package/docs/api/interfaces/SecureDataProviderProps.md +132 -0
  116. package/docs/api/interfaces/StorageConfig.md +1 -1
  117. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  118. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  119. package/docs/api/interfaces/StorageListOptions.md +1 -1
  120. package/docs/api/interfaces/StorageListResult.md +1 -1
  121. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  122. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  123. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  124. package/docs/api/interfaces/StyleImport.md +1 -1
  125. package/docs/api/interfaces/ToastActionElement.md +1 -1
  126. package/docs/api/interfaces/ToastProps.md +1 -1
  127. package/docs/api/interfaces/UnifiedAuthContextType.md +85 -85
  128. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  129. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  130. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  131. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  132. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  133. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  134. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  135. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  136. package/docs/api/interfaces/UserEventAccess.md +11 -11
  137. package/docs/api/interfaces/UserMenuProps.md +1 -1
  138. package/docs/api/interfaces/UserProfile.md +1 -1
  139. package/docs/api/modules.md +2244 -3
  140. package/docs/migration-guide.md +43 -18
  141. package/docs/styles/README.md +187 -98
  142. package/docs/usage.md +32 -7
  143. package/package.json +2 -2
  144. package/src/components/Footer/Footer.test.tsx +482 -0
  145. package/src/components/Form/Form.test.tsx +1158 -0
  146. package/src/components/Header/Header.test.tsx +582 -0
  147. package/src/components/Header/Header.tsx +1 -1
  148. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +489 -0
  149. package/src/components/Input/Input.test.tsx +466 -0
  150. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +450 -0
  151. package/src/components/LoginForm/LoginForm.test.tsx +816 -0
  152. package/src/components/NavigationMenu/NavigationMenu.test.tsx +883 -0
  153. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +748 -0
  154. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +891 -0
  155. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +475 -0
  156. package/src/components/PasswordReset/PasswordChangeForm.test.tsx +621 -0
  157. package/src/components/PasswordReset/PasswordResetForm.test.tsx +605 -0
  158. package/src/components/Select/Select.test.tsx +948 -0
  159. package/src/components/SuperAdminGuard.tsx +1 -1
  160. package/src/components/Toast/Toast.test.tsx +586 -0
  161. package/src/components/Tooltip/Tooltip.test.tsx +852 -0
  162. package/src/components/UserMenu/UserMenu.test.tsx +702 -0
  163. package/src/components/UserMenu/UserMenu.tsx +2 -2
  164. package/src/hooks/useDebounce.test.ts +375 -0
  165. package/src/hooks/useOrganisationPermissions.test.ts +528 -0
  166. package/src/hooks/useOrganisationSecurity.test.ts +734 -0
  167. package/src/hooks/usePermissionCache.test.ts +542 -0
  168. package/src/hooks/usePermissionCache.ts +1 -1
  169. package/src/index.ts +2 -3
  170. package/src/providers/UnifiedAuthProvider.tsx +2 -2
  171. package/src/providers/index.ts +3 -1
  172. package/src/rbac/__tests__/integration.test.tsx +218 -0
  173. package/src/rbac/api.test.ts +952 -0
  174. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +843 -0
  175. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1007 -0
  176. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +806 -0
  177. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +741 -0
  178. package/src/rbac/hooks/index.ts +21 -0
  179. package/src/rbac/hooks/useCan.test.ts +461 -0
  180. package/src/rbac/hooks/usePermissions.test.ts +364 -0
  181. package/src/rbac/hooks/usePermissions.ts +567 -0
  182. package/src/rbac/hooks/useRBAC.simple.test.ts +90 -0
  183. package/src/rbac/hooks/useRBAC.test.ts +551 -0
  184. package/src/{hooks → rbac/hooks}/useRBAC.ts +7 -7
  185. package/src/rbac/index.ts +5 -10
  186. package/src/{providers → rbac/providers}/RBACProvider.tsx +6 -6
  187. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +687 -0
  188. package/src/rbac/providers/index.ts +11 -0
  189. package/src/styles/core.css +0 -55
  190. package/src/utils/formatDate.test.ts +241 -0
  191. package/dist/chunk-AUE24LVR.js +0 -268
  192. package/dist/chunk-AUE24LVR.js.map +0 -1
  193. package/dist/chunk-COBPIXXQ.js +0 -379
  194. package/dist/chunk-COBPIXXQ.js.map +0 -1
  195. package/dist/chunk-OEGRKULD.js.map +0 -1
  196. package/dist/chunk-OYRY44Q2.js +0 -62
  197. package/dist/chunk-OYRY44Q2.js.map +0 -1
  198. package/dist/chunk-T3XIA4AJ.js.map +0 -1
  199. package/dist/chunk-TGDCLPP2.js.map +0 -1
  200. package/src/components/RBAC/PagePermissionGuard.tsx +0 -287
  201. package/src/components/RBAC/RBACGuard.tsx +0 -143
  202. package/src/components/RBAC/RBACProvider.tsx +0 -186
  203. package/src/components/RBAC/RoleBasedContent.tsx +0 -129
  204. package/src/components/RBAC/index.ts +0 -23
  205. package/src/rbac/hooks.ts +0 -570
  206. /package/dist/{DataTable-GX3XERFJ.js.map → DataTable-ZQDRE46Q.js.map} +0 -0
  207. /package/dist/{api-ETQ6YJ3C.js.map → api-H5A3H4IR.js.map} +0 -0
  208. /package/dist/{chunk-C5G2A4PO.js.map → chunk-GWSBHC4J.js.map} +0 -0
  209. /package/dist/{chunk-5EL3KHOQ.js.map → chunk-K6B7BLSE.js.map} +0 -0
  210. /package/dist/{chunk-GSNM5D6H.js.map → chunk-M4RW7PIP.js.map} +0 -0
@@ -0,0 +1,948 @@
1
+ /**
2
+ * @file Select Component Tests
3
+ * @description Comprehensive test suite for Select component and its sub-components
4
+ * @package @jmruthers/pace-core
5
+ */
6
+
7
+ import React from 'react';
8
+ import { screen, waitFor } from '@testing-library/react';
9
+ import userEvent from '@testing-library/user-event';
10
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
11
+ import {
12
+ Select,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectGroup,
18
+ SelectLabel,
19
+ SelectSeparator
20
+ } from './Select';
21
+ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
22
+
23
+ // Mock lucide-react icons
24
+ vi.mock('lucide-react', () => ({
25
+ Search: ({ className, ...props }: any) => <div data-testid="search-icon" className={className} {...props} />,
26
+ X: ({ className, ...props }: any) => <div data-testid="x-icon" className={className} {...props} />,
27
+ ChevronDown: ({ className, ...props }: any) => <div data-testid="chevron-down" className={className} {...props} />,
28
+ Check: ({ className, ...props }: any) => <div data-testid="check-icon" className={className} {...props} />,
29
+ }));
30
+
31
+ // Mock Button component
32
+ vi.mock('../Button/Button', () => ({
33
+ Button: React.forwardRef<HTMLButtonElement, any>(({ children, onClick, onKeyDown, ...props }, ref) => (
34
+ <button ref={ref} onClick={onClick} onKeyDown={onKeyDown} {...props}>
35
+ {children}
36
+ </button>
37
+ )),
38
+ }));
39
+
40
+ // Mock cn utility
41
+ vi.mock('../../utils/cn', () => ({
42
+ cn: (...classes: any[]) => classes.filter(Boolean).join(' '),
43
+ }));
44
+
45
+ describe('Select Component', () => {
46
+ const defaultOptions = [
47
+ { value: 'option1', label: 'Option 1' },
48
+ { value: 'option2', label: 'Option 2' },
49
+ { value: 'option3', label: 'Option 3' },
50
+ ];
51
+
52
+ const renderSelect = (props: any = {}, options = defaultOptions) => {
53
+ return renderWithProviders(
54
+ <Select {...props}>
55
+ <SelectTrigger>
56
+ <SelectValue placeholder="Select an option..." />
57
+ </SelectTrigger>
58
+ <SelectContent>
59
+ {options.map(option => (
60
+ <SelectItem key={option.value} value={option.value}>
61
+ {option.label}
62
+ </SelectItem>
63
+ ))}
64
+ </SelectContent>
65
+ </Select>
66
+ );
67
+ };
68
+
69
+ beforeEach(() => {
70
+ vi.clearAllMocks();
71
+ });
72
+
73
+ describe('Basic Rendering', () => {
74
+ it('renders with default props', () => {
75
+ renderSelect();
76
+
77
+ expect(screen.getByTestId('select-root')).toBeInTheDocument();
78
+ expect(screen.getByTestId('select-trigger')).toBeInTheDocument();
79
+ expect(screen.getByTestId('select-value')).toBeInTheDocument();
80
+ expect(screen.getByText('Select an option...')).toBeInTheDocument();
81
+ });
82
+
83
+ it('renders with custom placeholder', () => {
84
+ renderSelect({}, []);
85
+
86
+ const selectValue = screen.getByTestId('select-value');
87
+ expect(selectValue).toHaveTextContent('Select an option...');
88
+ });
89
+
90
+ it('renders with custom className', () => {
91
+ renderSelect({ className: 'custom-select' });
92
+
93
+ expect(screen.getByTestId('select-root')).toHaveClass('custom-select');
94
+ });
95
+
96
+ it('renders with disabled state', () => {
97
+ renderSelect({ disabled: true });
98
+
99
+ const trigger = screen.getByTestId('select-trigger');
100
+ expect(trigger).toBeDisabled();
101
+ });
102
+
103
+ it('renders with default value', () => {
104
+ renderSelect({ defaultValue: 'option1' });
105
+
106
+ expect(screen.getByTestId('select-root')).toHaveAttribute('data-value', 'option1');
107
+ });
108
+
109
+ it('renders with controlled value', () => {
110
+ renderSelect({ value: 'option2' });
111
+
112
+ expect(screen.getByTestId('select-root')).toHaveAttribute('data-value', 'option2');
113
+ });
114
+ });
115
+
116
+ describe('Trigger Component', () => {
117
+ it('renders trigger with proper ARIA attributes', () => {
118
+ renderSelect();
119
+
120
+ const trigger = screen.getByTestId('select-trigger');
121
+ expect(trigger).toHaveAttribute('role', 'combobox');
122
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
123
+ expect(trigger).toHaveAttribute('aria-haspopup', 'listbox');
124
+ });
125
+
126
+ it('shows chevron icon', () => {
127
+ renderSelect();
128
+
129
+ expect(screen.getByTestId('chevron-down')).toBeInTheDocument();
130
+ });
131
+
132
+ it('rotates chevron when open', async () => {
133
+ const user = userEvent.setup();
134
+ renderSelect();
135
+
136
+ const trigger = screen.getByTestId('select-trigger');
137
+ await user.click(trigger);
138
+
139
+ const chevron = screen.getByTestId('chevron-down');
140
+ expect(chevron).toHaveClass('rotate-180');
141
+ });
142
+
143
+ it('handles asChild prop correctly', () => {
144
+ renderWithProviders(
145
+ <Select>
146
+ <SelectTrigger asChild>
147
+ <button>Custom Trigger</button>
148
+ </SelectTrigger>
149
+ <SelectContent>
150
+ <SelectItem value="test">Test</SelectItem>
151
+ </SelectContent>
152
+ </Select>
153
+ );
154
+
155
+ expect(screen.getByText('Custom Trigger')).toBeInTheDocument();
156
+ expect(screen.getByTestId('chevron-down')).toBeInTheDocument();
157
+ });
158
+ });
159
+
160
+ describe('Value Component', () => {
161
+ it('shows placeholder when no value selected', () => {
162
+ renderSelect();
163
+
164
+ expect(screen.getByText('Select an option...')).toBeInTheDocument();
165
+ });
166
+
167
+ it('shows selected text when value is selected', async () => {
168
+ const user = userEvent.setup();
169
+ renderSelect();
170
+
171
+ const trigger = screen.getByTestId('select-trigger');
172
+ await user.click(trigger);
173
+
174
+ const option = screen.getByText('Option 1');
175
+ await user.click(option);
176
+
177
+ expect(screen.getByText('Option 1')).toBeInTheDocument();
178
+ });
179
+
180
+ it('shows custom children when provided', () => {
181
+ renderWithProviders(
182
+ <Select>
183
+ <SelectTrigger>
184
+ <SelectValue>
185
+ <span>Custom Value Display</span>
186
+ </SelectValue>
187
+ </SelectTrigger>
188
+ <SelectContent>
189
+ <SelectItem value="test">Test</SelectItem>
190
+ </SelectContent>
191
+ </Select>
192
+ );
193
+
194
+ expect(screen.getByText('Custom Value Display')).toBeInTheDocument();
195
+ });
196
+ });
197
+
198
+ describe('Content Component', () => {
199
+ it('renders content when open', async () => {
200
+ const user = userEvent.setup();
201
+ renderSelect();
202
+
203
+ const trigger = screen.getByTestId('select-trigger');
204
+ await user.click(trigger);
205
+
206
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
207
+ expect(screen.getByRole('listbox')).toBeInTheDocument();
208
+ });
209
+
210
+ it('does not render content when closed', () => {
211
+ renderSelect();
212
+
213
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
214
+ });
215
+
216
+ it('renders all options', async () => {
217
+ const user = userEvent.setup();
218
+ renderSelect();
219
+
220
+ const trigger = screen.getByTestId('select-trigger');
221
+ await user.click(trigger);
222
+
223
+ expect(screen.getByText('Option 1')).toBeInTheDocument();
224
+ expect(screen.getByText('Option 2')).toBeInTheDocument();
225
+ expect(screen.getByText('Option 3')).toBeInTheDocument();
226
+ });
227
+
228
+ it('renders with custom className', async () => {
229
+ const user = userEvent.setup();
230
+ renderWithProviders(
231
+ <Select>
232
+ <SelectTrigger>
233
+ <SelectValue />
234
+ </SelectTrigger>
235
+ <SelectContent className="custom-content">
236
+ <SelectItem value="test">Test</SelectItem>
237
+ </SelectContent>
238
+ </Select>
239
+ );
240
+
241
+ const trigger = screen.getByTestId('select-trigger');
242
+ await user.click(trigger);
243
+
244
+ expect(screen.getByTestId('select-content')).toHaveClass('custom-content');
245
+ });
246
+
247
+ it('renders with custom maxHeight', async () => {
248
+ const user = userEvent.setup();
249
+ renderWithProviders(
250
+ <Select>
251
+ <SelectTrigger>
252
+ <SelectValue />
253
+ </SelectTrigger>
254
+ <SelectContent maxHeight="10rem">
255
+ <SelectItem value="test">Test</SelectItem>
256
+ </SelectContent>
257
+ </Select>
258
+ );
259
+
260
+ const trigger = screen.getByTestId('select-trigger');
261
+ await user.click(trigger);
262
+
263
+ const content = screen.getByTestId('select-content');
264
+ // Check that the maxHeight is applied via the style prop
265
+ expect(content.style.maxHeight).toBe('10rem');
266
+ });
267
+ });
268
+
269
+ describe('Item Component', () => {
270
+ it('renders item with proper attributes', async () => {
271
+ const user = userEvent.setup();
272
+ renderSelect();
273
+
274
+ const trigger = screen.getByTestId('select-trigger');
275
+ await user.click(trigger);
276
+
277
+ const item = screen.getByText('Option 1');
278
+ expect(item).toHaveAttribute('data-value', 'option1');
279
+ expect(item).toHaveAttribute('role', 'option');
280
+ expect(item).toHaveAttribute('aria-selected', 'false');
281
+ });
282
+
283
+ it('shows check icon when selected', async () => {
284
+ const user = userEvent.setup();
285
+ renderSelect({ defaultValue: 'option1' });
286
+
287
+ const trigger = screen.getByTestId('select-trigger');
288
+ await user.click(trigger);
289
+
290
+ const item = screen.getByText('Option 1');
291
+ expect(item).toHaveAttribute('aria-selected', 'true');
292
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
293
+ });
294
+
295
+ it('handles disabled state', async () => {
296
+ const user = userEvent.setup();
297
+ renderWithProviders(
298
+ <Select>
299
+ <SelectTrigger>
300
+ <SelectValue />
301
+ </SelectTrigger>
302
+ <SelectContent>
303
+ <SelectItem value="disabled" disabled>
304
+ Disabled Option
305
+ </SelectItem>
306
+ </SelectContent>
307
+ </Select>
308
+ );
309
+
310
+ const trigger = screen.getByTestId('select-trigger');
311
+ await user.click(trigger);
312
+
313
+ const item = screen.getByText('Disabled Option');
314
+ expect(item).toHaveAttribute('data-disabled', 'true');
315
+ expect(item).toHaveClass('pointer-events-none', 'opacity-50');
316
+ });
317
+
318
+ it('handles custom onClick', async () => {
319
+ const user = userEvent.setup();
320
+ const onClick = vi.fn();
321
+
322
+ renderWithProviders(
323
+ <Select>
324
+ <SelectTrigger>
325
+ <SelectValue />
326
+ </SelectTrigger>
327
+ <SelectContent>
328
+ <SelectItem value="test" onClick={onClick}>
329
+ Test Option
330
+ </SelectItem>
331
+ </SelectContent>
332
+ </Select>
333
+ );
334
+
335
+ const trigger = screen.getByTestId('select-trigger');
336
+ await user.click(trigger);
337
+
338
+ const item = screen.getByText('Test Option');
339
+ await user.click(item);
340
+
341
+ expect(onClick).toHaveBeenCalledTimes(1);
342
+ });
343
+ });
344
+
345
+ describe('User Interactions', () => {
346
+ it('opens dropdown on trigger click', async () => {
347
+ const user = userEvent.setup();
348
+ renderSelect();
349
+
350
+ const trigger = screen.getByTestId('select-trigger');
351
+ await user.click(trigger);
352
+
353
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
354
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
355
+ });
356
+
357
+ it('closes dropdown on item selection', async () => {
358
+ const user = userEvent.setup();
359
+ renderSelect();
360
+
361
+ const trigger = screen.getByTestId('select-trigger');
362
+ await user.click(trigger);
363
+
364
+ const option = screen.getByText('Option 1');
365
+ await user.click(option);
366
+
367
+ await waitFor(() => {
368
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
369
+ });
370
+ });
371
+
372
+ it('updates value on item selection', async () => {
373
+ const user = userEvent.setup();
374
+ const onValueChange = vi.fn();
375
+ renderSelect({ onValueChange });
376
+
377
+ const trigger = screen.getByTestId('select-trigger');
378
+ await user.click(trigger);
379
+
380
+ const option = screen.getByText('Option 2');
381
+ await user.click(option);
382
+
383
+ expect(onValueChange).toHaveBeenCalledWith('option2');
384
+ });
385
+
386
+ it('closes dropdown on outside click', async () => {
387
+ const user = userEvent.setup();
388
+ renderSelect();
389
+
390
+ const trigger = screen.getByTestId('select-trigger');
391
+ await user.click(trigger);
392
+
393
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
394
+
395
+ // Click outside
396
+ await user.click(document.body);
397
+
398
+ await waitFor(() => {
399
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
400
+ });
401
+ });
402
+ });
403
+
404
+ describe('Keyboard Navigation', () => {
405
+ it('opens dropdown on Enter key', async () => {
406
+ const user = userEvent.setup();
407
+ renderSelect();
408
+
409
+ const trigger = screen.getByTestId('select-trigger');
410
+ trigger.focus();
411
+ await user.keyboard('{Enter}');
412
+
413
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
414
+ });
415
+
416
+ it('opens dropdown on Space key', async () => {
417
+ const user = userEvent.setup();
418
+ renderSelect();
419
+
420
+ const trigger = screen.getByTestId('select-trigger');
421
+ trigger.focus();
422
+ await user.keyboard(' ');
423
+
424
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
425
+ });
426
+
427
+ it('opens dropdown on ArrowDown key', async () => {
428
+ const user = userEvent.setup();
429
+ renderSelect();
430
+
431
+ const trigger = screen.getByTestId('select-trigger');
432
+ trigger.focus();
433
+ await user.keyboard('{ArrowDown}');
434
+
435
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
436
+ });
437
+
438
+ it('opens dropdown on ArrowUp key', async () => {
439
+ const user = userEvent.setup();
440
+ renderSelect();
441
+
442
+ const trigger = screen.getByTestId('select-trigger');
443
+ trigger.focus();
444
+ await user.keyboard('{ArrowUp}');
445
+
446
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
447
+ });
448
+
449
+ it('closes dropdown on Escape key', async () => {
450
+ const user = userEvent.setup();
451
+ renderSelect();
452
+
453
+ const trigger = screen.getByTestId('select-trigger');
454
+ await user.click(trigger);
455
+
456
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
457
+
458
+ await user.keyboard('{Escape}');
459
+
460
+ await waitFor(() => {
461
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
462
+ });
463
+ });
464
+
465
+ it('selects item on click', async () => {
466
+ const user = userEvent.setup();
467
+ const onValueChange = vi.fn();
468
+ renderSelect({ onValueChange });
469
+
470
+ const trigger = screen.getByTestId('select-trigger');
471
+ await user.click(trigger);
472
+
473
+ const item = screen.getByText('Option 1');
474
+ await user.click(item);
475
+
476
+ expect(onValueChange).toHaveBeenCalledWith('option1');
477
+ });
478
+
479
+ it('selects different item on click', async () => {
480
+ const user = userEvent.setup();
481
+ const onValueChange = vi.fn();
482
+ renderSelect({ onValueChange });
483
+
484
+ const trigger = screen.getByTestId('select-trigger');
485
+ await user.click(trigger);
486
+
487
+ const item = screen.getByText('Option 2');
488
+ await user.click(item);
489
+
490
+ expect(onValueChange).toHaveBeenCalledWith('option2');
491
+ });
492
+ });
493
+
494
+ describe('Search Functionality', () => {
495
+ it('renders search input when searchable is true', async () => {
496
+ const user = userEvent.setup();
497
+ renderWithProviders(
498
+ <Select>
499
+ <SelectTrigger>
500
+ <SelectValue />
501
+ </SelectTrigger>
502
+ <SelectContent searchable>
503
+ <SelectItem value="test1">Test Option 1</SelectItem>
504
+ <SelectItem value="test2">Test Option 2</SelectItem>
505
+ </SelectContent>
506
+ </Select>
507
+ );
508
+
509
+ const trigger = screen.getByTestId('select-trigger');
510
+ await user.click(trigger);
511
+
512
+ expect(screen.getByTestId('select-search-input')).toBeInTheDocument();
513
+ expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
514
+ });
515
+
516
+ it('filters options based on search term', async () => {
517
+ const user = userEvent.setup();
518
+ renderWithProviders(
519
+ <Select>
520
+ <SelectTrigger>
521
+ <SelectValue />
522
+ </SelectTrigger>
523
+ <SelectContent searchable>
524
+ <SelectItem value="apple">Apple</SelectItem>
525
+ <SelectItem value="banana">Banana</SelectItem>
526
+ <SelectItem value="cherry">Cherry</SelectItem>
527
+ </SelectContent>
528
+ </Select>
529
+ );
530
+
531
+ const trigger = screen.getByTestId('select-trigger');
532
+ await user.click(trigger);
533
+
534
+ const searchInput = screen.getByTestId('select-search-input');
535
+ await user.type(searchInput, 'app');
536
+
537
+ expect(screen.getByText('Apple')).toBeInTheDocument();
538
+ expect(screen.queryByText('Banana')).not.toBeInTheDocument();
539
+ expect(screen.queryByText('Cherry')).not.toBeInTheDocument();
540
+ });
541
+
542
+ it('shows clear search button when search term exists', async () => {
543
+ const user = userEvent.setup();
544
+ renderWithProviders(
545
+ <Select>
546
+ <SelectTrigger>
547
+ <SelectValue />
548
+ </SelectTrigger>
549
+ <SelectContent searchable>
550
+ <SelectItem value="test">Test</SelectItem>
551
+ </SelectContent>
552
+ </Select>
553
+ );
554
+
555
+ const trigger = screen.getByTestId('select-trigger');
556
+ await user.click(trigger);
557
+
558
+ const searchInput = screen.getByTestId('select-search-input');
559
+ await user.type(searchInput, 'test');
560
+
561
+ expect(screen.getByTestId('select-clear-search')).toBeInTheDocument();
562
+ });
563
+
564
+ it('clears search when clear button is clicked', async () => {
565
+ const user = userEvent.setup();
566
+ renderWithProviders(
567
+ <Select>
568
+ <SelectTrigger>
569
+ <SelectValue />
570
+ </SelectTrigger>
571
+ <SelectContent searchable>
572
+ <SelectItem value="apple">Apple</SelectItem>
573
+ <SelectItem value="banana">Banana</SelectItem>
574
+ </SelectContent>
575
+ </Select>
576
+ );
577
+
578
+ const trigger = screen.getByTestId('select-trigger');
579
+ await user.click(trigger);
580
+
581
+ const searchInput = screen.getByTestId('select-search-input');
582
+ await user.type(searchInput, 'app');
583
+
584
+ expect(screen.queryByText('Banana')).not.toBeInTheDocument();
585
+
586
+ const clearButton = screen.getByTestId('select-clear-search');
587
+ await user.click(clearButton);
588
+
589
+ expect(screen.getByText('Apple')).toBeInTheDocument();
590
+ expect(screen.getByText('Banana')).toBeInTheDocument();
591
+ });
592
+
593
+ it('clears search on Escape key', async () => {
594
+ const user = userEvent.setup();
595
+ renderWithProviders(
596
+ <Select>
597
+ <SelectTrigger>
598
+ <SelectValue />
599
+ </SelectTrigger>
600
+ <SelectContent searchable>
601
+ <SelectItem value="apple">Apple</SelectItem>
602
+ <SelectItem value="banana">Banana</SelectItem>
603
+ </SelectContent>
604
+ </Select>
605
+ );
606
+
607
+ const trigger = screen.getByTestId('select-trigger');
608
+ await user.click(trigger);
609
+
610
+ const searchInput = screen.getByTestId('select-search-input');
611
+ await user.type(searchInput, 'app');
612
+ await user.keyboard('{Escape}');
613
+
614
+ expect(screen.getByText('Apple')).toBeInTheDocument();
615
+ expect(screen.getByText('Banana')).toBeInTheDocument();
616
+ });
617
+
618
+ it('focuses search input when dropdown opens', async () => {
619
+ const user = userEvent.setup();
620
+ renderWithProviders(
621
+ <Select>
622
+ <SelectTrigger>
623
+ <SelectValue />
624
+ </SelectTrigger>
625
+ <SelectContent searchable>
626
+ <SelectItem value="test">Test</SelectItem>
627
+ </SelectContent>
628
+ </Select>
629
+ );
630
+
631
+ const trigger = screen.getByTestId('select-trigger');
632
+ await user.click(trigger);
633
+
634
+ const searchInput = screen.getByTestId('select-search-input');
635
+ expect(searchInput).toHaveFocus();
636
+ });
637
+ });
638
+
639
+ describe('Controlled vs Uncontrolled', () => {
640
+ it('works in uncontrolled mode', async () => {
641
+ const user = userEvent.setup();
642
+ const onValueChange = vi.fn();
643
+ renderSelect({ onValueChange });
644
+
645
+ const trigger = screen.getByTestId('select-trigger');
646
+ await user.click(trigger);
647
+
648
+ const option = screen.getByText('Option 1');
649
+ await user.click(option);
650
+
651
+ expect(onValueChange).toHaveBeenCalledWith('option1');
652
+ expect(screen.getByTestId('select-root')).toHaveAttribute('data-value', 'option1');
653
+ });
654
+
655
+ it('works in controlled mode', async () => {
656
+ const user = userEvent.setup();
657
+ const onValueChange = vi.fn();
658
+ const onOpenChange = vi.fn();
659
+
660
+ renderSelect({
661
+ value: 'option2',
662
+ onValueChange,
663
+ open: true,
664
+ onOpenChange
665
+ });
666
+
667
+ expect(screen.getByTestId('select-root')).toHaveAttribute('data-value', 'option2');
668
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
669
+
670
+ const option = screen.getByText('Option 1');
671
+ await user.click(option);
672
+
673
+ expect(onValueChange).toHaveBeenCalledWith('option1');
674
+ expect(onOpenChange).toHaveBeenCalledWith(false);
675
+ });
676
+
677
+ it('handles controlled open state', async () => {
678
+ const user = userEvent.setup();
679
+ const onOpenChange = vi.fn();
680
+
681
+ renderSelect({ open: false, onOpenChange });
682
+
683
+ const trigger = screen.getByTestId('select-trigger');
684
+ await user.click(trigger);
685
+
686
+ expect(onOpenChange).toHaveBeenCalledWith(true);
687
+ });
688
+ });
689
+
690
+ describe('Additional Components', () => {
691
+ it('renders SelectGroup', async () => {
692
+ const user = userEvent.setup();
693
+ renderWithProviders(
694
+ <Select>
695
+ <SelectTrigger>
696
+ <SelectValue />
697
+ </SelectTrigger>
698
+ <SelectContent>
699
+ <SelectGroup>
700
+ <SelectItem value="group1">Group Item 1</SelectItem>
701
+ </SelectGroup>
702
+ </SelectContent>
703
+ </Select>
704
+ );
705
+
706
+ const trigger = screen.getByTestId('select-trigger');
707
+ await user.click(trigger);
708
+
709
+ expect(screen.getByTestId('select-group')).toBeInTheDocument();
710
+ expect(screen.getByText('Group Item 1')).toBeInTheDocument();
711
+ });
712
+
713
+ it('renders SelectLabel', async () => {
714
+ const user = userEvent.setup();
715
+ renderWithProviders(
716
+ <Select>
717
+ <SelectTrigger>
718
+ <SelectValue />
719
+ </SelectTrigger>
720
+ <SelectContent>
721
+ <SelectLabel>Group Label</SelectLabel>
722
+ <SelectItem value="test">Test</SelectItem>
723
+ </SelectContent>
724
+ </Select>
725
+ );
726
+
727
+ const trigger = screen.getByTestId('select-trigger');
728
+ await user.click(trigger);
729
+
730
+ expect(screen.getByTestId('select-label')).toBeInTheDocument();
731
+ expect(screen.getByText('Group Label')).toBeInTheDocument();
732
+ });
733
+
734
+ it('renders SelectSeparator', async () => {
735
+ const user = userEvent.setup();
736
+ renderWithProviders(
737
+ <Select>
738
+ <SelectTrigger>
739
+ <SelectValue />
740
+ </SelectTrigger>
741
+ <SelectContent>
742
+ <SelectItem value="item1">Item 1</SelectItem>
743
+ <SelectSeparator />
744
+ <SelectItem value="item2">Item 2</SelectItem>
745
+ </SelectContent>
746
+ </Select>
747
+ );
748
+
749
+ const trigger = screen.getByTestId('select-trigger');
750
+ await user.click(trigger);
751
+
752
+ expect(screen.getByTestId('select-separator')).toBeInTheDocument();
753
+ });
754
+ });
755
+
756
+ describe('Edge Cases', () => {
757
+ it('handles empty options list', () => {
758
+ renderSelect({}, []);
759
+
760
+ expect(screen.getByTestId('select-root')).toBeInTheDocument();
761
+ expect(screen.getByTestId('select-trigger')).toBeInTheDocument();
762
+ });
763
+
764
+ it('handles rapid open/close cycles', async () => {
765
+ const user = userEvent.setup();
766
+ renderSelect();
767
+
768
+ const trigger = screen.getByTestId('select-trigger');
769
+
770
+ // Rapid clicks
771
+ await user.click(trigger);
772
+ await user.click(trigger);
773
+ await user.click(trigger);
774
+
775
+ // Should be open after odd number of clicks
776
+ expect(screen.getByTestId('select-content')).toBeInTheDocument();
777
+ });
778
+
779
+ it('handles disabled trigger', async () => {
780
+ const user = userEvent.setup();
781
+ renderSelect({ disabled: true });
782
+
783
+ const trigger = screen.getByTestId('select-trigger');
784
+ expect(trigger).toBeDisabled();
785
+
786
+ await user.click(trigger);
787
+
788
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
789
+ });
790
+
791
+ it('handles keyboard navigation on disabled trigger', async () => {
792
+ const user = userEvent.setup();
793
+ renderSelect({ disabled: true });
794
+
795
+ const trigger = screen.getByTestId('select-trigger');
796
+ trigger.focus();
797
+
798
+ await user.keyboard('{Enter}');
799
+
800
+ expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
801
+ });
802
+
803
+ it('handles multiple select instances', async () => {
804
+ const user = userEvent.setup();
805
+ renderWithProviders(
806
+ <div>
807
+ <Select>
808
+ <SelectTrigger>
809
+ <SelectValue placeholder="Select 1" />
810
+ </SelectTrigger>
811
+ <SelectContent>
812
+ <SelectItem value="a">Option A</SelectItem>
813
+ </SelectContent>
814
+ </Select>
815
+ <Select>
816
+ <SelectTrigger>
817
+ <SelectValue placeholder="Select 2" />
818
+ </SelectTrigger>
819
+ <SelectContent>
820
+ <SelectItem value="b">Option B</SelectItem>
821
+ </SelectContent>
822
+ </Select>
823
+ </div>
824
+ );
825
+
826
+ const triggers = screen.getAllByTestId('select-trigger');
827
+
828
+ // Open first select
829
+ await user.click(triggers[0]);
830
+ expect(screen.getByText('Option A')).toBeInTheDocument();
831
+
832
+ // Open second select (should close first)
833
+ await user.click(triggers[1]);
834
+ expect(screen.queryByText('Option A')).not.toBeInTheDocument();
835
+ expect(screen.getByText('Option B')).toBeInTheDocument();
836
+ });
837
+ });
838
+
839
+ describe('Accessibility', () => {
840
+ it('has proper ARIA attributes', () => {
841
+ renderSelect();
842
+
843
+ const trigger = screen.getByTestId('select-trigger');
844
+ expect(trigger).toHaveAttribute('role', 'combobox');
845
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
846
+ expect(trigger).toHaveAttribute('aria-haspopup', 'listbox');
847
+ });
848
+
849
+ it('updates ARIA attributes when opened', async () => {
850
+ const user = userEvent.setup();
851
+ renderSelect();
852
+
853
+ const trigger = screen.getByTestId('select-trigger');
854
+ await user.click(trigger);
855
+
856
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
857
+ });
858
+
859
+ it('has proper listbox role on content', async () => {
860
+ const user = userEvent.setup();
861
+ renderSelect();
862
+
863
+ const trigger = screen.getByTestId('select-trigger');
864
+ await user.click(trigger);
865
+
866
+ const content = screen.getByTestId('select-content');
867
+ expect(content).toHaveAttribute('role', 'listbox');
868
+ });
869
+
870
+ it('has proper option roles on items', async () => {
871
+ const user = userEvent.setup();
872
+ renderSelect();
873
+
874
+ const trigger = screen.getByTestId('select-trigger');
875
+ await user.click(trigger);
876
+
877
+ const items = screen.getAllByRole('option');
878
+ expect(items).toHaveLength(3);
879
+
880
+ items.forEach(item => {
881
+ expect(item).toHaveAttribute('role', 'option');
882
+ });
883
+ });
884
+
885
+ it('has proper aria-selected on items', async () => {
886
+ const user = userEvent.setup();
887
+ renderSelect({ defaultValue: 'option2' });
888
+
889
+ const trigger = screen.getByTestId('select-trigger');
890
+ await user.click(trigger);
891
+
892
+ const items = screen.getAllByRole('option');
893
+ expect(items[0]).toHaveAttribute('aria-selected', 'false');
894
+ expect(items[1]).toHaveAttribute('aria-selected', 'true');
895
+ expect(items[2]).toHaveAttribute('aria-selected', 'false');
896
+ });
897
+
898
+ it('has proper search input accessibility', async () => {
899
+ const user = userEvent.setup();
900
+ renderWithProviders(
901
+ <Select>
902
+ <SelectTrigger>
903
+ <SelectValue />
904
+ </SelectTrigger>
905
+ <SelectContent searchable>
906
+ <SelectItem value="test">Test</SelectItem>
907
+ </SelectContent>
908
+ </Select>
909
+ );
910
+
911
+ const trigger = screen.getByTestId('select-trigger');
912
+ await user.click(trigger);
913
+
914
+ const searchInput = screen.getByTestId('select-search-input');
915
+ expect(searchInput).toHaveAttribute('aria-label', 'Search options');
916
+ });
917
+ });
918
+
919
+ describe('Performance', () => {
920
+ it('does not re-render unnecessarily with stable props', () => {
921
+ const stableProps = {
922
+ defaultValue: 'option1',
923
+ disabled: false,
924
+ };
925
+
926
+ const { rerender } = renderSelect(stableProps);
927
+ const initialRoot = screen.getByTestId('select-root');
928
+
929
+ // Re-render with same props
930
+ rerender(
931
+ <Select {...stableProps}>
932
+ <SelectTrigger>
933
+ <SelectValue placeholder="Select an option..." />
934
+ </SelectTrigger>
935
+ <SelectContent>
936
+ {defaultOptions.map(option => (
937
+ <SelectItem key={option.value} value={option.value}>
938
+ {option.label}
939
+ </SelectItem>
940
+ ))}
941
+ </SelectContent>
942
+ </Select>
943
+ );
944
+
945
+ expect(screen.getByTestId('select-root')).toBeInTheDocument();
946
+ });
947
+ });
948
+ });