@mui/internal-test-utils 2.0.15 → 2.0.16

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 (212) hide show
  1. package/{src/chai.types.ts → chai.types.d.ts} +7 -40
  2. package/chai.types.js +5 -0
  3. package/chaiPlugin.d.ts +4 -0
  4. package/chaiPlugin.js +287 -0
  5. package/{build/components.d.ts → components.d.ts} +18 -19
  6. package/components.js +64 -0
  7. package/createDOM.d.ts +2 -0
  8. package/{src/createDOM.js → createDOM.js} +17 -35
  9. package/createDescribe.d.ts +7 -0
  10. package/createDescribe.js +26 -0
  11. package/createRenderer.d.ts +214 -0
  12. package/createRenderer.js +428 -0
  13. package/createRenderer.test.d.ts +1 -0
  14. package/describeConformance.d.ts +200 -0
  15. package/describeConformance.js +1038 -0
  16. package/env.d.ts +1 -0
  17. package/env.js +11 -0
  18. package/esm/chai.types.d.ts +74 -0
  19. package/esm/chai.types.js +3 -0
  20. package/esm/chaiPlugin.d.ts +4 -0
  21. package/esm/chaiPlugin.js +281 -0
  22. package/esm/components.d.ts +35 -0
  23. package/esm/components.js +56 -0
  24. package/esm/createDOM.d.ts +2 -0
  25. package/esm/createDOM.js +47 -0
  26. package/esm/createDescribe.d.ts +7 -0
  27. package/esm/createDescribe.js +19 -0
  28. package/esm/createRenderer.d.ts +214 -0
  29. package/esm/createRenderer.js +390 -0
  30. package/esm/createRenderer.test.d.ts +1 -0
  31. package/esm/describeConformance.d.ts +200 -0
  32. package/esm/describeConformance.js +1024 -0
  33. package/esm/env.d.ts +1 -0
  34. package/esm/env.js +5 -0
  35. package/{build → esm}/fireDiscreteEvent.d.ts +1 -2
  36. package/{src/fireDiscreteEvent.ts → esm/fireDiscreteEvent.js} +10 -18
  37. package/esm/flushMicrotasks.d.ts +1 -0
  38. package/{src/flushMicrotasks.ts → esm/flushMicrotasks.js} +2 -3
  39. package/{build → esm}/focusVisible.d.ts +1 -2
  40. package/{src/focusVisible.ts → esm/focusVisible.js} +10 -9
  41. package/esm/index.d.ts +18 -0
  42. package/esm/index.js +27 -0
  43. package/esm/init.d.ts +1 -0
  44. package/{src → esm}/init.js +4 -2
  45. package/esm/initMatchers.d.ts +1 -0
  46. package/esm/initMatchers.js +6 -0
  47. package/esm/initMatchers.test.d.ts +1 -0
  48. package/esm/initPlaywrightMatchers.d.ts +24 -0
  49. package/esm/initPlaywrightMatchers.js +40 -0
  50. package/esm/package.json +1 -0
  51. package/esm/reactMajor.d.ts +2 -0
  52. package/esm/reactMajor.js +2 -0
  53. package/esm/setup.d.ts +1 -0
  54. package/{src → esm}/setup.js +2 -4
  55. package/esm/setupVitest.d.ts +1 -0
  56. package/esm/setupVitest.js +28 -0
  57. package/esm/setupVitestBrowser.d.ts +1 -0
  58. package/esm/setupVitestBrowser.js +30 -0
  59. package/fireDiscreteEvent.d.ts +6 -0
  60. package/fireDiscreteEvent.js +79 -0
  61. package/flushMicrotasks.d.ts +1 -0
  62. package/flushMicrotasks.js +10 -0
  63. package/focusVisible.d.ts +7 -0
  64. package/focusVisible.js +44 -0
  65. package/index.d.ts +18 -0
  66. package/index.js +139 -0
  67. package/init.d.ts +1 -0
  68. package/init.js +15 -0
  69. package/initMatchers.d.ts +1 -0
  70. package/initMatchers.js +10 -0
  71. package/initMatchers.test.d.ts +1 -0
  72. package/initPlaywrightMatchers.d.ts +24 -0
  73. package/initPlaywrightMatchers.js +42 -0
  74. package/package.json +89 -46
  75. package/reactMajor.d.ts +2 -0
  76. package/reactMajor.js +9 -0
  77. package/setup.d.ts +1 -0
  78. package/setup.js +10 -0
  79. package/setupVitest.d.ts +1 -0
  80. package/setupVitest.js +32 -0
  81. package/setupVitestBrowser.d.ts +1 -0
  82. package/setupVitestBrowser.js +34 -0
  83. package/build/.tsbuildinfo +0 -1
  84. package/build/KarmaReporterReactProfiler.d.ts +0 -51
  85. package/build/KarmaReporterReactProfiler.d.ts.map +0 -1
  86. package/build/KarmaReporterReactProfiler.js +0 -66
  87. package/build/KarmaReporterReactProfiler.js.map +0 -1
  88. package/build/chai.types.d.ts +0 -75
  89. package/build/chai.types.d.ts.map +0 -1
  90. package/build/chai.types.js +0 -3
  91. package/build/chai.types.js.map +0 -1
  92. package/build/chaiPlugin.d.ts +0 -5
  93. package/build/chaiPlugin.d.ts.map +0 -1
  94. package/build/chaiPlugin.js +0 -416
  95. package/build/chaiPlugin.js.map +0 -1
  96. package/build/components.d.ts.map +0 -1
  97. package/build/components.js +0 -88
  98. package/build/components.js.map +0 -1
  99. package/build/createDOM.d.ts +0 -3
  100. package/build/createDOM.d.ts.map +0 -1
  101. package/build/createDOM.js +0 -60
  102. package/build/createDOM.js.map +0 -1
  103. package/build/createDescribe.d.ts +0 -8
  104. package/build/createDescribe.d.ts.map +0 -1
  105. package/build/createDescribe.js +0 -22
  106. package/build/createDescribe.js.map +0 -1
  107. package/build/createRenderer.d.ts +0 -215
  108. package/build/createRenderer.d.ts.map +0 -1
  109. package/build/createRenderer.js +0 -564
  110. package/build/createRenderer.js.map +0 -1
  111. package/build/createRenderer.test.d.ts +0 -2
  112. package/build/createRenderer.test.d.ts.map +0 -1
  113. package/build/createRenderer.test.js +0 -58
  114. package/build/createRenderer.test.js.map +0 -1
  115. package/build/describeConformance.d.ts +0 -201
  116. package/build/describeConformance.d.ts.map +0 -1
  117. package/build/describeConformance.js +0 -859
  118. package/build/describeConformance.js.map +0 -1
  119. package/build/describeSkipIf.d.ts +0 -4
  120. package/build/describeSkipIf.d.ts.map +0 -1
  121. package/build/describeSkipIf.js +0 -10
  122. package/build/describeSkipIf.js.map +0 -1
  123. package/build/fireDiscreteEvent.d.ts.map +0 -1
  124. package/build/fireDiscreteEvent.js +0 -77
  125. package/build/fireDiscreteEvent.js.map +0 -1
  126. package/build/flushMicrotasks.d.ts +0 -2
  127. package/build/flushMicrotasks.d.ts.map +0 -1
  128. package/build/flushMicrotasks.js +0 -8
  129. package/build/flushMicrotasks.js.map +0 -1
  130. package/build/focusVisible.d.ts.map +0 -1
  131. package/build/focusVisible.js +0 -38
  132. package/build/focusVisible.js.map +0 -1
  133. package/build/index.d.ts +0 -18
  134. package/build/index.d.ts.map +0 -1
  135. package/build/index.js +0 -68
  136. package/build/index.js.map +0 -1
  137. package/build/init.d.ts +0 -2
  138. package/build/init.d.ts.map +0 -1
  139. package/build/init.js +0 -46
  140. package/build/init.js.map +0 -1
  141. package/build/initMatchers.d.ts +0 -2
  142. package/build/initMatchers.d.ts.map +0 -1
  143. package/build/initMatchers.js +0 -45
  144. package/build/initMatchers.js.map +0 -1
  145. package/build/initMatchers.test.d.ts +0 -2
  146. package/build/initMatchers.test.d.ts.map +0 -1
  147. package/build/initMatchers.test.js +0 -101
  148. package/build/initMatchers.test.js.map +0 -1
  149. package/build/initPlaywrightMatchers.d.ts +0 -25
  150. package/build/initPlaywrightMatchers.d.ts.map +0 -1
  151. package/build/initPlaywrightMatchers.js +0 -73
  152. package/build/initPlaywrightMatchers.js.map +0 -1
  153. package/build/mochaHooks.d.ts +0 -24
  154. package/build/mochaHooks.d.ts.map +0 -1
  155. package/build/mochaHooks.js +0 -165
  156. package/build/mochaHooks.js.map +0 -1
  157. package/build/mochaHooks.test.d.ts +0 -2
  158. package/build/mochaHooks.test.d.ts.map +0 -1
  159. package/build/mochaHooks.test.js +0 -128
  160. package/build/mochaHooks.test.js.map +0 -1
  161. package/build/reactMajor.d.ts +0 -3
  162. package/build/reactMajor.d.ts.map +0 -1
  163. package/build/reactMajor.js +0 -38
  164. package/build/reactMajor.js.map +0 -1
  165. package/build/setup.d.ts +0 -2
  166. package/build/setup.d.ts.map +0 -1
  167. package/build/setup.js +0 -10
  168. package/build/setup.js.map +0 -1
  169. package/build/setupBabel.d.ts +0 -2
  170. package/build/setupBabel.d.ts.map +0 -1
  171. package/build/setupBabel.js +0 -5
  172. package/build/setupBabel.js.map +0 -1
  173. package/build/setupBabelPlaywright.d.ts +0 -2
  174. package/build/setupBabelPlaywright.d.ts.map +0 -1
  175. package/build/setupBabelPlaywright.js +0 -14
  176. package/build/setupBabelPlaywright.js.map +0 -1
  177. package/build/setupJSDOM.d.ts +0 -7
  178. package/build/setupJSDOM.d.ts.map +0 -1
  179. package/build/setupJSDOM.js +0 -17
  180. package/build/setupJSDOM.js.map +0 -1
  181. package/build/setupKarma.d.ts +0 -2
  182. package/build/setupKarma.d.ts.map +0 -1
  183. package/build/setupKarma.js +0 -56
  184. package/build/setupKarma.js.map +0 -1
  185. package/build/setupVitest.d.ts +0 -2
  186. package/build/setupVitest.d.ts.map +0 -1
  187. package/build/setupVitest.js +0 -131
  188. package/build/setupVitest.js.map +0 -1
  189. package/src/KarmaReporterReactProfiler.js +0 -82
  190. package/src/chai-augmentation.d.ts +0 -8
  191. package/src/chaiPlugin.ts +0 -515
  192. package/src/components.tsx +0 -61
  193. package/src/createDOM.d.ts +0 -9
  194. package/src/createDescribe.ts +0 -31
  195. package/src/createRenderer.test.js +0 -31
  196. package/src/createRenderer.tsx +0 -808
  197. package/src/describeConformance.tsx +0 -1257
  198. package/src/describeSkipIf.tsx +0 -11
  199. package/src/index.ts +0 -25
  200. package/src/initMatchers.test.js +0 -124
  201. package/src/initMatchers.ts +0 -7
  202. package/src/initPlaywrightMatchers.ts +0 -101
  203. package/src/mochaHooks.js +0 -200
  204. package/src/mochaHooks.test.js +0 -116
  205. package/src/reactMajor.ts +0 -3
  206. package/src/setupBabel.js +0 -3
  207. package/src/setupBabelPlaywright.js +0 -13
  208. package/src/setupJSDOM.js +0 -20
  209. package/src/setupKarma.js +0 -65
  210. package/src/setupVitest.ts +0 -117
  211. package/tsconfig.build.json +0 -16
  212. package/tsconfig.json +0 -17
@@ -1,1257 +0,0 @@
1
- /* eslint-env mocha */
2
- import { expect } from 'chai';
3
- import * as React from 'react';
4
- import createDescribe from './createDescribe';
5
- import { MuiRenderResult } from './createRenderer';
6
-
7
- function capitalize(string: string): string {
8
- return string.charAt(0).toUpperCase() + string.slice(1);
9
- }
10
-
11
- interface DataProps {
12
- [key: `data-${string}`]: string;
13
- }
14
-
15
- export interface SlotTestingOptions {
16
- /**
17
- * A custom React component to test if the receiving props are correct.
18
- *
19
- * It must:
20
- * - contains at least one DOM which has `data-testid="custom"`
21
- * - spread `className` to the DOM
22
- *
23
- * If not provided, the default custom component tests if the class name is spread.
24
- */
25
- testWithComponent?: React.ComponentType;
26
- /**
27
- * A custom HTML tag to use for the `slots` prop.
28
- */
29
- testWithElement?: keyof React.JSX.IntrinsicElements | null;
30
- /**
31
- * To ensure that the slot has this class name when `slotProps` is provided.
32
- */
33
- expectedClassName: string;
34
- isOptional?: boolean;
35
- }
36
-
37
- interface SlotTestOverride {
38
- slotName: string;
39
- slotClassName?: string;
40
- }
41
-
42
- export interface ConformanceOptions {
43
- muiName: string;
44
- classes: { root: string };
45
- refInstanceof: any;
46
- after?: () => void;
47
- inheritComponent?: React.ElementType;
48
- render: (node: React.ReactElement<DataProps>) => MuiRenderResult | Promise<MuiRenderResult>;
49
- only?: Array<keyof typeof fullSuite>;
50
- skip?: Array<keyof typeof fullSuite | 'classesRoot'>;
51
- testComponentsRootPropWith?: string;
52
- /**
53
- * A custom React component to test if the component prop is implemented correctly.
54
- *
55
- * It must either:
56
- * - Be a string that is a valid HTML tag, or
57
- * - A component that spread props to the underlying rendered element.
58
- *
59
- * If not provided, the default 'em' element is used.
60
- */
61
- testComponentPropWith?: string | React.ElementType;
62
- testDeepOverrides?: SlotTestOverride | SlotTestOverride[];
63
- testRootOverrides?: SlotTestOverride;
64
- testStateOverrides?: { prop?: string; value?: any; styleKey: string };
65
- testCustomVariant?: boolean;
66
- testVariantProps?: object;
67
- testLegacyComponentsProp?: boolean | string[];
68
- slots?: Record<string, SlotTestingOptions>;
69
- ThemeProvider?: React.ElementType;
70
- /**
71
- * If provided, the component will be tested by the `DefaultPropsProvider` (in addition to the ThemeProvider).
72
- */
73
- DefaultPropsProvider?: React.ElementType;
74
- createTheme?: (arg: any) => any;
75
- }
76
-
77
- /**
78
- * Glossary
79
- * - root component:
80
- * - renders the outermost host component
81
- * - has the `root` class if the component has one
82
- * - excess props are spread to this component
83
- * - has the type of `inheritComponent`
84
- */
85
-
86
- export function randomStringValue() {
87
- return `s${Math.random().toString(36).slice(2)}`;
88
- }
89
-
90
- function throwMissingPropError(field: string): never {
91
- throw new Error(`missing "${field}" in options
92
-
93
- > describeConformance(element, () => options)
94
- `);
95
- }
96
-
97
- /**
98
- * MUI components have a `className` prop. The `className` is applied to
99
- * the root component.
100
- */
101
- export function testClassName(
102
- element: React.ReactElement<
103
- DataProps & {
104
- className: string;
105
- }
106
- >,
107
- getOptions: () => ConformanceOptions,
108
- ) {
109
- it('applies the className to the root component', async () => {
110
- const { render } = getOptions();
111
-
112
- if (!render) {
113
- throwMissingPropError('render');
114
- }
115
-
116
- const className = randomStringValue();
117
- const testId = randomStringValue();
118
-
119
- const { getByTestId } = await render(
120
- React.cloneElement(element, { className, 'data-testid': testId }),
121
- );
122
-
123
- expect(getByTestId(testId)).to.have.class(className);
124
- });
125
- }
126
-
127
- /**
128
- * MUI components have a `component` prop that allows rendering a different
129
- * Component from @inheritComponent
130
- */
131
- export function testComponentProp(
132
- element: React.ReactElement<
133
- DataProps & {
134
- className: string;
135
- component?: string | React.ElementType;
136
- }
137
- >,
138
- getOptions: () => ConformanceOptions,
139
- ) {
140
- describe('prop: component', () => {
141
- it('can render another root component with the `component` prop', async () => {
142
- const { render, testComponentPropWith: component = 'em' } = getOptions();
143
- if (!render) {
144
- throwMissingPropError('render');
145
- }
146
-
147
- const testId = randomStringValue();
148
-
149
- if (typeof component === 'string') {
150
- const { getByTestId } = await render(
151
- React.cloneElement(element, { component, 'data-testid': testId }),
152
- );
153
- expect(getByTestId(testId)).not.to.equal(null);
154
- expect(getByTestId(testId).nodeName.toLowerCase()).to.eq(component);
155
- } else {
156
- const componentWithTestId = (props: {}) =>
157
- React.createElement(component, { ...props, 'data-testid': testId });
158
- const { getByTestId } = await render(
159
- React.cloneElement(element, {
160
- component: componentWithTestId,
161
- }),
162
- );
163
- expect(getByTestId(testId)).not.to.equal(null);
164
- }
165
- });
166
- });
167
- }
168
-
169
- /**
170
- * MUI components spread additional props to its root.
171
- */
172
- export function testPropsSpread(
173
- element: React.ReactElement<
174
- DataProps & {
175
- className: string;
176
- component: string | React.ElementType;
177
- }
178
- >,
179
-
180
- getOptions: () => ConformanceOptions,
181
- ) {
182
- it(`spreads props to the root component`, async () => {
183
- // type def in ConformanceOptions
184
- const { render } = getOptions();
185
-
186
- if (!render) {
187
- throwMissingPropError('render');
188
- }
189
-
190
- const testProp = 'data-test-props-spread';
191
- const value = randomStringValue();
192
- const testId = randomStringValue();
193
-
194
- const { getByTestId } = await render(
195
- React.cloneElement(element, { [testProp]: value, 'data-testid': testId }),
196
- );
197
-
198
- expect(getByTestId(testId)).to.have.attribute(testProp, value);
199
- });
200
- }
201
-
202
- /**
203
- * Tests that the `ref` of a component will return the correct instance
204
- *
205
- * This is determined by a given constructor i.e. a React.Component or HTMLElement for
206
- * components that forward their ref and attach it to a host component.
207
- */
208
- export function describeRef(
209
- element: React.ReactElement<{
210
- ref: React.RefObject<any>;
211
- }>,
212
- getOptions: () => ConformanceOptions,
213
- ) {
214
- describe('ref', () => {
215
- it(`attaches the ref`, async () => {
216
- // type def in ConformanceOptions
217
- const { render, refInstanceof } = getOptions();
218
-
219
- if (!render) {
220
- throwMissingPropError('render');
221
- }
222
-
223
- const ref = React.createRef();
224
-
225
- await render(React.cloneElement(element, { ref }));
226
-
227
- expect(ref.current).to.be.instanceof(refInstanceof);
228
- });
229
- });
230
- }
231
-
232
- /**
233
- * Tests that the root component has the root class
234
- */
235
- export function testRootClass(
236
- element: React.ReactElement<{
237
- className: string;
238
- classes: Record<string, string>;
239
- }>,
240
- getOptions: () => ConformanceOptions,
241
- ) {
242
- it('applies the root class to the root component if it has this class', async () => {
243
- const { classes, render, skip } = getOptions();
244
- if (classes.root == null) {
245
- return;
246
- }
247
-
248
- const className = randomStringValue();
249
- const classesRootClassname = randomStringValue();
250
- const { container } = await render(
251
- React.cloneElement(element, {
252
- className,
253
- classes: { ...classes, root: `${classes.root} ${classesRootClassname}` },
254
- }),
255
- );
256
-
257
- // we established that the root component renders the outermost host previously. We immediately
258
- // jump to the host component because some components pass the `root` class
259
- // to the `classes` prop of the root component.
260
- // https://github.com/mui/material-ui/blob/f9896bcd129a1209153106296b3d2487547ba205/packages/material-ui/src/OutlinedInput/OutlinedInput.js#L101
261
- expect(container.firstChild).to.have.class(className);
262
- expect(container.firstChild).to.have.class(classes.root);
263
- expect(document.querySelectorAll(`.${classes.root}`).length).to.equal(1);
264
-
265
- // classes test only for @mui/material
266
- if (!skip || !skip.includes('classesRoot')) {
267
- // Test that classes prop works
268
- expect(container.firstChild).to.have.class(classesRootClassname);
269
-
270
- // Test that `classes` does not spread to DOM
271
- expect(document.querySelectorAll('[classes]').length).to.equal(0);
272
- }
273
- });
274
- }
275
-
276
- function forEachSlot(
277
- slots: ConformanceOptions['slots'],
278
- callback: (slotName: string, slot: SlotTestingOptions) => void,
279
- ) {
280
- if (!slots) {
281
- return;
282
- }
283
-
284
- const slotNames = Object.keys(slots);
285
- slotNames.forEach((slotName) => {
286
- const slot = slots[slotName];
287
- callback(slotName, slot);
288
- });
289
- }
290
-
291
- function testSlotsProp(
292
- element: React.ReactElement<{
293
- className: string;
294
- classes: Record<string, string>;
295
- slots: {
296
- [x: string]:
297
- | SlotTestingOptions['testWithComponent']
298
- | keyof React.JSX.IntrinsicElements
299
- | React.ForwardRefExoticComponent<
300
- {
301
- children: React.ReactNode;
302
- } & React.RefAttributes<HTMLDivElement>
303
- >;
304
- };
305
- components?: {
306
- [x: string]:
307
- | SlotTestingOptions['testWithComponent']
308
- | keyof React.JSX.IntrinsicElements
309
- | React.ForwardRefExoticComponent<
310
- {
311
- children: React.ReactNode;
312
- } & React.RefAttributes<HTMLDivElement>
313
- >;
314
- };
315
- slotProps: {
316
- [x: string]: DataProps;
317
- };
318
- componentsProps?: {
319
- [x: string]: DataProps;
320
- };
321
- }>,
322
- getOptions: () => ConformanceOptions,
323
- ) {
324
- const { render, slots, testLegacyComponentsProp } = getOptions();
325
-
326
- const CustomComponent = React.forwardRef<
327
- HTMLElement,
328
- React.PropsWithChildren<{ className?: string }>
329
- >(({ className, children }, ref) => (
330
- <i className={className} ref={ref} data-testid="custom">
331
- {children}
332
- </i>
333
- ));
334
-
335
- forEachSlot(slots, (slotName, slotOptions) => {
336
- it(`allows overriding the ${slotName} slot with a component using the slots.${slotName} prop`, async () => {
337
- if (!render) {
338
- throwMissingPropError('render');
339
- }
340
-
341
- const slotComponent = slotOptions.testWithComponent ?? CustomComponent;
342
-
343
- const components = {
344
- [slotName]: slotComponent,
345
- };
346
-
347
- const { queryByTestId } = await render(React.cloneElement(element, { slots: components }));
348
- const renderedElement = queryByTestId('custom');
349
- expect(renderedElement).not.to.equal(null);
350
- if (slotOptions.expectedClassName) {
351
- expect(renderedElement).to.have.class(slotOptions.expectedClassName);
352
- }
353
- });
354
-
355
- if (slotOptions.testWithElement !== null) {
356
- it(`allows overriding the ${slotName} slot with an element using the slots.${slotName} prop`, async () => {
357
- if (!render) {
358
- throwMissingPropError('render');
359
- }
360
-
361
- const slotElement = slotOptions.testWithElement ?? 'i';
362
-
363
- const components = {
364
- [slotName]: slotElement,
365
- };
366
-
367
- const slotProps = {
368
- [slotName]: {
369
- 'data-testid': 'customized',
370
- },
371
- };
372
-
373
- const { queryByTestId } = await render(
374
- React.cloneElement(element, { slots: components, slotProps }),
375
- );
376
-
377
- const renderedElement = queryByTestId('customized');
378
- expect(renderedElement).not.to.equal(null);
379
-
380
- if (typeof slotElement === 'string') {
381
- expect(renderedElement!.nodeName.toLowerCase()).to.equal(slotElement);
382
- }
383
- if (slotOptions.expectedClassName) {
384
- expect(renderedElement).to.have.class(slotOptions.expectedClassName);
385
- }
386
- });
387
- }
388
-
389
- // For testing Material UI components v5, and v6. Likely to be removed in a future major release.
390
- if (
391
- testLegacyComponentsProp === true ||
392
- (Array.isArray(testLegacyComponentsProp) && testLegacyComponentsProp.includes(slotName))
393
- ) {
394
- it(`allows overriding the ${slotName} slot with a component using the components.${capitalize(
395
- slotName,
396
- )} prop`, async () => {
397
- if (!render) {
398
- throwMissingPropError('render');
399
- }
400
-
401
- const slotComponent = slotOptions.testWithComponent ?? CustomComponent;
402
-
403
- const components = {
404
- [capitalize(slotName)]: slotComponent,
405
- };
406
-
407
- const { queryByTestId } = await render(React.cloneElement(element, { components }));
408
- const renderedElement = queryByTestId('custom');
409
- expect(renderedElement).not.to.equal(null);
410
- if (slotOptions.expectedClassName) {
411
- expect(renderedElement).to.have.class(slotOptions.expectedClassName);
412
- }
413
- });
414
-
415
- it(`prioritizes the 'slots.${slotName}' over components.${capitalize(
416
- slotName,
417
- )} if both are defined`, async () => {
418
- if (!render) {
419
- throwMissingPropError('render');
420
- }
421
-
422
- const ComponentForComponentsProp = React.forwardRef<
423
- HTMLDivElement,
424
- { children: React.ReactNode }
425
- >(({ children }, ref) => {
426
- const SlotComponent = slotOptions.testWithComponent ?? 'div';
427
- return (
428
- <SlotComponent ref={ref} data-testid="from-components">
429
- {children}
430
- </SlotComponent>
431
- );
432
- });
433
-
434
- const ComponentForSlotsProp = React.forwardRef<
435
- HTMLDivElement,
436
- { children: React.ReactNode }
437
- >(({ children }, ref) => {
438
- const SlotComponent = slotOptions.testWithComponent ?? 'div';
439
- return (
440
- <SlotComponent ref={ref} data-testid="from-slots">
441
- {children}
442
- </SlotComponent>
443
- );
444
- });
445
-
446
- const components = {
447
- [capitalize(slotName)]: ComponentForComponentsProp,
448
- };
449
-
450
- const slotOverrides = {
451
- [slotName]: ComponentForSlotsProp,
452
- };
453
-
454
- const { queryByTestId } = await render(
455
- React.cloneElement(element, { components, slots: slotOverrides }),
456
- );
457
-
458
- expect(queryByTestId('from-slots')).not.to.equal(null);
459
- expect(queryByTestId('from-components')).to.equal(null);
460
- });
461
-
462
- if (slotOptions.testWithElement !== null) {
463
- it(`allows overriding the ${slotName} slot with an element using the components.${capitalize(
464
- slotName,
465
- )} prop`, async () => {
466
- if (!render) {
467
- throwMissingPropError('render');
468
- }
469
-
470
- const slotElement = slotOptions.testWithElement ?? 'i';
471
-
472
- const components = {
473
- [capitalize(slotName)]: slotElement,
474
- };
475
-
476
- const componentsProps = {
477
- [slotName]: {
478
- 'data-testid': 'customized',
479
- },
480
- };
481
-
482
- const { queryByTestId } = await render(
483
- React.cloneElement(element, { components, componentsProps }),
484
- );
485
-
486
- const renderedElement = queryByTestId('customized');
487
- expect(renderedElement).not.to.equal(null);
488
-
489
- if (typeof slotElement === 'string') {
490
- expect(renderedElement!.nodeName.toLowerCase()).to.equal(slotElement);
491
- }
492
- if (slotOptions.expectedClassName) {
493
- expect(renderedElement).to.have.class(slotOptions.expectedClassName);
494
- }
495
- });
496
- }
497
- }
498
- });
499
- }
500
-
501
- function testSlotPropsProp(
502
- element: React.ReactElement<{
503
- componentsProps?: Record<string, DataProps>;
504
- slotProps: Record<string, DataProps>;
505
- }>,
506
- getOptions: () => ConformanceOptions,
507
- ) {
508
- const { render, slots, testLegacyComponentsProp } = getOptions();
509
-
510
- if (!render) {
511
- throwMissingPropError('render');
512
- }
513
-
514
- forEachSlot(slots, (slotName, slotOptions) => {
515
- it(`sets custom properties on the ${slotName} slot's element with the slotProps.${slotName} prop`, async () => {
516
- const slotProps = {
517
- [slotName]: {
518
- 'data-testid': 'custom',
519
- },
520
- };
521
-
522
- const { queryByTestId } = await render(React.cloneElement(element, { slotProps }));
523
- const slotComponent = queryByTestId('custom');
524
- expect(slotComponent).not.to.equal(null);
525
-
526
- if (slotOptions.expectedClassName) {
527
- expect(slotComponent).to.have.class(slotOptions.expectedClassName);
528
- }
529
- });
530
-
531
- if (slotOptions.expectedClassName) {
532
- it(`merges the class names provided in slotsProps.${slotName} with the built-in ones`, async () => {
533
- const slotProps = {
534
- [slotName]: {
535
- 'data-testid': 'custom',
536
- className: randomStringValue(),
537
- },
538
- };
539
-
540
- const { getByTestId } = await render(React.cloneElement(element, { slotProps }));
541
-
542
- expect(getByTestId('custom')).to.have.class(slotOptions.expectedClassName);
543
- expect(getByTestId('custom')).to.have.class(slotProps[slotName].className);
544
- });
545
- }
546
-
547
- if (
548
- testLegacyComponentsProp === true ||
549
- (Array.isArray(testLegacyComponentsProp) && testLegacyComponentsProp.includes(slotName))
550
- ) {
551
- it(`sets custom properties on the ${slotName} slot's element with the componentsProps.${slotName} prop`, async () => {
552
- const componentsProps = {
553
- [slotName]: {
554
- 'data-testid': 'custom',
555
- },
556
- };
557
-
558
- const { queryByTestId } = await render(React.cloneElement(element, { componentsProps }));
559
- const slotComponent = queryByTestId('custom');
560
- expect(slotComponent).not.to.equal(null);
561
-
562
- if (slotOptions.expectedClassName) {
563
- expect(slotComponent).to.have.class(slotOptions.expectedClassName);
564
- }
565
- });
566
-
567
- it(`prioritizes the 'slotProps.${slotName}' over componentsProps.${slotName} if both are defined`, async () => {
568
- const componentsProps = {
569
- [slotName]: {
570
- 'data-testid': 'custom',
571
- 'data-from-components-props': 'true',
572
- },
573
- };
574
-
575
- const slotProps = {
576
- [slotName]: {
577
- 'data-testid': 'custom',
578
- 'data-from-slot-props': 'true',
579
- },
580
- };
581
-
582
- const { queryByTestId } = await render(
583
- React.cloneElement(element, { componentsProps, slotProps }),
584
- );
585
- const slotComponent = queryByTestId('custom');
586
- expect(slotComponent).to.have.attribute('data-from-slot-props', 'true');
587
- expect(slotComponent).not.to.have.attribute('data-from-components-props');
588
- });
589
- }
590
- });
591
- }
592
-
593
- function testSlotPropsCallback(
594
- element: React.ReactElement<{
595
- slotProps: Record<string, () => DataProps>;
596
- className: string;
597
- }>,
598
- getOptions: () => ConformanceOptions,
599
- ) {
600
- const { render, slots } = getOptions();
601
-
602
- if (!render) {
603
- throwMissingPropError('render');
604
- }
605
-
606
- forEachSlot(slots, (slotName) => {
607
- it(`sets custom properties on the ${slotName} slot's element with the slotProps.${slotName} callback`, async () => {
608
- const slotProps = {
609
- [slotName]: () => ({
610
- 'data-testid': 'custom',
611
- }),
612
- };
613
-
614
- const { queryByTestId } = await render(
615
- React.cloneElement(element, { slotProps, className: 'custom' }),
616
- );
617
- const slotComponent = queryByTestId('custom');
618
- expect(slotComponent).not.to.equal(null);
619
- });
620
- });
621
- }
622
-
623
- function testSlotPropsCallbackWithPropsAsOwnerState(
624
- element: React.ReactElement<{
625
- slotProps: Record<string, (ownerState: Record<string, any>) => DataProps>;
626
- className: string;
627
- }>,
628
- getOptions: () => ConformanceOptions,
629
- ) {
630
- const { render, slots } = getOptions();
631
-
632
- if (!render) {
633
- throwMissingPropError('render');
634
- }
635
-
636
- forEachSlot(slots, (slotName) => {
637
- it(`sets custom properties on the ${slotName} slot's element with the slotProps.${slotName} callback using the ownerState`, async () => {
638
- const slotProps = {
639
- [slotName]: (ownerState: Record<string, any>) => ({
640
- 'data-testid': ownerState.className,
641
- }),
642
- };
643
-
644
- const { queryByTestId } = await render(
645
- React.cloneElement(element, { slotProps, className: 'custom' }),
646
- );
647
- const slotComponent = queryByTestId('custom', { exact: false });
648
- expect(slotComponent).not.to.equal(null);
649
- });
650
- });
651
- }
652
-
653
- /**
654
- * MUI components have a `components` prop that allows rendering a different
655
- * Components from @inheritComponent
656
- */
657
- function testComponentsProp(
658
- element: React.ReactElement<
659
- {
660
- components?: Record<string, string>;
661
- } & DataProps
662
- >,
663
- getOptions: () => ConformanceOptions,
664
- ) {
665
- describe('prop components:', () => {
666
- it('can render another root component with the `components` prop', async () => {
667
- const { render, testComponentsRootPropWith: component = 'em' } = getOptions();
668
- if (!render) {
669
- throwMissingPropError('render');
670
- }
671
-
672
- const testId = randomStringValue();
673
-
674
- const { getByTestId } = await render(
675
- React.cloneElement(element, { components: { Root: component }, 'data-testid': testId }),
676
- );
677
- expect(getByTestId(testId)).not.to.equal(null);
678
- expect(getByTestId(testId).nodeName.toLowerCase()).to.eq(component);
679
- });
680
- });
681
- }
682
-
683
- /**
684
- * MUI theme has a components section that allows specifying default props.
685
- * Components from @inheritComponent
686
- */
687
- function testThemeDefaultProps(
688
- element: React.ReactElement<unknown>,
689
- getOptions: () => ConformanceOptions,
690
- ) {
691
- describe('theme default components:', () => {
692
- it("respect theme's defaultProps", async () => {
693
- const testProp = 'data-id';
694
- const { muiName, render, ThemeProvider, createTheme } = getOptions();
695
-
696
- if (!muiName) {
697
- throwMissingPropError('muiName');
698
- }
699
-
700
- if (!render) {
701
- throwMissingPropError('render');
702
- }
703
-
704
- if (!ThemeProvider) {
705
- throwMissingPropError('ThemeProvider');
706
- }
707
-
708
- if (!createTheme) {
709
- throwMissingPropError('createTheme');
710
- }
711
-
712
- const theme = createTheme({
713
- components: {
714
- [muiName]: {
715
- defaultProps: {
716
- [testProp]: 'testProp',
717
- },
718
- },
719
- },
720
- });
721
-
722
- const { container } = await render(<ThemeProvider theme={theme}>{element}</ThemeProvider>);
723
-
724
- expect(container.firstChild).to.have.attribute(testProp, 'testProp');
725
- });
726
- });
727
-
728
- describe('default props provider:', () => {
729
- it('respect custom default props', async function test(t = {}) {
730
- const testProp = 'data-id';
731
- const { muiName, render, DefaultPropsProvider } = getOptions();
732
-
733
- if (!DefaultPropsProvider) {
734
- // @ts-ignore
735
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
736
- this?.skip?.() ?? t?.skip();
737
- }
738
-
739
- if (!muiName) {
740
- throwMissingPropError('muiName');
741
- }
742
-
743
- if (!render) {
744
- throwMissingPropError('render');
745
- }
746
-
747
- const { container } = await render(
748
- // @ts-expect-error we skip it above.
749
- <DefaultPropsProvider
750
- value={{
751
- [muiName]: {
752
- defaultProps: {
753
- [testProp]: 'testProp',
754
- },
755
- },
756
- }}
757
- >
758
- {element}
759
- </DefaultPropsProvider>,
760
- );
761
-
762
- expect(container.firstChild).to.have.attribute(testProp, 'testProp');
763
- });
764
- });
765
- }
766
-
767
- /**
768
- * MUI theme has a components section that allows specifying style overrides.
769
- * Components from @inheritComponent
770
- */
771
- function testThemeStyleOverrides(
772
- element: React.ReactElement<unknown>,
773
- getOptions: () => ConformanceOptions,
774
- ) {
775
- describe('theme style overrides:', () => {
776
- it("respect theme's styleOverrides custom state", async function test(t = {}) {
777
- if (window.navigator.userAgent.includes('jsdom')) {
778
- // @ts-ignore
779
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
780
- this?.skip?.() ?? t?.skip();
781
- }
782
- const { muiName, testStateOverrides, render, ThemeProvider, createTheme } = getOptions();
783
-
784
- if (!testStateOverrides) {
785
- return;
786
- }
787
-
788
- if (!muiName) {
789
- throwMissingPropError('muiName');
790
- }
791
-
792
- if (!render) {
793
- throwMissingPropError('render');
794
- }
795
-
796
- if (!ThemeProvider) {
797
- throwMissingPropError('ThemeProvider');
798
- }
799
-
800
- if (!createTheme) {
801
- throwMissingPropError('createTheme');
802
- }
803
-
804
- const testStyle = {
805
- marginTop: '13px',
806
- };
807
-
808
- const theme = createTheme({
809
- components: {
810
- [muiName]: {
811
- styleOverrides: {
812
- [testStateOverrides.styleKey]: testStyle,
813
- },
814
- },
815
- },
816
- });
817
-
818
- if (!testStateOverrides.prop) {
819
- return;
820
- }
821
-
822
- const { container } = await render(
823
- <ThemeProvider theme={theme}>
824
- {React.cloneElement(element, {
825
- [testStateOverrides.prop]: testStateOverrides.value,
826
- })}
827
- </ThemeProvider>,
828
- );
829
-
830
- expect(container.firstChild).to.toHaveComputedStyle(testStyle);
831
- });
832
-
833
- it("respect theme's styleOverrides slots", async function test(t = {}) {
834
- if (window.navigator.userAgent.includes('jsdom')) {
835
- // @ts-ignore
836
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
837
- this?.skip?.() ?? t?.skip();
838
- }
839
-
840
- const {
841
- muiName,
842
- testDeepOverrides,
843
- testRootOverrides = { slotName: 'root' },
844
- render,
845
- ThemeProvider,
846
- createTheme,
847
- } = getOptions();
848
-
849
- if (!ThemeProvider) {
850
- throwMissingPropError('ThemeProvider');
851
- }
852
-
853
- if (!createTheme) {
854
- throwMissingPropError('createTheme');
855
- }
856
-
857
- const testStyle = {
858
- mixBlendMode: 'darken',
859
- };
860
-
861
- function resolveDeepOverrides(
862
- callback: (styles: Record<string, any>, slot: SlotTestOverride) => void,
863
- ) {
864
- if (!testDeepOverrides) {
865
- return {};
866
- }
867
- const styles = {};
868
- if (Array.isArray(testDeepOverrides)) {
869
- testDeepOverrides.forEach((slot) => {
870
- callback(styles, slot);
871
- });
872
- } else {
873
- callback(styles, testDeepOverrides);
874
- }
875
- return styles;
876
- }
877
-
878
- const theme = createTheme({
879
- components: {
880
- [muiName]: {
881
- styleOverrides: {
882
- [testRootOverrides.slotName]: {
883
- ...testStyle,
884
- ...resolveDeepOverrides((styles, slot) => {
885
- styles[`& .${slot.slotClassName}`] = {
886
- fontVariantCaps: 'all-petite-caps',
887
- };
888
- }),
889
- },
890
- ...resolveDeepOverrides((styles, slot) => {
891
- styles[slot.slotName] = {
892
- mixBlendMode: 'darken',
893
- };
894
- }),
895
- },
896
- },
897
- },
898
- });
899
-
900
- const { container, setProps } = await render(
901
- <ThemeProvider theme={theme}>{element}</ThemeProvider>,
902
- );
903
-
904
- if (testRootOverrides.slotClassName) {
905
- expect(
906
- document.querySelector(`.${testRootOverrides.slotClassName}`),
907
- ).to.toHaveComputedStyle(testStyle);
908
- } else {
909
- expect(container.firstChild).to.toHaveComputedStyle(testStyle);
910
- }
911
-
912
- if (testDeepOverrides) {
913
- (Array.isArray(testDeepOverrides) ? testDeepOverrides : [testDeepOverrides]).forEach(
914
- (slot) => {
915
- expect(document.querySelector(`.${slot.slotClassName}`)).to.toHaveComputedStyle({
916
- fontVariantCaps: 'all-petite-caps',
917
- mixBlendMode: 'darken',
918
- });
919
- },
920
- );
921
-
922
- const themeWithoutRootOverrides = createTheme({
923
- components: {
924
- [muiName]: {
925
- styleOverrides: {
926
- ...resolveDeepOverrides((styles, slot) => {
927
- styles[slot.slotName] = testStyle;
928
- }),
929
- },
930
- },
931
- },
932
- });
933
-
934
- setProps({ theme: themeWithoutRootOverrides });
935
-
936
- (Array.isArray(testDeepOverrides) ? testDeepOverrides : [testDeepOverrides]).forEach(
937
- (slot) => {
938
- expect(document.querySelector(`.${slot.slotClassName}`)).to.toHaveComputedStyle(
939
- testStyle,
940
- );
941
- },
942
- );
943
- }
944
- });
945
-
946
- it('overrideStyles does not replace each other in slots', async function test(t = {}) {
947
- if (window.navigator.userAgent.includes('jsdom')) {
948
- // @ts-ignore
949
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
950
- this?.skip?.() ?? t?.skip();
951
- }
952
-
953
- const { muiName, classes, testStateOverrides, render, ThemeProvider, createTheme } =
954
- getOptions();
955
-
956
- if (!ThemeProvider) {
957
- throwMissingPropError('ThemeProvider');
958
- }
959
-
960
- if (!createTheme) {
961
- throwMissingPropError('createTheme');
962
- }
963
-
964
- const classKeys = Object.keys(classes);
965
-
966
- // only test the component that has `root` and other classKey
967
- if (!testStateOverrides || !classKeys.includes('root') || classKeys.length === 1) {
968
- return;
969
- }
970
-
971
- // `styleKey` in some tests is `foo` or `bar`, so need to check if it is a valid classKey.
972
- const isStyleKeyExists = classKeys.includes(testStateOverrides.styleKey);
973
-
974
- if (!isStyleKeyExists) {
975
- return;
976
- }
977
-
978
- const theme = createTheme({
979
- components: {
980
- [muiName]: {
981
- styleOverrides: {
982
- root: {
983
- [`&.${classes.root}`]: {
984
- filter: 'blur(1px)',
985
- mixBlendMode: 'darken',
986
- },
987
- },
988
- ...(testStateOverrides && {
989
- [testStateOverrides.styleKey]: {
990
- [`&.${classes.root}`]: {
991
- mixBlendMode: 'color',
992
- },
993
- },
994
- }),
995
- },
996
- },
997
- },
998
- });
999
-
1000
- if (!testStateOverrides.prop) {
1001
- return;
1002
- }
1003
-
1004
- await render(
1005
- <ThemeProvider theme={theme}>
1006
- {React.cloneElement(element, {
1007
- [testStateOverrides.prop]: testStateOverrides.value,
1008
- })}
1009
- </ThemeProvider>,
1010
- );
1011
-
1012
- expect(document.querySelector(`.${classes.root}`)).toHaveComputedStyle({
1013
- filter: 'blur(1px)', // still valid in root
1014
- mixBlendMode: 'color', // overridden by `styleKey`
1015
- });
1016
- });
1017
- });
1018
- }
1019
-
1020
- /**
1021
- * MUI theme has a components section that allows specifying custom variants.
1022
- * Components from @inheritComponent
1023
- */
1024
- function testThemeVariants(
1025
- element: React.ReactElement<DataProps & { variant: string }>,
1026
- getOptions: () => ConformanceOptions,
1027
- ) {
1028
- describe('theme variants:', () => {
1029
- it("respect theme's variants", async function test(t = {}) {
1030
- if (window.navigator.userAgent.includes('jsdom')) {
1031
- // @ts-ignore
1032
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1033
- this?.skip?.() ?? t?.skip();
1034
- }
1035
-
1036
- const { muiName, testVariantProps, render, ThemeProvider, createTheme } = getOptions();
1037
-
1038
- if (!testVariantProps) {
1039
- throw new Error('missing testVariantProps');
1040
- }
1041
-
1042
- if (!muiName) {
1043
- throwMissingPropError('muiName');
1044
- }
1045
-
1046
- if (!render) {
1047
- throwMissingPropError('render');
1048
- }
1049
-
1050
- if (!ThemeProvider) {
1051
- throwMissingPropError('ThemeProvider');
1052
- }
1053
-
1054
- if (!createTheme) {
1055
- throwMissingPropError('createTheme');
1056
- }
1057
-
1058
- const testStyle = {
1059
- mixBlendMode: 'darken',
1060
- };
1061
-
1062
- const theme = createTheme({
1063
- components: {
1064
- [muiName]: {
1065
- variants: [
1066
- {
1067
- props: testVariantProps,
1068
- style: testStyle,
1069
- },
1070
- ],
1071
- },
1072
- },
1073
- });
1074
-
1075
- const { getByTestId } = await render(
1076
- <ThemeProvider theme={theme}>
1077
- {React.cloneElement(element, { ...testVariantProps, 'data-testid': 'with-props' })}
1078
- {React.cloneElement(element, { 'data-testid': 'without-props' })}
1079
- </ThemeProvider>,
1080
- );
1081
-
1082
- expect(getByTestId('with-props')).to.toHaveComputedStyle(testStyle);
1083
- expect(getByTestId('without-props')).not.to.toHaveComputedStyle(testStyle);
1084
- });
1085
-
1086
- it('supports custom variant', async function test(t = {}) {
1087
- if (window.navigator.userAgent.includes('jsdom')) {
1088
- // @ts-ignore
1089
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1090
- this?.skip?.() ?? t?.skip();
1091
- }
1092
-
1093
- const { muiName, testCustomVariant, render, ThemeProvider, createTheme } = getOptions();
1094
-
1095
- if (!ThemeProvider) {
1096
- throwMissingPropError('ThemeProvider');
1097
- }
1098
-
1099
- if (!createTheme) {
1100
- throwMissingPropError('createTheme');
1101
- }
1102
-
1103
- if (!testCustomVariant) {
1104
- return;
1105
- }
1106
-
1107
- const theme = createTheme({
1108
- components: {
1109
- [muiName]: {
1110
- styleOverrides: {
1111
- root: ({ ownerState }: { ownerState: any }) => ({
1112
- ...(ownerState.variant === 'unknown' && {
1113
- mixBlendMode: 'darken',
1114
- }),
1115
- }),
1116
- },
1117
- },
1118
- },
1119
- });
1120
-
1121
- const { getByTestId } = await render(
1122
- <ThemeProvider theme={theme}>
1123
- {React.cloneElement(element, { variant: 'unknown', 'data-testid': 'custom-variant' })}
1124
- </ThemeProvider>,
1125
- );
1126
-
1127
- expect(getByTestId('custom-variant')).toHaveComputedStyle({ mixBlendMode: 'darken' });
1128
- });
1129
- });
1130
- }
1131
-
1132
- /**
1133
- * MUI theme supports custom palettes.
1134
- * The components that iterate over the palette via `variants` should be able to render with or without applying the custom palette styles.
1135
- */
1136
- function testThemeCustomPalette(
1137
- element: React.ReactElement<unknown>,
1138
- getOptions: () => ConformanceOptions,
1139
- ) {
1140
- describe('theme extended palette:', () => {
1141
- it('should render without errors', function test(t = {}) {
1142
- const { render, ThemeProvider, createTheme } = getOptions();
1143
- if (
1144
- !window.navigator.userAgent.includes('jsdom') ||
1145
- !render ||
1146
- !ThemeProvider ||
1147
- !createTheme
1148
- ) {
1149
- // @ts-ignore
1150
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1151
- this?.skip?.() ?? t?.skip();
1152
- }
1153
- // @ts-ignore
1154
- const theme = createTheme({
1155
- palette: {
1156
- custom: {
1157
- main: '#ff5252',
1158
- },
1159
- unknown: null,
1160
- custom2: {
1161
- main: {
1162
- blue: {
1163
- dark: '#FFCC00',
1164
- },
1165
- },
1166
- },
1167
- },
1168
- });
1169
- // @ts-ignore
1170
- expect(() => render(<ThemeProvider theme={theme}>{element}</ThemeProvider>)).not.to.throw();
1171
- });
1172
- });
1173
- }
1174
-
1175
- const fullSuite = {
1176
- componentProp: testComponentProp,
1177
- componentsProp: testComponentsProp,
1178
- mergeClassName: testClassName,
1179
- propsSpread: testPropsSpread,
1180
- refForwarding: describeRef,
1181
- rootClass: testRootClass,
1182
- slotPropsProp: testSlotPropsProp,
1183
- slotPropsCallback: testSlotPropsCallback,
1184
- slotPropsCallbackWithPropsAsOwnerState: testSlotPropsCallbackWithPropsAsOwnerState,
1185
- slotsProp: testSlotsProp,
1186
- themeDefaultProps: testThemeDefaultProps,
1187
- themeStyleOverrides: testThemeStyleOverrides,
1188
- themeVariants: testThemeVariants,
1189
- themeCustomPalette: testThemeCustomPalette,
1190
- };
1191
-
1192
- /**
1193
- * Tests various aspects of a component that should be equal across MUI
1194
- * components.
1195
- */
1196
- function describeConformance(
1197
- minimalElement: React.ReactElement<any>,
1198
- getOptions: () => ConformanceOptions,
1199
- ) {
1200
- let originalMatchmedia: typeof window.matchMedia;
1201
- const storage: Record<string, string> = {};
1202
- beforeEach(() => {
1203
- originalMatchmedia = window.matchMedia;
1204
- // Create mocks of localStorage getItem and setItem functions
1205
- Object.defineProperty(globalThis, 'localStorage', {
1206
- value: {
1207
- getItem: (key: string) => storage[key],
1208
- setItem: (key: string, value: string) => {
1209
- storage[key] = value;
1210
- },
1211
- },
1212
- configurable: true,
1213
- });
1214
- window.matchMedia = () =>
1215
- ({
1216
- // Keep mocking legacy methods because @mui/material v5 still uses them
1217
- addListener: () => {},
1218
- addEventListener: () => {},
1219
- removeListener: () => {},
1220
- removeEventListener: () => {},
1221
- }) as unknown as MediaQueryList;
1222
- });
1223
- afterEach(() => {
1224
- window.matchMedia = originalMatchmedia;
1225
- });
1226
- const {
1227
- after: runAfterHook = () => {},
1228
- only = Object.keys(fullSuite),
1229
- slots,
1230
- skip = [],
1231
- } = getOptions();
1232
-
1233
- let filteredTests = Object.keys(fullSuite).filter(
1234
- (testKey) => only.includes(testKey) && !skip.includes(testKey as keyof typeof fullSuite),
1235
- ) as (keyof typeof fullSuite)[];
1236
-
1237
- const slotBasedTests = [
1238
- 'slotsProp',
1239
- 'slotPropsProp',
1240
- 'slotPropsCallback',
1241
- 'slotPropsCallbackWithPropsAsOwnerState',
1242
- ];
1243
-
1244
- if (!slots) {
1245
- // if `slots` are not defined, do not run tests that depend on them
1246
- filteredTests = filteredTests.filter((testKey) => !slotBasedTests.includes(testKey));
1247
- }
1248
-
1249
- after(runAfterHook);
1250
-
1251
- filteredTests.forEach((testKey) => {
1252
- const test = fullSuite[testKey];
1253
- test(minimalElement, getOptions);
1254
- });
1255
- }
1256
-
1257
- export default createDescribe('MUI component API', describeConformance);