useApiRequests.spec.tsx 5.4 KB

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