useApiRequests.spec.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import * as React from 'react';
  2. import {createMemoryHistory, Route, Router, RouterContext} from 'react-router';
  3. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import useApiRequests from 'sentry/utils/useApiRequests';
  5. import {RouteContext} from 'sentry/views/routeContext';
  6. describe('useApiRequests', () => {
  7. describe('error handling', () => {
  8. function HomePage() {
  9. const {renderComponent, data} = useApiRequests({
  10. endpoints: [['message', '/some/path/to/something/']],
  11. shouldRenderBadRequests: true,
  12. });
  13. return renderComponent(<div>{data.message?.value}</div>);
  14. }
  15. function UniqueErrorsAsyncComponent() {
  16. const {renderComponent, data} = useApiRequests({
  17. endpoints: [
  18. ['first', '/first/path/'],
  19. ['second', '/second/path/'],
  20. ['third', '/third/path/'],
  21. ],
  22. shouldRenderBadRequests: true,
  23. });
  24. return renderComponent(<div>{data.message?.value}</div>);
  25. }
  26. const memoryHistory = createMemoryHistory();
  27. memoryHistory.push('/');
  28. function App() {
  29. return (
  30. <Router
  31. history={memoryHistory}
  32. render={props => {
  33. return (
  34. <RouteContext.Provider value={props}>
  35. <RouterContext {...props} />
  36. </RouteContext.Provider>
  37. );
  38. }}
  39. >
  40. <Route path="/" component={HomePage} />
  41. <Route path="/unique-error" component={UniqueErrorsAsyncComponent} />
  42. </Router>
  43. );
  44. }
  45. it('renders on successful request', async function () {
  46. MockApiClient.clearMockResponses();
  47. MockApiClient.addMockResponse({
  48. url: '/some/path/to/something/',
  49. method: 'GET',
  50. body: {
  51. value: 'hi',
  52. },
  53. });
  54. render(<App />);
  55. await waitFor(() => {
  56. expect(screen.getByText('hi')).toBeInTheDocument();
  57. });
  58. });
  59. it('renders error message', async function () {
  60. MockApiClient.clearMockResponses();
  61. MockApiClient.addMockResponse({
  62. url: '/some/path/to/something/',
  63. method: 'GET',
  64. body: {
  65. detail: 'oops there was a problem',
  66. },
  67. statusCode: 400,
  68. });
  69. render(<App />);
  70. await waitFor(() => {
  71. expect(screen.getByText('oops there was a problem')).toBeInTheDocument();
  72. });
  73. });
  74. it('renders only unique error message', async function () {
  75. MockApiClient.clearMockResponses();
  76. MockApiClient.addMockResponse({
  77. url: '/first/path/',
  78. method: 'GET',
  79. body: {
  80. detail: 'oops there was a problem 1',
  81. },
  82. statusCode: 400,
  83. });
  84. MockApiClient.addMockResponse({
  85. url: '/second/path/',
  86. method: 'GET',
  87. body: {
  88. detail: 'oops there was a problem 2',
  89. },
  90. statusCode: 400,
  91. });
  92. MockApiClient.addMockResponse({
  93. url: '/third/path/',
  94. method: 'GET',
  95. body: {
  96. detail: 'oops there was a different problem',
  97. },
  98. statusCode: 400,
  99. });
  100. memoryHistory.push('/unique-error');
  101. render(<App />);
  102. await waitFor(() => {
  103. expect(screen.getByTestId('loading-error')).toHaveTextContent(
  104. 'oops there was a problem 1'
  105. );
  106. });
  107. await waitFor(() => {
  108. expect(screen.getByTestId('loading-error')).toHaveTextContent(
  109. 'oops there was a problem 2'
  110. );
  111. });
  112. await waitFor(() => {
  113. expect(screen.getByTestId('loading-error')).toHaveTextContent(
  114. 'oops there was a different problem'
  115. );
  116. });
  117. });
  118. });
  119. describe('slow requests', () => {
  120. it('calls onLoadAllEndpointsSuccess when all endpoints have been loaded', async () => {
  121. const requestQueue: Array<() => void> = [];
  122. jest.spyOn(MockApiClient.prototype, 'request').mockImplementation((_, options) => {
  123. requestQueue.push(() => {
  124. return options?.success?.({message: 'good'});
  125. });
  126. });
  127. const mockOnAllEndpointsSuccess = jest.fn();
  128. function MultiRouteComponent() {
  129. const {remainingRequests} = useApiRequests({
  130. endpoints: [
  131. ['data', '/some/path/to/something/'],
  132. ['project', '/another/path/here'],
  133. ],
  134. onLoadAllEndpointsSuccess: mockOnAllEndpointsSuccess,
  135. });
  136. return <div data-test-id="remaining-requests">{remainingRequests}</div>;
  137. }
  138. const memoryHistory = createMemoryHistory();
  139. memoryHistory.push('/multi');
  140. function App() {
  141. return (
  142. <Router
  143. history={memoryHistory}
  144. render={props => {
  145. return (
  146. <RouteContext.Provider value={props}>
  147. <RouterContext {...props} />
  148. </RouteContext.Provider>
  149. );
  150. }}
  151. >
  152. <Route path="/multi" component={MultiRouteComponent} />
  153. </Router>
  154. );
  155. }
  156. render(<App />);
  157. expect(await screen.findByText('2')).toBeInTheDocument();
  158. const req1 = requestQueue.shift();
  159. req1?.();
  160. expect(await screen.findByText('1')).toBeInTheDocument();
  161. const req2 = requestQueue.shift();
  162. req2?.();
  163. expect(await screen.findByText('0')).toBeInTheDocument();
  164. expect(mockOnAllEndpointsSuccess).toHaveBeenCalled();
  165. jest.restoreAllMocks();
  166. });
  167. });
  168. });