@samitouri / QOS-React / commits / 14863c9a87

Adding SSR test for form fields.

Sasha Aickin committed Mar 26, 2017 at 20:51 UTC 14863c9a8722281b6bf1d0d9fc7b95b44e189354
4 files changed +670 -15
scripts/fiber/tests-failing.txt
+36
@@ -102,6 +102,42 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
102 * renders >,<, and & as single child with client render on top of good server markup
103 * renders >,<, and & as multiple children with server string render
104 * renders >,<, and & as multiple children with client render on top of good server markup
105 +* renders an input with a value and an onChange with client render on top of good server markup
106 +* renders an input with a value and readOnly with client render on top of good server markup
107 +* renders an input with a value and no onChange/readOnly with client render on top of good server markup
108 +* renders an input with a defaultValue with client render on top of good server markup
109 +* renders an input value overriding defaultValue with client render on top of good server markup
110 +* renders an input value overriding defaultValue no matter the prop order with client render on top of good server markup
111 +* renders a checkbox that is checked with an onChange with client render on top of good server markup
112 +* renders a checkbox that is checked with readOnly with client render on top of good server markup
113 +* renders a checkbox that is checked and no onChange/readOnly with client render on top of good server markup
114 +* renders a checkbox with defaultChecked with client render on top of good server markup
115 +* renders a checkbox checked overriding defaultChecked with client render on top of good server markup
116 +* renders a checkbox checked overriding defaultChecked no matter the prop order with client render on top of good server markup
117 +* renders a textarea with a value and an onChange with client render on top of good server markup
118 +* renders a textarea with a value and readOnly with client render on top of good server markup
119 +* renders a textarea with a value and no onChange/readOnly with client render on top of good server markup
120 +* renders a textarea with a defaultValue with client render on top of good server markup
121 +* renders a textarea value overriding defaultValue with client render on top of good server markup
122 +* renders a textarea value overriding defaultValue no matter the prop order with client render on top of good server markup
123 +* renders a select with a value and an onChange with client render on top of good server markup
124 +* renders a select with a value and readOnly with client render on top of good server markup
125 +* renders a select with a multiple values and an onChange with client render on top of good server markup
126 +* renders a select with a multiple values and readOnly with client render on top of good server markup
127 +* renders a select with a value and no onChange/readOnly with client render on top of good server markup
128 +* renders a select with a defaultValue with client render on top of good server markup
129 +* renders a select value overriding defaultValue with client render on top of good server markup
130 +* renders a select value overriding defaultValue no matter the prop order with client render on top of good server markup
131 +* renders a controlled text input with client render on top of good server markup
132 +* renders a controlled textarea with client render on top of good server markup
133 +* renders a controlled checkbox with client render on top of good server markup
134 +* renders a controlled select with client render on top of good server markup
135 +* should not blow away user-entered text on successful reconnect to an uncontrolled input
136 +* should not blow away user-entered text on successful reconnect to a controlled input
137 +* should not blow away user-entered text on successful reconnect to an uncontrolled checkbox
138 +* should not blow away user-entered text on successful reconnect to a controlled checkbox
139 +* should not blow away user-selected value on successful reconnect to an uncontrolled select
140 +* should not blow away user-selected value on successful reconnect to an controlled select
141 * renders class child with context with client render on top of good server markup
142 * renders stateless child with context with client render on top of good server markup
143 * renders class child without context with client render on top of good server markup
scripts/fiber/tests-passing-except-dev.txt
+30
@@ -95,6 +95,36 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
95 * throws when rendering an undefined component with client render on top of bad server markup
96 * throws when rendering a number component with clean client render
97 * throws when rendering a number component with client render on top of bad server markup
98 +* renders an input with a value and an onChange with client render on top of bad server markup
99 +* renders an input with a value and readOnly with client render on top of bad server markup
100 +* renders an input with a value and no onChange/readOnly with client render on top of bad server markup
101 +* renders an input with a defaultValue with client render on top of bad server markup
102 +* renders an input value overriding defaultValue with client render on top of bad server markup
103 +* renders an input value overriding defaultValue no matter the prop order with client render on top of bad server markup
104 +* renders a checkbox that is checked with an onChange with client render on top of bad server markup
105 +* renders a checkbox that is checked with readOnly with client render on top of bad server markup
106 +* renders a checkbox that is checked and no onChange/readOnly with client render on top of bad server markup
107 +* renders a checkbox with defaultChecked with client render on top of bad server markup
108 +* renders a checkbox checked overriding defaultChecked with client render on top of bad server markup
109 +* renders a checkbox checked overriding defaultChecked no matter the prop order with client render on top of bad server markup
110 +* renders a textarea with a value and an onChange with client render on top of bad server markup
111 +* renders a textarea with a value and readOnly with client render on top of bad server markup
112 +* renders a textarea with a value and no onChange/readOnly with client render on top of bad server markup
113 +* renders a textarea with a defaultValue with client render on top of bad server markup
114 +* renders a textarea value overriding defaultValue with client render on top of bad server markup
115 +* renders a textarea value overriding defaultValue no matter the prop order with client render on top of bad server markup
116 +* renders a select with a value and an onChange with client render on top of bad server markup
117 +* renders a select with a value and readOnly with client render on top of bad server markup
118 +* renders a select with a multiple values and an onChange with client render on top of bad server markup
119 +* renders a select with a multiple values and readOnly with client render on top of bad server markup
120 +* renders a select with a value and no onChange/readOnly with client render on top of bad server markup
121 +* renders a select with a defaultValue with client render on top of bad server markup
122 +* renders a select value overriding defaultValue with client render on top of bad server markup
123 +* renders a select value overriding defaultValue no matter the prop order with client render on top of bad server markup
124 +* renders a controlled text input with client render on top of bad server markup
125 +* renders a controlled textarea with client render on top of bad server markup
126 +* renders a controlled checkbox with client render on top of bad server markup
127 +* renders a controlled select with client render on top of bad server markup
128 * renders class child with context with client render on top of bad server markup
129 * renders stateless child with context with client render on top of bad server markup
130 * renders class child without context with client render on top of bad server markup
scripts/fiber/tests-passing.txt
+56 -1
@@ -1154,6 +1154,62 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1154 * throws when rendering string with server string render
1155 * throws when rendering string with clean client render
1156 * throws when rendering string with client render on top of bad server markup
1157 +* renders an input with a value and an onChange with server string render
1158 +* renders an input with a value and an onChange with clean client render
1159 +* renders an input with a value and readOnly with server string render
1160 +* renders an input with a value and readOnly with clean client render
1161 +* renders an input with a value and no onChange/readOnly with server string render
1162 +* renders an input with a value and no onChange/readOnly with clean client render
1163 +* renders an input with a defaultValue with server string render
1164 +* renders an input with a defaultValue with clean client render
1165 +* renders an input value overriding defaultValue with server string render
1166 +* renders an input value overriding defaultValue with clean client render
1167 +* renders an input value overriding defaultValue no matter the prop order with server string render
1168 +* renders an input value overriding defaultValue no matter the prop order with clean client render
1169 +* renders a checkbox that is checked with an onChange with server string render
1170 +* renders a checkbox that is checked with an onChange with clean client render
1171 +* renders a checkbox that is checked with readOnly with server string render
1172 +* renders a checkbox that is checked with readOnly with clean client render
1173 +* renders a checkbox that is checked and no onChange/readOnly with server string render
1174 +* renders a checkbox that is checked and no onChange/readOnly with clean client render
1175 +* renders a checkbox with defaultChecked with server string render
1176 +* renders a checkbox with defaultChecked with clean client render
1177 +* renders a checkbox checked overriding defaultChecked with server string render
1178 +* renders a checkbox checked overriding defaultChecked with clean client render
1179 +* renders a checkbox checked overriding defaultChecked no matter the prop order with server string render
1180 +* renders a checkbox checked overriding defaultChecked no matter the prop order with clean client render
1181 +* renders a textarea with a value and an onChange with server string render
1182 +* renders a textarea with a value and an onChange with clean client render
1183 +* renders a textarea with a value and readOnly with server string render
1184 +* renders a textarea with a value and readOnly with clean client render
1185 +* renders a textarea with a value and no onChange/readOnly with server string render
1186 +* renders a textarea with a value and no onChange/readOnly with clean client render
1187 +* renders a textarea with a defaultValue with server string render
1188 +* renders a textarea with a defaultValue with clean client render
1189 +* renders a textarea value overriding defaultValue with server string render
1190 +* renders a textarea value overriding defaultValue with clean client render
1191 +* renders a textarea value overriding defaultValue no matter the prop order with server string render
1192 +* renders a textarea value overriding defaultValue no matter the prop order with clean client render
1193 +* renders a select with a value and an onChange with server string render
1194 +* renders a select with a value and an onChange with clean client render
1195 +* renders a select with a value and readOnly with server string render
1196 +* renders a select with a value and readOnly with clean client render
1197 +* renders a select with a multiple values and an onChange with server string render
1198 +* renders a select with a multiple values and an onChange with clean client render
1199 +* renders a select with a multiple values and readOnly with server string render
1200 +* renders a select with a multiple values and readOnly with clean client render
1201 +* renders a select with a value and no onChange/readOnly with server string render
1202 +* renders a select with a value and no onChange/readOnly with clean client render
1203 +* renders a select with a defaultValue with server string render
1204 +* renders a select with a defaultValue with clean client render
1205 +* renders a select value overriding defaultValue with server string render
1206 +* renders a select value overriding defaultValue with clean client render
1207 +* renders a select value overriding defaultValue no matter the prop order with server string render
1208 +* renders a select value overriding defaultValue no matter the prop order with clean client render
1209 +* renders a controlled text input with clean client render
1210 +* renders a controlled textarea with clean client render
1211 +* renders a controlled checkbox with clean client render
1212 +* renders a controlled select with clean client render
1213 * renders class child with context with server string render
1214 * renders class child with context with clean client render
1215 * renders stateless child with context with server string render
@@ -1193,7 +1249,6 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1249 * should reconnect Pure Component to Bare Element
1250 * should reconnect Bare Element to Bare Element
1251 * should reconnect a div with a number and string version of number
1196 -* should reconnect if component trees differ but resulting markup is the same
1252
1253 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
1254 * updates a mounted text component in place
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+548 -14
@@ -16,6 +16,7 @@ let React;
16 let ReactDOM;
17 let ReactDOMServer;
18 let ReactDOMFeatureFlags;
19 +let ReactTestUtils;
20
21 // Helper functions for rendering tests
22 // ====================================
@@ -209,6 +210,7 @@ function resetModules() {
210 ReactDOM = require('ReactDOM');
211 ReactDOMServer = require('ReactDOMServer');
212 ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
213 + ReactTestUtils = require('ReactTestUtils');
214 ExecutionEnvironment = require('ExecutionEnvironment');
215 }
216
@@ -1022,6 +1024,552 @@ describe('ReactDOMServerIntegration', () => {
1024 });
1025 });
1026
1027 + describe('form controls', function() {
1028 + describe('inputs', function() {
1029 + itRenders('an input with a value and an onChange', async render => {
1030 + const e = await render(<input value="foo" onChange={() => {}} />);
1031 + expect(e.value).toBe('foo');
1032 + });
1033 +
1034 + itRenders('an input with a value and readOnly', async render => {
1035 + const e = await render(<input value="foo" readOnly={true} />);
1036 + expect(e.value).toBe('foo');
1037 + });
1038 +
1039 + itRenders(
1040 + 'an input with a value and no onChange/readOnly',
1041 + async render => {
1042 + // this configuration should raise a dev warning that value without
1043 + // onChange or readOnly is a mistake.
1044 + const e = await render(<input value="foo" />, 1);
1045 + expect(e.value).toBe('foo');
1046 + },
1047 + );
1048 +
1049 + itRenders('an input with a defaultValue', async render => {
1050 + const e = await render(<input defaultValue="foo" />);
1051 + expect(e.value).toBe('foo');
1052 + expect(e.getAttribute('defaultValue')).toBe(null);
1053 + });
1054 +
1055 + itRenders('an input value overriding defaultValue', async render => {
1056 + const e = await render(
1057 + <input value="foo" defaultValue="bar" readOnly={true} />,
1058 + 1,
1059 + );
1060 + expect(e.value).toBe('foo');
1061 + expect(e.getAttribute('defaultValue')).toBe(null);
1062 + });
1063 +
1064 + itRenders(
1065 + 'an input value overriding defaultValue no matter the prop order',
1066 + async render => {
1067 + const e = await render(
1068 + <input defaultValue="bar" value="foo" readOnly={true} />,
1069 + 1,
1070 + );
1071 + expect(e.value).toBe('foo');
1072 + expect(e.getAttribute('defaultValue')).toBe(null);
1073 + },
1074 + );
1075 + });
1076 +
1077 + describe('checkboxes', function() {
1078 + itRenders('a checkbox that is checked with an onChange', async render => {
1079 + const e = await render(
1080 + <input type="checkbox" checked={true} onChange={() => {}} />,
1081 + );
1082 + expect(e.checked).toBe(true);
1083 + });
1084 +
1085 + itRenders('a checkbox that is checked with readOnly', async render => {
1086 + const e = await render(
1087 + <input type="checkbox" checked={true} readOnly={true} />,
1088 + );
1089 + expect(e.checked).toBe(true);
1090 + });
1091 +
1092 + itRenders(
1093 + 'a checkbox that is checked and no onChange/readOnly',
1094 + async render => {
1095 + // this configuration should raise a dev warning that checked without
1096 + // onChange or readOnly is a mistake.
1097 + const e = await render(<input type="checkbox" checked={true} />, 1);
1098 + expect(e.checked).toBe(true);
1099 + },
1100 + );
1101 +
1102 + itRenders('a checkbox with defaultChecked', async render => {
1103 + const e = await render(<input type="checkbox" defaultChecked={true} />);
1104 + expect(e.checked).toBe(true);
1105 + expect(e.getAttribute('defaultChecked')).toBe(null);
1106 + });
1107 +
1108 + itRenders(
1109 + 'a checkbox checked overriding defaultChecked',
1110 + async render => {
1111 + const e = await render(
1112 + <input
1113 + type="checkbox"
1114 + checked={true}
1115 + defaultChecked={false}
1116 + readOnly={true}
1117 + />,
1118 + 1,
1119 + );
1120 + expect(e.checked).toBe(true);
1121 + expect(e.getAttribute('defaultChecked')).toBe(null);
1122 + },
1123 + );
1124 +
1125 + itRenders(
1126 + 'a checkbox checked overriding defaultChecked no matter the prop order',
1127 + async render => {
1128 + const e = await render(
1129 + <input
1130 + type="checkbox"
1131 + defaultChecked={false}
1132 + checked={true}
1133 + readOnly={true}
1134 + />,
1135 + 1,
1136 + );
1137 + expect(e.checked).toBe(true);
1138 + expect(e.getAttribute('defaultChecked')).toBe(null);
1139 + },
1140 + );
1141 + });
1142 +
1143 + describe('textareas', function() {
1144 + // textareas
1145 + // ---------
1146 + itRenders('a textarea with a value and an onChange', async render => {
1147 + const e = await render(<textarea value="foo" onChange={() => {}} />);
1148 + // textarea DOM elements don't have a value **attribute**, the text is
1149 + // a child of the element and accessible via the .value **property**.
1150 + expect(e.getAttribute('value')).toBe(null);
1151 + expect(e.value).toBe('foo');
1152 + });
1153 +
1154 + itRenders('a textarea with a value and readOnly', async render => {
1155 + const e = await render(<textarea value="foo" readOnly={true} />);
1156 + // textarea DOM elements don't have a value **attribute**, the text is
1157 + // a child of the element and accessible via the .value **property**.
1158 + expect(e.getAttribute('value')).toBe(null);
1159 + expect(e.value).toBe('foo');
1160 + });
1161 +
1162 + itRenders(
1163 + 'a textarea with a value and no onChange/readOnly',
1164 + async render => {
1165 + // this configuration should raise a dev warning that value without
1166 + // onChange or readOnly is a mistake.
1167 + const e = await render(<textarea value="foo" />, 1);
1168 + expect(e.getAttribute('value')).toBe(null);
1169 + expect(e.value).toBe('foo');
1170 + },
1171 + );
1172 +
1173 + itRenders('a textarea with a defaultValue', async render => {
1174 + const e = await render(<textarea defaultValue="foo" />);
1175 + expect(e.getAttribute('value')).toBe(null);
1176 + expect(e.getAttribute('defaultValue')).toBe(null);
1177 + expect(e.value).toBe('foo');
1178 + });
1179 +
1180 + itRenders('a textarea value overriding defaultValue', async render => {
1181 + const e = await render(
1182 + <textarea value="foo" defaultValue="bar" readOnly={true} />,
1183 + 1,
1184 + );
1185 + expect(e.getAttribute('value')).toBe(null);
1186 + expect(e.getAttribute('defaultValue')).toBe(null);
1187 + expect(e.value).toBe('foo');
1188 + });
1189 +
1190 + itRenders(
1191 + 'a textarea value overriding defaultValue no matter the prop order',
1192 + async render => {
1193 + const e = await render(
1194 + <textarea defaultValue="bar" value="foo" readOnly={true} />,
1195 + 1,
1196 + );
1197 + expect(e.getAttribute('value')).toBe(null);
1198 + expect(e.getAttribute('defaultValue')).toBe(null);
1199 + expect(e.value).toBe('foo');
1200 + },
1201 + );
1202 + });
1203 +
1204 + describe('selects', function() {
1205 + var options;
1206 + beforeEach(function() {
1207 + options = [
1208 + <option key={1} value="foo" id="foo">Foo</option>,
1209 + <option key={2} value="bar" id="bar">Bar</option>,
1210 + <option key={3} value="baz" id="baz">Baz</option>,
1211 + ];
1212 + });
1213 +
1214 + // a helper function to test the selected value of a <select> element.
1215 + // takes in a <select> DOM element (element) and a value or array of
1216 + // values that should be selected (selected).
1217 + const expectSelectValue = (element, selected) => {
1218 + if (!Array.isArray(selected)) {
1219 + selected = [selected];
1220 + }
1221 + // the select DOM element shouldn't ever have a value or defaultValue
1222 + // attribute; that is not how select values are expressed in the DOM.
1223 + expect(element.getAttribute('value')).toBe(null);
1224 + expect(element.getAttribute('defaultValue')).toBe(null);
1225 +
1226 + ['foo', 'bar', 'baz'].forEach(value => {
1227 + const expectedValue = selected.indexOf(value) !== -1;
1228 + var option = element.querySelector(`#${value}`);
1229 + expect(option.selected).toBe(expectedValue);
1230 + });
1231 + };
1232 +
1233 + itRenders('a select with a value and an onChange', async render => {
1234 + const e = await render(
1235 + <select value="bar" onChange={() => {}}>{options}</select>,
1236 + );
1237 + expectSelectValue(e, 'bar');
1238 + });
1239 +
1240 + itRenders('a select with a value and readOnly', async render => {
1241 + const e = await render(
1242 + <select value="bar" readOnly={true}>{options}</select>,
1243 + );
1244 + expectSelectValue(e, 'bar');
1245 + });
1246 +
1247 + itRenders(
1248 + 'a select with a multiple values and an onChange',
1249 + async render => {
1250 + const e = await render(
1251 + <select value={['bar', 'baz']} multiple={true} onChange={() => {}}>
1252 + {options}
1253 + </select>,
1254 + );
1255 + expectSelectValue(e, ['bar', 'baz']);
1256 + },
1257 + );
1258 +
1259 + itRenders(
1260 + 'a select with a multiple values and readOnly',
1261 + async render => {
1262 + const e = await render(
1263 + <select value={['bar', 'baz']} multiple={true} readOnly={true}>
1264 + {options}
1265 + </select>,
1266 + );
1267 + expectSelectValue(e, ['bar', 'baz']);
1268 + },
1269 + );
1270 +
1271 + itRenders(
1272 + 'a select with a value and no onChange/readOnly',
1273 + async render => {
1274 + // this configuration should raise a dev warning that value without
1275 + // onChange or readOnly is a mistake.
1276 + const e = await render(<select value="bar">{options}</select>, 1);
1277 + expectSelectValue(e, 'bar');
1278 + },
1279 + );
1280 +
1281 + itRenders('a select with a defaultValue', async render => {
1282 + const e = await render(<select defaultValue="bar">{options}</select>);
1283 + expectSelectValue(e, 'bar');
1284 + });
1285 +
1286 + itRenders('a select value overriding defaultValue', async render => {
1287 + const e = await render(
1288 + <select value="bar" defaultValue="baz" readOnly={true}>
1289 + {options}
1290 + </select>,
1291 + 1,
1292 + );
1293 + expectSelectValue(e, 'bar');
1294 + });
1295 +
1296 + itRenders(
1297 + 'a select value overriding defaultValue no matter the prop order',
1298 + async render => {
1299 + const e = await render(
1300 + <select defaultValue="baz" value="bar" readOnly={true}>
1301 + {options}
1302 + </select>,
1303 + 1,
1304 + );
1305 + expectSelectValue(e, 'bar');
1306 + },
1307 + );
1308 + });
1309 +
1310 + describe('user interaction', function() {
1311 + let ControlledInput,
1312 + ControlledTextArea,
1313 + ControlledCheckbox,
1314 + ControlledSelect;
1315 + beforeEach(() => {
1316 + ControlledInput = class extends React.Component {
1317 + constructor() {
1318 + super();
1319 + this.state = {value: 'Hello'};
1320 + }
1321 + handleChange(event) {
1322 + if (this.props.onChange) {
1323 + this.props.onChange(event);
1324 + }
1325 + this.setState({value: event.target.value});
1326 + }
1327 + render() {
1328 + return (
1329 + <input
1330 + value={this.state.value}
1331 + onChange={this.handleChange.bind(this)}
1332 + />
1333 + );
1334 + }
1335 + };
1336 + ControlledTextArea = class extends React.Component {
1337 + constructor() {
1338 + super();
1339 + this.state = {value: 'Hello'};
1340 + }
1341 + handleChange(event) {
1342 + if (this.props.onChange) {
1343 + this.props.onChange(event);
1344 + }
1345 + this.setState({value: event.target.value});
1346 + }
1347 + render() {
1348 + return (
1349 + <textarea
1350 + value={this.state.value}
1351 + onChange={this.handleChange.bind(this)}
1352 + />
1353 + );
1354 + }
1355 + };
1356 + ControlledCheckbox = class extends React.Component {
1357 + constructor() {
1358 + super();
1359 + this.state = {value: true};
1360 + }
1361 + handleChange(event) {
1362 + if (this.props.onChange) {
1363 + this.props.onChange(event);
1364 + }
1365 + this.setState({value: event.target.checked});
1366 + }
1367 + render() {
1368 + return (
1369 + <input
1370 + type="checkbox"
1371 + checked={this.state.value}
1372 + onChange={this.handleChange.bind(this)}
1373 + />
1374 + );
1375 + }
1376 + };
1377 + ControlledSelect = class extends React.Component {
1378 + constructor() {
1379 + super();
1380 + this.state = {value: 'Hello'};
1381 + }
1382 + handleChange(event) {
1383 + if (this.props.onChange) {
1384 + this.props.onChange(event);
1385 + }
1386 + this.setState({value: event.target.value});
1387 + }
1388 + render() {
1389 + return (
1390 + <select
1391 + value={this.state.value}
1392 + onChange={this.handleChange.bind(this)}>
1393 + <option key="1" value="Hello">Hello</option>
1394 + <option key="2" value="Goodbye">Goodbye</option>
1395 + </select>
1396 + );
1397 + }
1398 + };
1399 + });
1400 +
1401 + describe('user interaction with controlled inputs', function() {
1402 + itClientRenders('a controlled text input', async render => {
1403 + let changeCount = 0;
1404 + const e = await render(
1405 + <ControlledInput onChange={() => changeCount++} />,
1406 + );
1407 + expect(changeCount).toBe(0);
1408 + expect(e.value).toBe('Hello');
1409 +
1410 + // simulate a user typing.
1411 + e.value = 'Goodbye';
1412 + ReactTestUtils.Simulate.change(e);
1413 +
1414 + expect(changeCount).toBe(1);
1415 + expect(e.value).toBe('Goodbye');
1416 + });
1417 +
1418 + itClientRenders('a controlled textarea', async render => {
1419 + let changeCount = 0;
1420 + const e = await render(
1421 + <ControlledTextArea onChange={() => changeCount++} />,
1422 + );
1423 + expect(changeCount).toBe(0);
1424 + expect(e.value).toBe('Hello');
1425 +
1426 + // simulate a user typing.
1427 + e.value = 'Goodbye';
1428 + ReactTestUtils.Simulate.change(e);
1429 +
1430 + expect(changeCount).toBe(1);
1431 + expect(e.value).toBe('Goodbye');
1432 + });
1433 +
1434 + itClientRenders('a controlled checkbox', async render => {
1435 + let changeCount = 0;
1436 + const e = await render(
1437 + <ControlledCheckbox onChange={() => changeCount++} />,
1438 + );
1439 + expect(changeCount).toBe(0);
1440 + expect(e.checked).toBe(true);
1441 +
1442 + // simulate a user typing.
1443 + e.checked = false;
1444 + ReactTestUtils.Simulate.change(e);
1445 +
1446 + expect(changeCount).toBe(1);
1447 + expect(e.checked).toBe(false);
1448 + });
1449 +
1450 + itClientRenders('a controlled select', async render => {
1451 + let changeCount = 0;
1452 + const e = await render(
1453 + <ControlledSelect onChange={() => changeCount++} />,
1454 + );
1455 + expect(changeCount).toBe(0);
1456 + expect(e.value).toBe('Hello');
1457 +
1458 + // simulate a user typing.
1459 + e.value = 'Goodbye';
1460 + ReactTestUtils.Simulate.change(e);
1461 +
1462 + expect(changeCount).toBe(1);
1463 + expect(e.value).toBe('Goodbye');
1464 + });
1465 + });
1466 +
1467 + describe('user interaction with inputs before client render', function() {
1468 + // renders the element and changes the value **before** the client
1469 + // code has a chance to render; this simulates what happens when a
1470 + // user starts to interact with a server-rendered form before
1471 + // ReactDOM.render is called. the client render should NOT blow away
1472 + // the changes the user has made.
1473 + const testUserInteractionBeforeClientRender = async (
1474 + element,
1475 + initialValue = 'Hello',
1476 + changedValue = 'Goodbye',
1477 + valueKey = 'value',
1478 + ) => {
1479 + const field = await serverRender(element);
1480 + expect(field[valueKey]).toBe(initialValue);
1481 +
1482 + // simulate a user typing in the field **before** client-side reconnect happens.
1483 + field[valueKey] = changedValue;
1484 +
1485 + resetModules();
1486 + // client render on top of the server markup.
1487 + const clientField = await renderIntoDom(element, field.parentNode);
1488 + // verify that the input field was not replaced.
1489 + // Note that we cannot use expect(clientField).toBe(field) because
1490 + // of jest bug #1772
1491 + expect(clientField === field).toBe(true);
1492 + // confirm that the client render has not changed what the user typed.
1493 + expect(clientField[valueKey]).toBe(changedValue);
1494 + };
1495 +
1496 + it('should not blow away user-entered text on successful reconnect to an uncontrolled input', () =>
1497 + testUserInteractionBeforeClientRender(
1498 + <input defaultValue="Hello" />,
1499 + ));
1500 +
1501 + it('should not blow away user-entered text on successful reconnect to a controlled input', async () => {
1502 + let changeCount = 0;
1503 + await testUserInteractionBeforeClientRender(
1504 + <ControlledInput onChange={() => changeCount++} />,
1505 + );
1506 + // note that there's a strong argument to be made that the DOM revival
1507 + // algorithm should notice that the user has changed the value and fire
1508 + // an onChange. however, it does not now, so that's what this tests.
1509 + expect(changeCount).toBe(0);
1510 + });
1511 +
1512 + it('should not blow away user-entered text on successful reconnect to an uncontrolled checkbox', () =>
1513 + testUserInteractionBeforeClientRender(
1514 + <input type="checkbox" defaultChecked={true} />,
1515 + true,
1516 + false,
1517 + 'checked',
1518 + ));
1519 +
1520 + it('should not blow away user-entered text on successful reconnect to a controlled checkbox', async () => {
1521 + let changeCount = 0;
1522 + await testUserInteractionBeforeClientRender(
1523 + <ControlledCheckbox onChange={() => changeCount++} />,
1524 + true,
1525 + false,
1526 + 'checked',
1527 + );
1528 + expect(changeCount).toBe(0);
1529 + });
1530 +
1531 + // skipping this test because React 15 does the wrong thing. it blows
1532 + // away the user's typing in the textarea.
1533 + xit(
1534 + 'should not blow away user-entered text on successful reconnect to an uncontrolled textarea',
1535 + () =>
1536 + testUserInteractionBeforeClientRender(
1537 + <textarea defaultValue="Hello" />,
1538 + ),
1539 + );
1540 +
1541 + // skipping this test because React 15 does the wrong thing. it blows
1542 + // away the user's typing in the textarea.
1543 + xit(
1544 + 'should not blow away user-entered text on successful reconnect to a controlled textarea',
1545 + async () => {
1546 + let changeCount = 0;
1547 + await testUserInteractionBeforeClientRender(
1548 + <ControlledTextArea onChange={() => changeCount++} />,
1549 + );
1550 + expect(changeCount).toBe(0);
1551 + },
1552 + );
1553 +
1554 + it('should not blow away user-selected value on successful reconnect to an uncontrolled select', () =>
1555 + testUserInteractionBeforeClientRender(
1556 + <select defaultValue="Hello">
1557 + <option key="1" value="Hello">Hello</option>
1558 + <option key="2" value="Goodbye">Goodbye</option>
1559 + </select>,
1560 + ));
1561 +
1562 + it('should not blow away user-selected value on successful reconnect to an controlled select', async () => {
1563 + let changeCount = 0;
1564 + await testUserInteractionBeforeClientRender(
1565 + <ControlledSelect onChange={() => changeCount++} />,
1566 + );
1567 + expect(changeCount).toBe(0);
1568 + });
1569 + });
1570 + });
1571 + });
1572 +
1573 describe('context', function() {
1574 let PurpleContext, RedContext;
1575 beforeEach(() => {
@@ -1442,20 +1990,6 @@ describe('ReactDOMServerIntegration', () => {
1990
1991 it('can distinguish an empty component from an empty text component', () =>
1992 expectMarkupMismatch(<div><EmptyComponent /></div>, <div>{''}</div>));
1445 -
1446 - it('should reconnect if component trees differ but resulting markup is the same', () => {
1447 - class Component1 extends React.Component {
1448 - render() {
1449 - return <span id="foobar" />;
1450 - }
1451 - }
1452 - class Component2 extends React.Component {
1453 - render() {
1454 - return <span id="foobar" />;
1455 - }
1456 - }
1457 - return expectMarkupMatch(<Component1 />, <Component2 />);
1458 - });
1993 });
1994
1995 // Markup Mismatches: misc