indicators.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  2. import {
  3. addErrorMessage,
  4. addMessage,
  5. addSuccessMessage,
  6. clearIndicators,
  7. } from 'sentry/actionCreators/indicator';
  8. import Indicators from 'sentry/components/indicators';
  9. import IndicatorStore from 'sentry/stores/indicatorStore';
  10. // Make sure we use `duration: null` to test add/remove
  11. jest.mock('framer-motion', () => ({
  12. ...jest.requireActual('framer-motion'),
  13. AnimatePresence: jest.fn(({children}) => children),
  14. }));
  15. describe('Indicators', function () {
  16. beforeEach(function () {
  17. act(() => clearIndicators());
  18. });
  19. it('renders nothing by default', function () {
  20. const {container} = render(<Indicators />);
  21. expect(container).toHaveTextContent('');
  22. });
  23. it('has a loading indicator by default', function () {
  24. const {container} = render(<Indicators />);
  25. // when "type" is empty, we should treat it as loading state
  26. act(() => void IndicatorStore.add('Loading'));
  27. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  28. expect(container).toHaveTextContent('Loading');
  29. });
  30. it('adds and removes a toast by calling IndicatorStore directly', function () {
  31. const {container} = render(<Indicators />);
  32. // when "type" is empty, we should treat it as loading state
  33. let indicator;
  34. act(() => {
  35. indicator = IndicatorStore.add('Loading');
  36. });
  37. expect(container).toHaveTextContent('Loading');
  38. // Old indicator gets replaced when a new one is added
  39. act(() => IndicatorStore.remove(indicator));
  40. expect(container).toHaveTextContent('');
  41. });
  42. // This is a common pattern used throughout the code for API calls
  43. it('adds and replaces toast by calling IndicatorStore directly', function () {
  44. const {container} = render(<Indicators />);
  45. act(() => void IndicatorStore.add('Loading'));
  46. expect(container).toHaveTextContent('Loading');
  47. // Old indicator gets replaced when a new one is added
  48. act(() => void IndicatorStore.add('success', 'success'));
  49. expect(container).toHaveTextContent('success');
  50. });
  51. it('does not have loading indicator when "type" is empty (default)', function () {
  52. const {container} = render(<Indicators />);
  53. act(() => addMessage('Loading', '', {duration: null}));
  54. expect(container).toHaveTextContent('Loading');
  55. expect(screen.queryByTestId('loading-indicator')).not.toBeInTheDocument();
  56. });
  57. it('has a loading indicator when type is "loading"', function () {
  58. const {container} = render(<Indicators />);
  59. act(() => addMessage('Loading', 'loading', {duration: null}));
  60. expect(container).toHaveTextContent('Loading');
  61. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  62. });
  63. it('adds and removes toast by calling action creators', function () {
  64. const {container} = render(<Indicators />);
  65. // action creators don't return anything
  66. act(() => addMessage('Loading', '', {duration: null}));
  67. expect(container).toHaveTextContent('Loading');
  68. // If no indicator is specified, will remove all indicators
  69. act(() => clearIndicators());
  70. expect(container).toHaveTextContent('');
  71. expect(screen.queryByTestId('loading-indicator')).not.toBeInTheDocument();
  72. });
  73. it('adds and replaces toast by calling action creators', function () {
  74. const {container} = render(<Indicators />);
  75. act(() => addMessage('Loading', '', {duration: null}));
  76. expect(container).toHaveTextContent('Loading');
  77. // Old indicator gets replaced when a new one is added
  78. act(() => addMessage('success', 'success', {duration: null}));
  79. expect(container).toHaveTextContent('success');
  80. expect(screen.queryByTestId('loading-indicator')).not.toBeInTheDocument();
  81. });
  82. it('adds and replaces toasts by calling action creators helpers', async function () {
  83. const {container} = render(<Indicators />);
  84. // Old indicator gets replaced when a new one is added
  85. act(() => addSuccessMessage('success'));
  86. await waitFor(() => {
  87. expect(container).toHaveTextContent('success');
  88. });
  89. act(() => clearIndicators());
  90. act(() => addErrorMessage('error'));
  91. await waitFor(() => {
  92. expect(container).toHaveTextContent('error');
  93. });
  94. });
  95. it('appends toasts', function () {
  96. const {container} = render(<Indicators />);
  97. act(() => addMessage('Loading', '', {append: true, duration: null}));
  98. expect(screen.getByTestId('toast')).toHaveTextContent('Loading');
  99. act(() => addMessage('Success', 'success', {append: true, duration: null}));
  100. // Toasts get appended to the end
  101. expect(screen.getByTestId('toast')).toHaveTextContent('Loading');
  102. expect(screen.getByTestId('toast-success')).toHaveTextContent('Success');
  103. act(() => addMessage('Error', 'error', {append: true, duration: null}));
  104. // Toasts get appended to the end
  105. expect(screen.getByTestId('toast')).toHaveTextContent('Loading');
  106. expect(screen.getByTestId('toast-success')).toHaveTextContent('Success');
  107. expect(screen.getByTestId('toast-error')).toHaveTextContent('Error');
  108. // clears all toasts
  109. act(() => clearIndicators());
  110. expect(container).toHaveTextContent('');
  111. expect(screen.queryByTestId('loading-indicator')).not.toBeInTheDocument();
  112. });
  113. it('dismisses on click', async function () {
  114. const {container} = render(<Indicators />);
  115. act(() => addMessage('Loading', '', {append: true, duration: null}));
  116. expect(screen.getByTestId('toast')).toHaveTextContent('Loading');
  117. await userEvent.click(screen.getByTestId('toast'));
  118. expect(container).toHaveTextContent('');
  119. expect(screen.queryByTestId('toast')).not.toBeInTheDocument();
  120. });
  121. it('hides after 10s', function () {
  122. jest.useFakeTimers();
  123. const {container} = render(<Indicators />);
  124. act(() => addMessage('Duration', '', {append: true, duration: 10000}));
  125. act(() => jest.advanceTimersByTime(9000));
  126. expect(screen.getByTestId('toast')).toHaveTextContent('Duration');
  127. // Still visible
  128. act(() => jest.advanceTimersByTime(999));
  129. expect(screen.getByTestId('toast')).toHaveTextContent('Duration');
  130. act(() => jest.advanceTimersByTime(2));
  131. expect(container).toHaveTextContent('');
  132. expect(screen.queryByTestId('toast')).not.toBeInTheDocument();
  133. jest.useRealTimers();
  134. });
  135. });