lazyLoad.spec.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {render, screen} from 'sentry-test/reactTestingLibrary';
  2. import LazyLoad from 'sentry/components/lazyLoad';
  3. type TestProps = {
  4. testProp?: boolean;
  5. };
  6. function FooComponent({}: TestProps) {
  7. return <div>my foo component</div>;
  8. }
  9. function BarComponent({}: TestProps) {
  10. return <div>my bar component</div>;
  11. }
  12. type ResolvedComponent = {default: React.ComponentType<TestProps>};
  13. type GetComponent = () => Promise<ResolvedComponent>;
  14. describe('LazyLoad', function () {
  15. it('renders with a loading indicator when promise is not resolved yet', function () {
  16. const importTest = new Promise<ResolvedComponent>(() => {});
  17. const getComponent = () => importTest;
  18. render(<LazyLoad component={getComponent} />);
  19. // Should be loading
  20. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  21. });
  22. it('renders when given a promise of a "foo" component', async function () {
  23. let doResolve: (c: ResolvedComponent) => void;
  24. const importFoo = new Promise<ResolvedComponent>(resolve => {
  25. doResolve = resolve;
  26. });
  27. render(<LazyLoad component={() => importFoo} />);
  28. // Should be loading
  29. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  30. // resolve with foo
  31. doResolve!({default: FooComponent});
  32. expect(await screen.findByText('my foo component')).toBeInTheDocument();
  33. });
  34. it('renders with error message when promise is rejected', async function () {
  35. // eslint-disable-next-line no-console
  36. jest.spyOn(console, 'error').mockImplementation(jest.fn());
  37. const getComponent = jest.fn(
  38. () =>
  39. new Promise<ResolvedComponent>((_resolve, reject) =>
  40. reject(new Error('Could not load component'))
  41. )
  42. );
  43. try {
  44. render(<LazyLoad component={getComponent} />);
  45. } catch (err) {
  46. // ignore
  47. }
  48. expect(
  49. await screen.findByText('There was an error loading a component.')
  50. ).toBeInTheDocument();
  51. // eslint-disable-next-line no-console
  52. expect(console.error).toHaveBeenCalled();
  53. // @ts-expect-error
  54. // eslint-disable-next-line no-console
  55. console.error.mockRestore();
  56. });
  57. it('refetches only when component changes', async function () {
  58. let doResolve: (c: ResolvedComponent) => void;
  59. const importFoo = new Promise<ResolvedComponent>(resolve => {
  60. doResolve = resolve;
  61. });
  62. // First render Foo
  63. const {rerender} = render(<LazyLoad component={() => importFoo} />);
  64. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  65. // resolve with foo
  66. doResolve!({default: FooComponent});
  67. expect(await screen.findByText('my foo component')).toBeInTheDocument();
  68. // Re-render with Bar
  69. const importBar = new Promise<ResolvedComponent>(resolve => {
  70. doResolve = resolve;
  71. });
  72. rerender(<LazyLoad component={() => importBar} />);
  73. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  74. // resolve with bar
  75. doResolve!({default: BarComponent});
  76. expect(await screen.findByText('my bar component')).toBeInTheDocument();
  77. // Update component prop to a mock to make sure it isn't re-called
  78. const getComponent2: GetComponent = jest.fn(
  79. () => new Promise<ResolvedComponent>(() => {})
  80. );
  81. rerender(<LazyLoad component={getComponent2} />);
  82. expect(getComponent2).toHaveBeenCalledTimes(1);
  83. // Does not refetch on other prop changes
  84. rerender(<LazyLoad component={getComponent2} testProp />);
  85. expect(getComponent2).toHaveBeenCalledTimes(1);
  86. });
  87. });