Browse Source

test(js): Introduce enforceActOnUseLegacyStoreHook for RTL migration (#30039)

Evan Purkhiser 3 years ago
parent
commit
49d8337fa7

+ 12 - 1
static/app/stores/useLegacyStore.tsx

@@ -5,6 +5,17 @@ import {CommonStoreInterface} from './types';
 
 
 type LegacyStoreShape = Reflux.Store & CommonStoreInterface<any>;
 type LegacyStoreShape = Reflux.Store & CommonStoreInterface<any>;
 
 
+/**
+ * This wrapper exists because we have many old-style enzyme tests that trigger
+ * updates to stores without being wrapped in act.
+ *
+ * Wrting tests with React Testing Library typically circumvents the need for
+ * this. See [0].
+ *
+ * [0]: https://javascript.plainenglish.io/you-probably-dont-need-act-in-your-react-tests-2a0bcd2ad65c
+ */
+window._legacyStoreHookUpdate = update => update();
+
 /**
 /**
  * Returns the state of a reflux store. Automatically unsubscribes when destroyed
  * Returns the state of a reflux store. Automatically unsubscribes when destroyed
  *
  *
@@ -18,7 +29,7 @@ export function useLegacyStore<T extends LegacyStoreShape>(
   const [state, setState] = useState(store.getState());
   const [state, setState] = useState(store.getState());
 
 
   // Not all stores emit the new state, call get on change
   // Not all stores emit the new state, call get on change
-  const callback = () => setState(store.getState());
+  const callback = () => window._legacyStoreHookUpdate(() => setState(store.getState()));
 
 
   useEffect(() => store.listen(callback, undefined) as () => void, []);
   useEffect(() => store.listen(callback, undefined) as () => void, []);
 
 

+ 7 - 0
static/app/types/index.tsx

@@ -104,6 +104,13 @@ declare global {
      * See sentry/js/ads.js for how this global is disabled.
      * See sentry/js/ads.js for how this global is disabled.
      */
      */
     adblockSuspected?: boolean;
     adblockSuspected?: boolean;
+    /**
+     * This is used for testing purposes as an interem while we translate tests
+     * to React Testing Library.
+     *
+     * See the useLegacyStore hook for more unformation about this.
+     */
+    _legacyStoreHookUpdate: (update: () => void) => void;
 
 
     // typing currently used for demo add on
     // typing currently used for demo add on
     // TODO: improve typing
     // TODO: improve typing

+ 25 - 4
tests/js/sentry-test/enzyme.js

@@ -2,6 +2,8 @@ import {cache} from '@emotion/css'; // eslint-disable-line emotion/no-vanilla
 import {CacheProvider, ThemeProvider} from '@emotion/react';
 import {CacheProvider, ThemeProvider} from '@emotion/react';
 import {mount, shallow as enzymeShallow} from 'enzyme'; // eslint-disable-line no-restricted-imports
 import {mount, shallow as enzymeShallow} from 'enzyme'; // eslint-disable-line no-restricted-imports
 
 
+import {act} from 'sentry-test/reactTestingLibrary';
+
 import {lightTheme} from 'app/utils/theme';
 import {lightTheme} from 'app/utils/theme';
 
 
 /**
 /**
@@ -9,7 +11,7 @@ import {lightTheme} from 'app/utils/theme';
  * As we are migrating our tests to React Testing Library,
  * As we are migrating our tests to React Testing Library,
  * please avoid using `sentry-test/enzyme/mountWithTheme` and use `sentry-test/reactTestingLibrary/mountWithTheme` instead.
  * please avoid using `sentry-test/enzyme/mountWithTheme` and use `sentry-test/reactTestingLibrary/mountWithTheme` instead.
  */
  */
-const mountWithTheme = (tree, opts) => {
+export function mountWithTheme(tree, opts) {
   const WrappingThemeProvider = props => (
   const WrappingThemeProvider = props => (
     <CacheProvider value={cache}>
     <CacheProvider value={cache}>
       <ThemeProvider theme={lightTheme}>{props.children}</ThemeProvider>
       <ThemeProvider theme={lightTheme}>{props.children}</ThemeProvider>
@@ -17,13 +19,32 @@ const mountWithTheme = (tree, opts) => {
   );
   );
 
 
   return mount(tree, {wrappingComponent: WrappingThemeProvider, ...opts});
   return mount(tree, {wrappingComponent: WrappingThemeProvider, ...opts});
-};
+}
 
 
 /**
 /**
  * @deprecated
  * @deprecated
  * As we are migrating our tests to React Testing Library,
  * As we are migrating our tests to React Testing Library,
  * please avoid using `sentry-test/enzyme/shallow` and use `sentry-test/reactTestingLibrary/mountWithTheme` instead.
  * please avoid using `sentry-test/enzyme/shallow` and use `sentry-test/reactTestingLibrary/mountWithTheme` instead.
  */
  */
-const shallow = enzymeShallow;
+export const shallow = enzymeShallow;
+
+/**
+ * Force the useLegacyStore setState updates to be wrapped in act.
+ *
+ * This is useful for old-style enzyme tests where enzyme does not correctly
+ * wrap things in `act()` for you.
+ *
+ * Do NOT use this in RTL tests, as setState's triggered by store updates
+ * should be captured with RTL style tests.
+ */
+export function enforceActOnUseLegacyStoreHook() {
+  const originalHook = window._legacyStoreHookUpdate;
+
+  beforeEach(() => {
+    window._legacyStoreHookUpdate = update => act(update);
+  });
 
 
-export {mountWithTheme, shallow};
+  afterEach(() => {
+    window._legacyStoreHookUpdate = originalHook;
+  });
+}