Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
517e197
Report DOM events to reportError directly instead of rethrowing
sebmarkbage Mar 12, 2024
bec21b2
Never rethrow at the root
sebmarkbage Mar 12, 2024
742aba4
Log fatal errors as they happen
sebmarkbage Mar 22, 2024
54471d4
Report root errors to the browser so they show up as "uncaught"
sebmarkbage Mar 12, 2024
bc5374d
Polyfill dispatching error event
sebmarkbage Mar 13, 2024
c6dd65e
Remove rethrowing in the commit phase
sebmarkbage Mar 13, 2024
0d7615d
Rethrow global errors that happened during an internal act
sebmarkbage Mar 22, 2024
ef549d4
Rethrow uncaught errors from act instead of logging them
sebmarkbage Mar 22, 2024
9f8a43a
Aggregate errors in internal act
sebmarkbage Mar 22, 2024
8fe758b
Aggregate errors in act
sebmarkbage Mar 22, 2024
254af8d
Use shared queue and only track errors once for internalAct/waitFor
sebmarkbage Mar 23, 2024
b4de7d2
Test error logging recovery without act
sebmarkbage Mar 24, 2024
06e4464
Fix tests that failed due to internalAct now rethrowing non-render er…
sebmarkbage Mar 22, 2024
785c32a
Fix tests
sebmarkbage Mar 22, 2024
e32089f
Fix tests that rely on flushSync to throw
sebmarkbage Mar 22, 2024
175484e
Use internal act for prod testing
sebmarkbage Mar 25, 2024
7344587
Build lint process for the reportGlobalError polyfill
sebmarkbage Mar 25, 2024
613ae34
Fix test
sebmarkbage Mar 27, 2024
d3f0b57
Fix legacy tests
rickhanlonii Mar 26, 2024
c06e47d
Fix legacy tests in ReactDOM-test.js
rickhanlonii Mar 26, 2024
45fb81e
Add back React.Children.only
rickhanlonii Mar 26, 2024
8e3c0ae
Fix useSyncExternalStoreShared-test.js
rickhanlonii Mar 26, 2024
7a07e98
Fix ReactFresh-test.js
rickhanlonii Mar 26, 2024
0928d91
Update error messages
sebmarkbage Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix tests that rely on flushSync to throw
  • Loading branch information
sebmarkbage committed Mar 27, 2024
commit e32089f96a5ba659f941126f21b835a3ee4cc59d
58 changes: 29 additions & 29 deletions packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ describe('ReactCompositeComponent', () => {

const el = document.createElement('div');
const root = ReactDOMClient.createRoot(el);
expect(() => {
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await expect(async () => {
await act(() => {
root.render(<Child test="test" />);
});
}).toThrow(
}).rejects.toThrow(
'Objects are not valid as a React child (found: object with keys {render}).',
);
}).toErrorDev(
Expand Down Expand Up @@ -526,24 +526,24 @@ describe('ReactCompositeComponent', () => {
}
}
const root = ReactDOMClient.createRoot(container);
expect(() => {
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await expect(async () => {
await act(() => {
root.render(<ClassWithRenderNotExtended />);
});
}).toThrow(TypeError);
}).rejects.toThrow(TypeError);
}).toErrorDev(
'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
"but doesn't extend React.Component. This is likely to cause errors. " +
'Change ClassWithRenderNotExtended to extend React.Component instead.',
);

// Test deduplication
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<ClassWithRenderNotExtended />);
});
}).toThrow(TypeError);
}).rejects.toThrow(TypeError);
});

it('should warn about `setState` in render', async () => {
Expand Down Expand Up @@ -596,11 +596,11 @@ describe('ReactCompositeComponent', () => {
expect(ReactCurrentOwner.current).toBe(null);

const root = ReactDOMClient.createRoot(document.createElement('div'));
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(instance);
});
}).toThrow();
}).rejects.toThrow();

expect(ReactCurrentOwner.current).toBe(null);
});
Expand Down Expand Up @@ -884,7 +884,7 @@ describe('ReactCompositeComponent', () => {
);
});

it('should only call componentWillUnmount once', () => {
it('should only call componentWillUnmount once', async () => {
let app;
let count = 0;

Expand Down Expand Up @@ -919,14 +919,14 @@ describe('ReactCompositeComponent', () => {
};

const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<App ref={setRef} stage={1} />);
});
ReactDOM.flushSync(() => {
await act(() => {
root.render(<App ref={setRef} stage={2} />);
});
}).toThrow();
}).rejects.toThrow();
expect(count).toBe(1);
});

Expand Down Expand Up @@ -1211,7 +1211,7 @@ describe('ReactCompositeComponent', () => {
assertLog(['setState callback called']);
});

it('should return a meaningful warning when constructor is returned', () => {
it('should return a meaningful warning when constructor is returned', async () => {
class RenderTextInvalidConstructor extends React.Component {
constructor(props) {
super(props);
Expand All @@ -1224,12 +1224,12 @@ describe('ReactCompositeComponent', () => {
}

const root = ReactDOMClient.createRoot(document.createElement('div'));
expect(() => {
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await expect(async () => {
await act(() => {
root.render(<RenderTextInvalidConstructor />);
});
}).toThrow();
}).rejects.toThrow();
}).toErrorDev([
'Warning: No `render` method found on the RenderTextInvalidConstructor instance: ' +
'did you accidentally return an object from the constructor?',
Expand Down Expand Up @@ -1260,16 +1260,16 @@ describe('ReactCompositeComponent', () => {
);
});

it('should return error if render is not defined', () => {
it('should return error if render is not defined', async () => {
class RenderTestUndefinedRender extends React.Component {}

const root = ReactDOMClient.createRoot(document.createElement('div'));
expect(() => {
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await expect(async () => {
await act(() => {
root.render(<RenderTestUndefinedRender />);
});
}).toThrow();
}).rejects.toThrow();
}).toErrorDev([
'Warning: No `render` method found on the RenderTestUndefinedRender instance: ' +
'you may have forgotten to define `render`.',
Expand Down
10 changes: 6 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMFiber-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1108,11 +1108,13 @@ describe('ReactDOMFiber', () => {
// It's an error of type 'NotFoundError' with no message
container.innerHTML = '<div>MEOW.</div>';

expect(() => {
ReactDOM.flushSync(() => {
root.render(<div key="2">baz</div>);
await expect(async () => {
await act(() => {
ReactDOM.flushSync(() => {
root.render(<div key="2">baz</div>);
});
});
}).toThrow('The node to be removed is not a child of this node');
}).rejects.toThrow('The node to be removed is not a child of this node');
});

it('should not warn when doing an update to a container manually updated outside of React', async () => {
Expand Down
78 changes: 40 additions & 38 deletions packages/react-dom/src/__tests__/ReactUpdates-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1542,11 +1542,11 @@ describe('ReactUpdates', () => {

let limit = 55;
const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<EventuallyTerminating ref={ref} />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');

// Verify that we don't go over the limit if these updates are unrelated.
limit -= 10;
Expand All @@ -1566,15 +1566,15 @@ describe('ReactUpdates', () => {
expect(container.textContent).toBe(limit.toString());

limit += 10;
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
ref.current.setState({step: 0});
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
expect(ref.current).toBe(null);
});

it('does not fall into an infinite update loop', () => {
it('does not fall into an infinite update loop', async () => {
class NonTerminating extends React.Component {
state = {step: 0};

Expand All @@ -1599,14 +1599,14 @@ describe('ReactUpdates', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);

expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<NonTerminating />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
});

it('does not fall into an infinite update loop with useLayoutEffect', () => {
it('does not fall into an infinite update loop with useLayoutEffect', async () => {
function NonTerminating() {
const [step, setStep] = React.useState(0);
React.useLayoutEffect(() => {
Expand All @@ -1617,11 +1617,11 @@ describe('ReactUpdates', () => {

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<NonTerminating />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
});

it('can recover after falling into an infinite update loop', async () => {
Expand Down Expand Up @@ -1650,29 +1650,29 @@ describe('ReactUpdates', () => {

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<NonTerminating />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');

await act(() => {
root.render(<Terminating />);
});
expect(container.textContent).toBe('1');

expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<NonTerminating />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
await act(() => {
root.render(<Terminating />);
});
expect(container.textContent).toBe('1');
});

it('does not fall into mutually recursive infinite update loop with same container', () => {
it('does not fall into mutually recursive infinite update loop with same container', async () => {
// Note: this test would fail if there were two or more different roots.
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
Expand All @@ -1694,14 +1694,14 @@ describe('ReactUpdates', () => {
}
}

expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<A />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
});

it('does not fall into an infinite error loop', () => {
it('does not fall into an infinite error loop', async () => {
function BadRender() {
throw new Error('error');
}
Expand Down Expand Up @@ -1730,11 +1730,11 @@ describe('ReactUpdates', () => {

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
await expect(async () => {
await act(() => {
root.render(<NonTerminating />);
});
}).toThrow('Maximum');
}).rejects.toThrow('Maximum');
});

it('can schedule ridiculously many updates within the same batch without triggering a maximum update error', async () => {
Expand Down Expand Up @@ -1775,7 +1775,7 @@ describe('ReactUpdates', () => {
expect(subscribers.length).toBe(limit);
});

it("does not infinite loop if there's a synchronous render phase update on another component", () => {
it("does not infinite loop if there's a synchronous render phase update on another component", async () => {
if (gate(flags => !flags.enableInfiniteRenderLoopDetection)) {
return;
}
Expand All @@ -1795,10 +1795,10 @@ describe('ReactUpdates', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);

expect(() => {
expect(() => ReactDOM.flushSync(() => root.render(<App />))).toThrow(
'Maximum update depth exceeded',
);
await expect(async () => {
await expect(async () => {
await act(() => ReactDOM.flushSync(() => root.render(<App />)));
}).rejects.toThrow('Maximum update depth exceeded');
}).toErrorDev(
'Warning: Cannot update a component (`App`) while rendering a different component (`Child`)',
);
Expand Down Expand Up @@ -1926,7 +1926,7 @@ describe('ReactUpdates', () => {
});
}

it('prevents infinite update loop triggered by synchronous updates in useEffect', () => {
it('prevents infinite update loop triggered by synchronous updates in useEffect', async () => {
// Ignore flushSync warning
spyOnDev(console, 'error').mockImplementation(() => {});

Expand All @@ -1950,10 +1950,12 @@ describe('ReactUpdates', () => {

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
expect(() => {
ReactDOM.flushSync(() => {
root.render(<NonTerminating />);
await expect(async () => {
await act(() => {
ReactDOM.flushSync(() => {
root.render(<NonTerminating />);
});
});
}).toThrow('Maximum update depth exceeded');
}).rejects.toThrow('Maximum update depth exceeded');
});
});
10 changes: 6 additions & 4 deletions packages/react-reconciler/src/__tests__/ReactFlushSync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,12 @@ describe('ReactFlushSync', () => {

let error;
try {
ReactDOM.flushSync(() => {
root1.render(<Throws error={aahh} />);
root2.render(<Throws error={nooo} />);
root3.render(<Text text="aww" />);
await act(() => {
ReactDOM.flushSync(() => {
root1.render(<Throws error={aahh} />);
root2.render(<Throws error={nooo} />);
root3.render(<Text text="aww" />);
});
});
} catch (e) {
error = e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,12 @@ describe('ReactFlushSync (AggregateError not available)', () => {
overrideQueueMicrotask = true;
let error;
try {
ReactDOM.flushSync(() => {
root1.render(<Throws error={aahh} />);
root2.render(<Throws error={nooo} />);
root3.render(<Text text="aww" />);
await act(() => {
ReactDOM.flushSync(() => {
root1.render(<Throws error={aahh} />);
root2.render(<Throws error={nooo} />);
root3.render(<Text text="aww" />);
});
});
} catch (e) {
error = e;
Expand Down
Loading