UNPKG

8.8 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.default = describeConformance;
9
10var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
12var _chai = require("chai");
13
14var _react = _interopRequireDefault(require("react"));
15
16var _findOutermostIntrinsic = _interopRequireDefault(require("./findOutermostIntrinsic"));
17
18var _reactTestRenderer = _interopRequireDefault(require("react-test-renderer"));
19
20var _testRef = _interopRequireDefault(require("./testRef"));
21
22/**
23 * Glossary
24 * - root component:
25 * - renders the outermost host component
26 * - has the `root` class if the component has one
27 * - excess props are spread to this component
28 * - has the type of `inheritComponent`
29 */
30
31/**
32 * Returns the component with the same constructor as `component` that renders
33 * the outermost host
34 *
35 * @param {import('enzyme').ReactWrapper} wrapper
36 * @param {object} options
37 * @param {import('react').ElementType} component
38 */
39function findRootComponent(wrapper, _ref) {
40 var component = _ref.component;
41 var outermostHostElement = (0, _findOutermostIntrinsic.default)(wrapper).getElement();
42 return wrapper.find(component).filterWhere(function (componentWrapper) {
43 return componentWrapper.contains(outermostHostElement);
44 });
45}
46
47function randomStringValue() {
48 return Math.random().toString(36).slice(2);
49}
50/**
51 * Material-UI components have a `className` prop. The `className` is applied to
52 * the root component.
53 *
54 * @param {React.ReactElement} element
55 * @param {() => ConformanceOptions} getOptions
56 */
57
58
59function testClassName(element, getOptions) {
60 it('applies the className to the root component', function () {
61 var _getOptions = getOptions(),
62 mount = _getOptions.mount;
63
64 var className = randomStringValue();
65 var wrapper = mount(_react.default.cloneElement(element, {
66 className: className
67 }));
68
69 _chai.assert.strictEqual((0, _findOutermostIntrinsic.default)(wrapper).hasClass(className), true, 'does have a custom `className`');
70 });
71}
72/**
73 * Material-UI components have a `component` prop that allows rendering a different
74 * Component from @inheritComponent
75 *
76 * @param {React.ReactElement} element
77 * @param {() => ConformanceOptions} getOptions
78 */
79
80
81function testComponentProp(element, getOptions) {
82 describe('prop: component', function () {
83 it('can render another root component with the `component` prop', function () {
84 var _getOptions2 = getOptions(),
85 classes = _getOptions2.classes,
86 mount = _getOptions2.mount,
87 _getOptions2$testComp = _getOptions2.testComponentPropWith,
88 component = _getOptions2$testComp === void 0 ? 'em' : _getOptions2$testComp;
89
90 var wrapper = mount(_react.default.cloneElement(element, {
91 component: component
92 }));
93
94 _chai.assert.strictEqual(findRootComponent(wrapper, {
95 classes: classes,
96 component: component
97 }).exists(), true);
98 });
99 });
100}
101/**
102 * Material-UI components can spread additional props to a documented component.
103 * It's set via @inheritComponent in the source.
104 *
105 * @param {React.ReactElement} element
106 * @param {() => ConformanceOptions} getOptions
107 */
108
109
110function testPropsSpread(element, getOptions) {
111 it("does spread props to the root component", function () {
112 // type def in ConformanceOptions
113 var _getOptions3 = getOptions(),
114 classes = _getOptions3.classes,
115 inheritComponent = _getOptions3.inheritComponent,
116 mount = _getOptions3.mount;
117
118 var testProp = 'data-test-props-spread';
119 var value = randomStringValue();
120 var wrapper = mount(_react.default.cloneElement(element, (0, _defineProperty2.default)({}, testProp, value)));
121 var root = findRootComponent(wrapper, {
122 classes: classes,
123 component: inheritComponent
124 });
125
126 _chai.assert.strictEqual(root.props()[testProp], value);
127 });
128}
129/**
130 * Tests that the `ref` of a component will return the correct instance
131 *
132 * This is determined by a given constructor i.e. a React.Component or HTMLElement for
133 * components that forward their ref and attach it to a host component.
134 *
135 * @param {React.ReactElement} element
136 * @param {() => ConformanceOptions} getOptions
137 */
138
139
140function describeRef(element, getOptions) {
141 describe('ref', function () {
142 it("attaches the ref", function () {
143 // type def in ConformanceOptions
144 var _getOptions4 = getOptions(),
145 inheritComponent = _getOptions4.inheritComponent,
146 mount = _getOptions4.mount,
147 refInstanceof = _getOptions4.refInstanceof;
148
149 (0, _testRef.default)(element, mount, function (instance, wrapper) {
150 _chai.assert.instanceOf(instance, refInstanceof);
151
152 if (inheritComponent && instance instanceof window.Element) {
153 var rootHost = (0, _findOutermostIntrinsic.default)(wrapper);
154
155 _chai.assert.strictEqual(instance, rootHost.instance());
156 }
157 });
158 });
159 });
160}
161/**
162 * Tests that the root component has the root class
163 *
164 * @param {React.ReactElement} element
165 * @param {() => ConformanceOptions} getOptions
166 */
167
168
169function testRootClass(element, getOptions) {
170 it('applies to root class to the root component if it has this class', function () {
171 var _getOptions5 = getOptions(),
172 classes = _getOptions5.classes,
173 mount = _getOptions5.mount;
174
175 if (classes.root == null) {
176 return;
177 }
178
179 var className = randomStringValue();
180 var wrapper = mount(_react.default.cloneElement(element, {
181 className: className
182 })); // we established that the root component renders the outermost host previously. We immediately
183 // jump to the host component because some components pass the `root` class
184 // to the `classes` prop of the root component.
185 // https://github.com/mui-org/material-ui/blob/f9896bcd129a1209153106296b3d2487547ba205/packages/material-ui/src/OutlinedInput/OutlinedInput.js#L101
186
187 _chai.assert.strictEqual((0, _findOutermostIntrinsic.default)(wrapper).hasClass(classes.root), true);
188
189 _chai.assert.strictEqual((0, _findOutermostIntrinsic.default)(wrapper).hasClass(className), true);
190 });
191}
192/**
193 * Tests that the component can be rendered with react-test-renderer.
194 * This is important for snapshot testing with Jest (even if we don't encourage it).
195 *
196 * @param {React.ReactElement} element
197 */
198
199
200function testReactTestRenderer(element) {
201 it('should render without errors in ReactTestRenderer', function () {
202 _reactTestRenderer.default.act(function () {
203 _reactTestRenderer.default.create(element, {
204 createNodeMock: function createNodeMock(node) {
205 return document.createElement(node.type);
206 }
207 });
208 });
209 });
210}
211
212var fullSuite = {
213 componentProp: testComponentProp,
214 mergeClassName: testClassName,
215 propsSpread: testPropsSpread,
216 refForwarding: describeRef,
217 rootClass: testRootClass,
218 reactTestRenderer: testReactTestRenderer
219};
220/**
221 * @typedef {Object} ConformanceOptions
222 * @property {Record<string, string>} classes - `classes` of the component provided by `@material-ui/styles`
223 * @property {string} inheritComponent - The element type that receives spread props.
224 * @property {function} mount - Should be a return value from createMount
225 * @property {(keyof typeof fullSuite)[]} [only] - If specified only run the tests listed
226 * @property {any} refInstanceof - `ref` will be an instanceof this constructor.
227 * @property {keyof typeof fullSuite[]} [skip] - Skip the specified tests
228 * @property {string} [testComponentPropWith] - The host component that should be rendered instead.
229 */
230
231/**
232 * Tests various aspects of a component that should be equal across Material-UI
233 * components.
234 *
235 * @param {React.ReactElement} minimalElement - the component with it's minimal required props
236 * @param {() => ConformanceOptions} getOptions
237 *
238 */
239
240function describeConformance(minimalElement, getOptions) {
241 var _getOptions6 = getOptions(),
242 _getOptions6$after = _getOptions6.after,
243 runAfterHook = _getOptions6$after === void 0 ? function () {} : _getOptions6$after,
244 _getOptions6$only = _getOptions6.only,
245 only = _getOptions6$only === void 0 ? Object.keys(fullSuite) : _getOptions6$only,
246 _getOptions6$skip = _getOptions6.skip,
247 skip = _getOptions6$skip === void 0 ? [] : _getOptions6$skip;
248
249 describe('Material-UI component API', function () {
250 after(runAfterHook);
251 Object.keys(fullSuite).filter(function (testKey) {
252 return only.indexOf(testKey) !== -1 && skip.indexOf(testKey) === -1;
253 }).forEach(function (testKey) {
254 var test = fullSuite[testKey];
255 test(minimalElement, getOptions);
256 });
257 });
258}
\No newline at end of file