UNPKG

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