Repository: facebook/react
File: packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js (JavaScript)

Code before:
      await expect(async () => {
        await act(() => {
          ReactDOM.flushSync(async () =>
            root.render(React.createElement(App, null)),
          );
        });
      }).rejects.toThrow(
        'Maximum update depth exceeded. This can happen when a component repeatedly ' +
          'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' +
          'the number of nested updates to prevent infinite loops.',
      );
    }).toErrorDev(
      gate(flags => flags.enableUseSyncExternalStoreShim)
        ? [
            'Maximum update depth exceeded. ',
            'The result of getSnapshot should be cached to avoid an infinite loop',
            'The above error occurred in the',
          ]
        : [
            'The result of getSnapshot should be cached to avoid an infinite loop',
          ],
      {
        withoutStack: gate(flags => {
          if (flags.enableUseSyncExternalStoreShim) {
            // `console.error` in this package should not be transformed, so we'll never see stack traces
            return true;
          }
          return false;
        }),
      },
    );
  });
  it('getSnapshot can return NaN without infinite loop warning', async () => {
    const store = createExternalStore('not a number');
    function App() {
      const value = useSyncExternalStore(store.subscribe, () =>
        parseInt(store.getState(), 10),
      );
      return React.createElement(Text, {
        text: value,
      });
    }
    const container = document.createElement('div');
    const root = createRoot(container);

    // Initial render that reads a snapshot of NaN. This is OK because we use
    // Object.is algorithm to compare values.
    await act(() => root.render(React.createElement(App, null)));
    expect(container.textContent).toEqual('NaN');
    assertLog([NaN]);


Review comment (bug):
The warning already had a test here, so I believe it's not necessary to add one more.

I'm a bit stumped that the existing test didn't crash with React 17 and new builds, though. I believe it should have been running with the npm artefact, too?

Code after:
        'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' +
        'the number of nested updates to prevent infinite loops.',
    );

    assertConsoleErrorDev(
      gate(flags => flags.enableUseSyncExternalStoreShim)
        ? [
            [
              'The result of getSnapshot should be cached to avoid an infinite loop',
              {withoutStack: true},
            ],
            'Error: Maximum update depth exceeded',
            'The above error occurred i',
          ]
        : [
            'The result of getSnapshot should be cached to avoid an infinite loop',
          ],
    );
  });
  it('getSnapshot can return NaN without infinite loop warning', async () => {
    const store = createExternalStore('not a number');
    function App() {
      const value = useSyncExternalStore(store.subscribe, () =>
        parseInt(store.getState(), 10),
      );
      return React.createElement(Text, {
        text: value,
      });
    }
    const container = document.createElement('div');
    const root = createRoot(container);

    // Initial render that reads a snapshot of NaN. This is OK because we use
    // Object.is algorithm to compare values.
    await act(() => root.render(React.createElement(App, null)));
    expect(container.textContent).toEqual('NaN');
    assertLog([NaN]);

    // Update to real number
    await act(() => store.set(123));
    expect(container.textContent).toEqual('123');
    assertLog([123]);

    // Update back to NaN
    await act(() => store.set('not a number'));
    expect(container.textContent).toEqual('NaN');
    assertLog([NaN]);
  });
  describe('extra features implemented in user-space', () => {
    it('memoized selectors are only called once per update', async () => {
      const store = createExternalStore({