From 147bdef11bbeb8f4aef18c56d53ec1591fce8653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Wed, 8 Apr 2020 20:54:54 -0700 Subject: [PATCH] Port more tests to the Scheduler.unstable_yieldValue pattern and drop internal.js (#18549) * Drop the .internal.js suffix on some files that don't need it anymore * Port some ops patterns to scheduler yield * Fix triangle test to avoid side-effects in constructor * Move replaying of setState updaters until after the effect Otherwise any warnings get silenced if they're deduped. * Drop .internal.js in more files * Don't check propTypes on a simple memo component unless it's lazy Comparing the elementType doesn't work for this because it will never be the same for a simple element. This caused us to double validate these. This was covered up because in internal tests this was deduped since they shared the prop types cache but since we now inline it, it doesn't get deduped. --- ...internal.js => createSubscription-test.js} | 0 ...internal.js => ReactDOMFiberAsync-test.js} | 0 ....internal.js => ChangeEventPlugin-test.js} | 76 +- ....internal.js => SimpleEventPlugin-test.js} | 35 +- .../src/dom/__tests__/Input-test.internal.js | 47 +- .../MixedResponders-test-internal.js | 28 +- .../src/ReactFiberBeginWork.js | 20 +- .../react-reconciler/src/ReactUpdateQueue.js | 12 +- ...st.internal.js => ReactExpiration-test.js} | 0 ...t.internal.js => ReactIncremental-test.js} | 724 ++++++++---------- ...tIncrementalErrorHandling-test.internal.js | 146 ++-- ....js => ReactIncrementalReflection-test.js} | 0 ....js => ReactIncrementalScheduling-test.js} | 24 +- ...js => ReactIncrementalSideEffects-test.js} | 0 ...al.js => ReactIncrementalTriangle-test.js} | 18 +- ...nal.js => ReactIncrementalUpdates-test.js} | 77 +- ...emo-test.internal.js => ReactMemo-test.js} | 0 ...st.internal.js => ReactNewContext-test.js} | 2 +- ...l.js => ReactSchedulerIntegration-test.js} | 0 ...rnal.js => ReactTestRendererAsync-test.js} | 0 ...st.internal.js => useSubscription-test.js} | 0 21 files changed, 494 insertions(+), 715 deletions(-) rename packages/create-subscription/src/__tests__/{createSubscription-test.internal.js => createSubscription-test.js} (100%) rename packages/react-dom/src/__tests__/{ReactDOMFiberAsync-test.internal.js => ReactDOMFiberAsync-test.js} (100%) rename packages/react-dom/src/events/__tests__/{ChangeEventPlugin-test.internal.js => ChangeEventPlugin-test.js} (93%) rename packages/react-dom/src/events/__tests__/{SimpleEventPlugin-test.internal.js => SimpleEventPlugin-test.js} (95%) rename packages/react-reconciler/src/__tests__/{ReactExpiration-test.internal.js => ReactExpiration-test.js} (100%) rename packages/react-reconciler/src/__tests__/{ReactIncremental-test.internal.js => ReactIncremental-test.js} (83%) rename packages/react-reconciler/src/__tests__/{ReactIncrementalReflection-test.internal.js => ReactIncrementalReflection-test.js} (100%) rename packages/react-reconciler/src/__tests__/{ReactIncrementalScheduling-test.internal.js => ReactIncrementalScheduling-test.js} (96%) rename packages/react-reconciler/src/__tests__/{ReactIncrementalSideEffects-test.internal.js => ReactIncrementalSideEffects-test.js} (100%) rename packages/react-reconciler/src/__tests__/{ReactIncrementalTriangle-test.internal.js => ReactIncrementalTriangle-test.js} (98%) rename packages/react-reconciler/src/__tests__/{ReactIncrementalUpdates-test.internal.js => ReactIncrementalUpdates-test.js} (94%) rename packages/react-reconciler/src/__tests__/{ReactMemo-test.internal.js => ReactMemo-test.js} (100%) rename packages/react-reconciler/src/__tests__/{ReactNewContext-test.internal.js => ReactNewContext-test.js} (99%) rename packages/react-reconciler/src/__tests__/{ReactSchedulerIntegration-test.internal.js => ReactSchedulerIntegration-test.js} (100%) rename packages/react-test-renderer/src/__tests__/{ReactTestRendererAsync-test.internal.js => ReactTestRendererAsync-test.js} (100%) rename packages/use-subscription/src/__tests__/{useSubscription-test.internal.js => useSubscription-test.js} (100%) diff --git a/packages/create-subscription/src/__tests__/createSubscription-test.internal.js b/packages/create-subscription/src/__tests__/createSubscription-test.js similarity index 100% rename from packages/create-subscription/src/__tests__/createSubscription-test.internal.js rename to packages/create-subscription/src/__tests__/createSubscription-test.js diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js similarity index 100% rename from packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js rename to packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js diff --git a/packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.internal.js b/packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.js similarity index 93% rename from packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.internal.js rename to packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.js index 8357dbc633..8be8e5f18f 100644 --- a/packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.internal.js +++ b/packages/react-dom/src/events/__tests__/ChangeEventPlugin-test.js @@ -470,7 +470,7 @@ describe('ChangeEventPlugin', () => { beforeEach(() => { jest.resetModules(); ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false; + React = require('react'); ReactDOM = require('react-dom'); TestUtils = require('react-dom/test-utils'); @@ -481,13 +481,11 @@ describe('ChangeEventPlugin', () => { const root = ReactDOM.createRoot(container); let input; - let ops = []; - class ControlledInput extends React.Component { state = {value: 'initial'}; onChange = event => this.setState({value: event.target.value}); render() { - ops.push(`render: ${this.state.value}`); + Scheduler.unstable_yieldValue(`render: ${this.state.value}`); const controlledValue = this.state.value === 'changed' ? 'changed [!]' : this.state.value; return ( @@ -504,22 +502,19 @@ describe('ChangeEventPlugin', () => { // Initial mount. Test that this is async. root.render(); // Should not have flushed yet. - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(input).toBe(undefined); // Flush callbacks. - Scheduler.unstable_flushAll(); - expect(ops).toEqual(['render: initial']); + expect(Scheduler).toFlushAndYield(['render: initial']); expect(input.value).toBe('initial'); - ops = []; - // Trigger a change event. setUntrackedValue.call(input, 'changed'); input.dispatchEvent( new Event('input', {bubbles: true, cancelable: true}), ); // Change should synchronously flush - expect(ops).toEqual(['render: changed']); + expect(Scheduler).toHaveYielded(['render: changed']); // Value should be the controlled value, not the original one expect(input.value).toBe('changed [!]'); }); @@ -528,15 +523,13 @@ describe('ChangeEventPlugin', () => { const root = ReactDOM.createRoot(container); let input; - let ops = []; - class ControlledInput extends React.Component { state = {checked: false}; onChange = event => { this.setState({checked: event.target.checked}); }; render() { - ops.push(`render: ${this.state.checked}`); + Scheduler.unstable_yieldValue(`render: ${this.state.checked}`); const controlledValue = this.props.reverse ? !this.state.checked : this.state.checked; @@ -554,35 +547,30 @@ describe('ChangeEventPlugin', () => { // Initial mount. Test that this is async. root.render(); // Should not have flushed yet. - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(input).toBe(undefined); // Flush callbacks. - Scheduler.unstable_flushAll(); - expect(ops).toEqual(['render: false']); + expect(Scheduler).toFlushAndYield(['render: false']); expect(input.checked).toBe(false); - ops = []; - // Trigger a change event. input.dispatchEvent( new MouseEvent('click', {bubbles: true, cancelable: true}), ); // Change should synchronously flush - expect(ops).toEqual(['render: true']); + expect(Scheduler).toHaveYielded(['render: true']); expect(input.checked).toBe(true); // Now let's make sure we're using the controlled value. root.render(); - Scheduler.unstable_flushAll(); - - ops = []; + expect(Scheduler).toFlushAndYield(['render: true']); // Trigger another change event. input.dispatchEvent( new MouseEvent('click', {bubbles: true, cancelable: true}), ); // Change should synchronously flush - expect(ops).toEqual(['render: true']); + expect(Scheduler).toHaveYielded(['render: true']); expect(input.checked).toBe(false); }); @@ -590,13 +578,11 @@ describe('ChangeEventPlugin', () => { const root = ReactDOM.createRoot(container); let textarea; - let ops = []; - class ControlledTextarea extends React.Component { state = {value: 'initial'}; onChange = event => this.setState({value: event.target.value}); render() { - ops.push(`render: ${this.state.value}`); + Scheduler.unstable_yieldValue(`render: ${this.state.value}`); const controlledValue = this.state.value === 'changed' ? 'changed [!]' : this.state.value; return ( @@ -613,22 +599,19 @@ describe('ChangeEventPlugin', () => { // Initial mount. Test that this is async. root.render(); // Should not have flushed yet. - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(textarea).toBe(undefined); // Flush callbacks. - Scheduler.unstable_flushAll(); - expect(ops).toEqual(['render: initial']); + expect(Scheduler).toFlushAndYield(['render: initial']); expect(textarea.value).toBe('initial'); - ops = []; - // Trigger a change event. setUntrackedTextareaValue.call(textarea, 'changed'); textarea.dispatchEvent( new Event('input', {bubbles: true, cancelable: true}), ); // Change should synchronously flush - expect(ops).toEqual(['render: changed']); + expect(Scheduler).toHaveYielded(['render: changed']); // Value should be the controlled value, not the original one expect(textarea.value).toBe('changed [!]'); }); @@ -637,13 +620,11 @@ describe('ChangeEventPlugin', () => { const root = ReactDOM.createRoot(container); let input; - let ops = []; - class ControlledInput extends React.Component { state = {value: 'initial'}; onChange = event => this.setState({value: event.target.value}); render() { - ops.push(`render: ${this.state.value}`); + Scheduler.unstable_yieldValue(`render: ${this.state.value}`); const controlledValue = this.state.value === 'changed' ? 'changed [!]' : this.state.value; return ( @@ -664,22 +645,19 @@ describe('ChangeEventPlugin', () => { // Initial mount. Test that this is async. root.render(); // Should not have flushed yet. - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(input).toBe(undefined); // Flush callbacks. - Scheduler.unstable_flushAll(); - expect(ops).toEqual(['render: initial']); + expect(Scheduler).toFlushAndYield(['render: initial']); expect(input.value).toBe('initial'); - ops = []; - // Trigger a change event. setUntrackedValue.call(input, 'changed'); input.dispatchEvent( new Event('input', {bubbles: true, cancelable: true}), ); // Change should synchronously flush - expect(ops).toEqual(['render: changed']); + expect(Scheduler).toHaveYielded(['render: changed']); // Value should be the controlled value, not the original one expect(input.value).toBe('changed [!]'); }); @@ -688,8 +666,6 @@ describe('ChangeEventPlugin', () => { const root = ReactDOM.createRoot(container); let input; - let ops = []; - class ControlledInput extends React.Component { state = {value: 'initial'}; onChange = event => this.setState({value: event.target.value}); @@ -697,7 +673,7 @@ describe('ChangeEventPlugin', () => { this.setState({value: ''}); }; render() { - ops.push(`render: ${this.state.value}`); + Scheduler.unstable_yieldValue(`render: ${this.state.value}`); const controlledValue = this.state.value === 'changed' ? 'changed [!]' : this.state.value; return ( @@ -715,27 +691,23 @@ describe('ChangeEventPlugin', () => { // Initial mount. Test that this is async. root.render(); // Should not have flushed yet. - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(input).toBe(undefined); // Flush callbacks. - Scheduler.unstable_flushAll(); - expect(ops).toEqual(['render: initial']); + expect(Scheduler).toFlushAndYield(['render: initial']); expect(input.value).toBe('initial'); - ops = []; - // Trigger a click event input.dispatchEvent( new Event('click', {bubbles: true, cancelable: true}), ); // Nothing should have changed - expect(ops).toEqual([]); + expect(Scheduler).toHaveYielded([]); expect(input.value).toBe('initial'); // Flush callbacks. - Scheduler.unstable_flushAll(); // Now the click update has flushed. - expect(ops).toEqual(['render: ']); + expect(Scheduler).toFlushAndYield(['render: ']); expect(input.value).toBe(''); }); diff --git a/packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.internal.js b/packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.js similarity index 95% rename from packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.internal.js rename to packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.js index 0dd4952a3c..03502cdb5a 100644 --- a/packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.internal.js +++ b/packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.js @@ -12,7 +12,6 @@ describe('SimpleEventPlugin', function() { let React; let ReactDOM; - let ReactFeatureFlags; let Scheduler; let onClick; @@ -184,7 +183,6 @@ describe('SimpleEventPlugin', function() { container = document.createElement('div'); document.body.appendChild(container); - const ops = []; let button; class Button extends React.Component { state = {count: 0}; @@ -193,7 +191,7 @@ describe('SimpleEventPlugin', function() { count: state.count + 1, })); componentDidUpdate() { - ops.push(`didUpdate - Count: ${this.state.count}`); + Scheduler.unstable_yieldValue(`didUpdate - Count: ${this.state.count}`); } render() { return ( @@ -221,20 +219,19 @@ describe('SimpleEventPlugin', function() { ReactDOM.render(