@samitouri / QOS-React / commits / 47d87028a7

Moar attribute tests (#10509)

* WIP * WIP Add the rest of the tests for what we expect re: unknown attributes **what is the change?:** Adds tests for the following behavior - - Numbers and booleans should be converted to strings, and not warn - NaN, Symbols, functions, and objects should be converted to strings, and *should* warn Going to add tests for the not-warning behavior in a follow-up. These tests are not entirely passing - we either need to change what we expect or change the behavior. **why make this change?:** Gets everyone on the same page about expected behavior, and codifies it in a maintainable way **test plan:** `yarn test src/renderers/dom/shared/__tests__/ReactDOMAttribute-test.js` **issue:** https://github.com/facebook/react/issues/10399 * WIP Add check that we *don't* warn when handling some unknown attributes **what is the change?:** We are testing the behavior of unknown attributes, which has changed since React 15. We want to *not* warn for the following cases - - null - undefined - missing - strings - numbers - booleans **why make this change?:** We want to verify that warnings don't get fired at the wrong time. **test plan:** `yarn test src/renderers/dom/shared/__tests__/ReactDOMAttribute-test.js` **issue:** https://github.com/facebook/react/issues/10399 * ran prettier * Symbols and functions passed to unknown attributes should remove and warn * Abstract tests a bit to make them easier to read * Remove Markdown from test names I don't think we use this convention anywhere. * Add an assertion for NaN warning message * Update ReactDOMAttribute test based on attribute fixture **what is the change?:** - booleans don't get stringified - some warnings have changed since we originally wrote this **why make this change?:** The attribute behavior is finalized and now we can test it :D I also found it handy to have a row with a truly unknown attribute, so added "imaginaryFriend". **test plan:** `yarn test src/renderers/dom/shared/__tests__/ReactDOMAttribute-test.js` and comparing the tests to the attribute table **issue:** https://github.com/facebook/react/issues/10399 * remove imaginaryFriend to resolve conflict * ran prettier

Andrew Clark committed Sep 5, 2017 at 11:25 UTC 47d87028a7c5f06d509653487683599a5a292224
1 file changed +137
src/renderers/dom/shared/__tests__/ReactDOMAttribute-test.js new
+137
@@ -0,0 +1,137 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +'use strict';
13 +
14 +describe('ReactDOM unknown attribute', () => {
15 + var React;
16 + var ReactDOM;
17 +
18 + beforeEach(() => {
19 + jest.resetModules();
20 + React = require('react');
21 + ReactDOM = require('react-dom');
22 + });
23 +
24 + function normalizeCodeLocInfo(str) {
25 + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
26 + }
27 +
28 + function testUnknownAttributeRemoval(givenValue) {
29 + var el = document.createElement('div');
30 + ReactDOM.render(<div unknown="something" />, el);
31 + expect(el.firstChild.getAttribute('unknown')).toBe('something');
32 + ReactDOM.render(<div unknown={givenValue} />, el);
33 + expect(el.firstChild.hasAttribute('unknown')).toBe(false);
34 + }
35 +
36 + function testUnknownAttributeAssignment(givenValue, expectedDOMValue) {
37 + var el = document.createElement('div');
38 + ReactDOM.render(<div unknown="something" />, el);
39 + expect(el.firstChild.getAttribute('unknown')).toBe('something');
40 + ReactDOM.render(<div unknown={givenValue} />, el);
41 + expect(el.firstChild.getAttribute('unknown')).toBe(expectedDOMValue);
42 + }
43 +
44 + describe('unknown attributes', () => {
45 + it('removes values null and undefined', () => {
46 + testUnknownAttributeRemoval(null);
47 + testUnknownAttributeRemoval(undefined);
48 + });
49 +
50 + it('changes values true, false to null, and also warns once', () => {
51 + spyOn(console, 'error');
52 +
53 + testUnknownAttributeAssignment(true, null);
54 + testUnknownAttributeAssignment(false, null);
55 +
56 + expectDev(
57 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
58 + ).toMatch(
59 + 'Warning: Received `true` for non-boolean attribute `unknown`. ' +
60 + 'If this is expected, cast the value to a string.\n' +
61 + ' in div (at **)',
62 + );
63 + expectDev(console.error.calls.count()).toBe(1);
64 + });
65 +
66 + it('removes unknown attributes that were rendered but are now missing', () => {
67 + var el = document.createElement('div');
68 + ReactDOM.render(<div unknown="something" />, el);
69 + expect(el.firstChild.getAttribute('unknown')).toBe('something');
70 + ReactDOM.render(<div />, el);
71 + expect(el.firstChild.hasAttribute('unknown')).toBe(false);
72 + });
73 +
74 + it('passes through strings', () => {
75 + testUnknownAttributeAssignment('a string', 'a string');
76 + });
77 +
78 + it('coerces numbers to strings', () => {
79 + testUnknownAttributeAssignment(0, '0');
80 + testUnknownAttributeAssignment(-1, '-1');
81 + testUnknownAttributeAssignment(42, '42');
82 + testUnknownAttributeAssignment(9000.99, '9000.99');
83 + });
84 +
85 + it('coerces NaN to strings and warns', () => {
86 + spyOn(console, 'error');
87 +
88 + testUnknownAttributeAssignment(NaN, 'NaN');
89 + expectDev(
90 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
91 + ).toMatch(
92 + 'Warning: Received NaN for numeric attribute `unknown`. ' +
93 + 'If this is expected, cast the value to a string.\n' +
94 + ' in div (at **)',
95 + );
96 + expectDev(console.error.calls.count()).toBe(1);
97 + });
98 +
99 + it('coerces objects to strings **and warns**', () => {
100 + const lol = {
101 + toString() {
102 + return 'lol';
103 + },
104 + };
105 +
106 + testUnknownAttributeAssignment({hello: 'world'}, '[object Object]');
107 + testUnknownAttributeAssignment(lol, 'lol');
108 + });
109 +
110 + it('removes symbols and warns', () => {
111 + spyOn(console, 'error');
112 +
113 + testUnknownAttributeRemoval(Symbol('foo'));
114 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
115 + 'Warning: Invalid prop `unknown` on <div> tag. Either remove this ' +
116 + 'prop from the element, or pass a string or number value to keep it ' +
117 + 'in the DOM. For details, see https://fb.me/react-unknown-prop\n' +
118 + ' in div (at **)',
119 + );
120 + expectDev(console.error.calls.count()).toBe(1);
121 + });
122 +
123 + it('removes functions and warns', () => {
124 + spyOn(console, 'error');
125 +
126 + testUnknownAttributeRemoval(function someFunction() {});
127 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
128 + 'Warning: Invalid prop `unknown` on <div> tag. Either remove this ' +
129 + 'prop from the element, or pass a string or number value to ' +
130 + 'keep it in the DOM. For details, see ' +
131 + 'https://fb.me/react-unknown-prop\n' +
132 + ' in div (at **)',
133 + );
134 + expectDev(console.error.calls.count()).toBe(1);
135 + });
136 + });
137 +});