reactTestingLibrary.tsx 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import {Component, Fragment} from 'react';
  2. import {cache} from '@emotion/css'; // eslint-disable-line @emotion/no-vanilla
  3. import {CacheProvider, ThemeProvider} from '@emotion/react';
  4. import * as rtl from '@testing-library/react'; // eslint-disable-line no-restricted-imports
  5. import * as reactHooks from '@testing-library/react-hooks'; // eslint-disable-line no-restricted-imports
  6. import userEvent from '@testing-library/user-event'; // eslint-disable-line no-restricted-imports
  7. import GlobalModal from 'sentry/components/globalModal';
  8. import {Organization} from 'sentry/types';
  9. import {lightTheme} from 'sentry/utils/theme';
  10. import {OrganizationContext} from 'sentry/views/organizationContext';
  11. import {instrumentUserEvent} from '../instrumentedEnv/userEventIntegration';
  12. type ProviderOptions = {
  13. context?: Record<string, any>;
  14. organization?: Organization;
  15. };
  16. type Options = ProviderOptions & rtl.RenderOptions;
  17. function createProvider(contextDefs: Record<string, any>) {
  18. return class ContextProvider extends Component {
  19. static childContextTypes = contextDefs.childContextTypes;
  20. getChildContext() {
  21. return contextDefs.context;
  22. }
  23. render() {
  24. return this.props.children;
  25. }
  26. };
  27. }
  28. function makeAllTheProviders({context, organization}: ProviderOptions) {
  29. const ContextProvider = context ? createProvider(context) : Fragment;
  30. return function ({children}: {children?: React.ReactNode}) {
  31. return (
  32. <ContextProvider>
  33. <CacheProvider value={cache}>
  34. <ThemeProvider theme={lightTheme}>
  35. <OrganizationContext.Provider value={organization ?? null}>
  36. {children}
  37. </OrganizationContext.Provider>
  38. </ThemeProvider>
  39. </CacheProvider>
  40. </ContextProvider>
  41. );
  42. };
  43. }
  44. /**
  45. * Migrating from enzyme?
  46. * Try avoiding unnecessary context and just mount your component. If it works, then you dont need anything else.
  47. * render(<TestedComponent />);
  48. *
  49. * If your component requires routerContext or organization to render, pass it via context options argument.
  50. * render(<TestedComponent />, {context: routerContext, organization});
  51. */
  52. function render(ui: React.ReactElement, options?: Options) {
  53. const {context, organization, ...otherOptions} = options ?? {};
  54. const AllTheProviders = makeAllTheProviders({context, organization});
  55. return rtl.render(ui, {wrapper: AllTheProviders, ...otherOptions});
  56. }
  57. /**
  58. * @deprecated
  59. * Use userEvent over fireEvent where possible.
  60. * More details: https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-testing-libraryuser-event
  61. */
  62. const fireEvent = rtl.fireEvent;
  63. function renderGlobalModal(options?: Options) {
  64. return render(<GlobalModal />, options);
  65. }
  66. /**
  67. * jest-sentry-environment attaches a global Sentry object that can be used.
  68. * The types on it conflicts with the existing window.Sentry object so it's using any here.
  69. */
  70. const globalSentry = (global as any).Sentry;
  71. /**
  72. * This cannot be implemented as a Sentry Integration because Jest creates an
  73. * isolated environment for each test suite. This means that if we were to apply
  74. * the monkey patching ahead of time, it would be shadowed by Jest.
  75. */
  76. instrumentUserEvent(globalSentry?.getCurrentHub.bind(globalSentry));
  77. // eslint-disable-next-line no-restricted-imports, import/export
  78. export * from '@testing-library/react';
  79. // eslint-disable-next-line import/export
  80. export {render, renderGlobalModal, userEvent, reactHooks, fireEvent};