organizationContextContainer.spec.jsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  2. import {openSudo} from 'sentry/actionCreators/modal';
  3. import * as OrganizationActionCreator from 'sentry/actionCreators/organization';
  4. import ConfigStore from 'sentry/stores/configStore';
  5. import OrganizationStore from 'sentry/stores/organizationStore';
  6. import ProjectsStore from 'sentry/stores/projectsStore';
  7. import TeamStore from 'sentry/stores/teamStore';
  8. import useOrganization from 'sentry/utils/useOrganization';
  9. import {OrganizationLegacyContext} from 'sentry/views/organizationContextContainer';
  10. jest.mock('sentry/stores/configStore', () => ({
  11. get: jest.fn(),
  12. }));
  13. jest.mock('sentry/actionCreators/modal', () => ({
  14. openSudo: jest.fn(),
  15. }));
  16. describe('OrganizationContextContainer', function () {
  17. const org = TestStubs.Organization();
  18. const teams = [TestStubs.Team()];
  19. const projects = [TestStubs.Project()];
  20. const api = new MockApiClient();
  21. let getOrgMock;
  22. let getProjectsMock;
  23. let getTeamsMock;
  24. function DisplayOrg() {
  25. const contextOrg = useOrganization();
  26. return <div>{contextOrg.slug}</div>;
  27. }
  28. function makeComponent(props) {
  29. return (
  30. <OrganizationLegacyContext
  31. api={api}
  32. params={{orgId: 'org-slug'}}
  33. location={{query: {}}}
  34. routes={[]}
  35. {...props}
  36. >
  37. <DisplayOrg />
  38. </OrganizationLegacyContext>
  39. );
  40. }
  41. function renderComponent(props) {
  42. return render(makeComponent(props));
  43. }
  44. beforeEach(function () {
  45. MockApiClient.clearMockResponses();
  46. getOrgMock = MockApiClient.addMockResponse({
  47. url: '/organizations/org-slug/',
  48. body: org,
  49. });
  50. getProjectsMock = MockApiClient.addMockResponse({
  51. url: '/organizations/org-slug/projects/',
  52. body: projects,
  53. });
  54. getTeamsMock = MockApiClient.addMockResponse({
  55. url: '/organizations/org-slug/teams/',
  56. body: teams,
  57. });
  58. jest.spyOn(TeamStore, 'loadInitialData');
  59. jest.spyOn(ProjectsStore, 'loadInitialData');
  60. jest.spyOn(OrganizationActionCreator, 'fetchOrganizationDetails');
  61. });
  62. afterEach(function () {
  63. OrganizationStore.reset();
  64. jest.restoreAllMocks();
  65. });
  66. it('renders and fetches org, projects, and teams', async function () {
  67. renderComponent();
  68. await waitFor(() => expect(getOrgMock).toHaveBeenCalled());
  69. expect(getProjectsMock).toHaveBeenCalled();
  70. expect(getTeamsMock).toHaveBeenCalled();
  71. expect(screen.queryByRole('loading-indicator')).not.toBeInTheDocument();
  72. expect(screen.getByText(org.slug)).toBeInTheDocument();
  73. expect(
  74. screen.queryByText('The organization you were looking for was not found.')
  75. ).not.toBeInTheDocument();
  76. expect(TeamStore.loadInitialData).toHaveBeenCalledWith(teams);
  77. expect(ProjectsStore.loadInitialData).toHaveBeenCalledWith(projects);
  78. expect(OrganizationActionCreator.fetchOrganizationDetails).toHaveBeenCalledWith(
  79. api,
  80. 'org-slug',
  81. true,
  82. true
  83. );
  84. });
  85. it('fetches new org when router params change', async function () {
  86. const newOrg = TestStubs.Organization({slug: 'new-slug'});
  87. const {rerender} = renderComponent();
  88. expect(await screen.findByText(org.slug)).toBeInTheDocument();
  89. const mock = MockApiClient.addMockResponse({
  90. url: '/organizations/new-slug/',
  91. body: newOrg,
  92. });
  93. const projectsMock = MockApiClient.addMockResponse({
  94. url: '/organizations/new-slug/projects/',
  95. body: projects,
  96. });
  97. const teamsMock = MockApiClient.addMockResponse({
  98. url: '/organizations/new-slug/teams/',
  99. body: teams,
  100. });
  101. // Re-render with new org slug
  102. rerender(makeComponent({params: {orgId: newOrg.slug}}));
  103. // Loads new org
  104. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  105. // Renders new org
  106. expect(await screen.findByText(newOrg.slug)).toBeInTheDocument();
  107. expect(mock).toHaveBeenLastCalledWith('/organizations/new-slug/', expect.anything());
  108. expect(projectsMock).toHaveBeenCalled();
  109. expect(teamsMock).toHaveBeenCalled();
  110. });
  111. it('shows loading error for non-superusers on 403s', async function () {
  112. getOrgMock = MockApiClient.addMockResponse({
  113. url: '/organizations/org-slug/',
  114. statusCode: 403,
  115. });
  116. jest.spyOn(console, 'error').mockImplementation(jest.fn()); // eslint-disable-line no-console
  117. renderComponent();
  118. expect(
  119. await screen.findByText('There was an error loading data.')
  120. ).toBeInTheDocument();
  121. // eslint-disable-next-line no-console
  122. expect(console.error).toHaveBeenCalled();
  123. });
  124. it('opens sudo modal for superusers on 403s', async function () {
  125. ConfigStore.get.mockImplementation(() => ({
  126. isSuperuser: true,
  127. }));
  128. getOrgMock = MockApiClient.addMockResponse({
  129. url: '/organizations/org-slug/',
  130. statusCode: 403,
  131. });
  132. renderComponent();
  133. await waitFor(() => expect(openSudo).toHaveBeenCalled());
  134. });
  135. it('uses last organization from ConfigStore', function () {
  136. getOrgMock = MockApiClient.addMockResponse({
  137. url: '/organizations/last-org/',
  138. body: org,
  139. });
  140. MockApiClient.addMockResponse({
  141. url: '/organizations/last-org/projects/',
  142. body: projects,
  143. });
  144. MockApiClient.addMockResponse({
  145. url: '/organizations/last-org/teams/',
  146. body: teams,
  147. });
  148. // mocking `.get('lastOrganization')`
  149. ConfigStore.get.mockImplementation(() => 'last-org');
  150. renderComponent({useLastOrganization: true, params: {}});
  151. expect(getOrgMock).toHaveBeenLastCalledWith(
  152. '/organizations/last-org/',
  153. expect.anything()
  154. );
  155. });
  156. it('uses last organization from `organizations` prop', async function () {
  157. MockApiClient.addMockResponse({
  158. url: '/organizations/foo/environments/',
  159. body: TestStubs.Environments(),
  160. });
  161. getOrgMock = MockApiClient.addMockResponse({
  162. url: '/organizations/foo/',
  163. body: org,
  164. });
  165. getProjectsMock = MockApiClient.addMockResponse({
  166. url: '/organizations/foo/projects/',
  167. body: projects,
  168. });
  169. getTeamsMock = MockApiClient.addMockResponse({
  170. url: '/organizations/foo/teams/',
  171. body: teams,
  172. });
  173. ConfigStore.get.mockImplementation(() => '');
  174. const {rerender} = renderComponent({
  175. params: {orgId: ''},
  176. useLastOrganization: true,
  177. organizationsLoading: true,
  178. organizations: [],
  179. });
  180. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  181. rerender(
  182. makeComponent({
  183. params: {orgId: ''},
  184. useLastOrganization: true,
  185. organizationsLoading: false,
  186. organizations: [
  187. TestStubs.Organization({slug: 'foo'}),
  188. TestStubs.Organization({slug: 'bar'}),
  189. ],
  190. })
  191. );
  192. expect(await screen.findByText(org.slug)).toBeInTheDocument();
  193. expect(getOrgMock).toHaveBeenCalled();
  194. expect(getProjectsMock).toHaveBeenCalled();
  195. expect(getTeamsMock).toHaveBeenCalled();
  196. });
  197. it('uses last organization when no orgId in URL - and fetches org details once', async function () {
  198. ConfigStore.get.mockImplementation(() => 'my-last-org');
  199. getOrgMock = MockApiClient.addMockResponse({
  200. url: '/organizations/my-last-org/',
  201. body: TestStubs.Organization({slug: 'my-last-org'}),
  202. });
  203. getProjectsMock = MockApiClient.addMockResponse({
  204. url: '/organizations/my-last-org/projects/',
  205. body: projects,
  206. });
  207. getTeamsMock = MockApiClient.addMockResponse({
  208. url: '/organizations/my-last-org/teams/',
  209. body: teams,
  210. });
  211. const {rerender} = renderComponent({
  212. params: {},
  213. useLastOrganization: true,
  214. organizations: [],
  215. });
  216. expect(await screen.findByText('my-last-org')).toBeInTheDocument();
  217. expect(getOrgMock).toHaveBeenCalledTimes(1);
  218. // Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
  219. // org details fetch
  220. rerender(
  221. makeComponent({
  222. params: {},
  223. useLastOrganization: true,
  224. organizationsLoading: false,
  225. organizations: [
  226. TestStubs.Organization({slug: 'foo'}),
  227. TestStubs.Organization({slug: 'bar'}),
  228. ],
  229. })
  230. );
  231. expect(getOrgMock).toHaveBeenCalledTimes(1);
  232. expect(getProjectsMock).toHaveBeenCalledTimes(1);
  233. expect(getTeamsMock).toHaveBeenCalledTimes(1);
  234. });
  235. it('fetches org details only once if organizations loading store changes', async function () {
  236. const {rerender} = renderComponent({
  237. params: {orgId: 'org-slug'},
  238. organizationsLoading: true,
  239. organizations: [],
  240. });
  241. expect(await screen.findByText(org.slug)).toBeInTheDocument();
  242. expect(getOrgMock).toHaveBeenCalledTimes(1);
  243. // Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
  244. // org details fetch
  245. rerender(
  246. makeComponent({
  247. params: {orgId: 'org-slug'},
  248. organizationsLoading: false,
  249. organizations: [
  250. TestStubs.Organization({slug: 'foo'}),
  251. TestStubs.Organization({slug: 'bar'}),
  252. ],
  253. })
  254. );
  255. expect(getOrgMock).toHaveBeenCalledTimes(1);
  256. expect(getProjectsMock).toHaveBeenCalledTimes(1);
  257. expect(getTeamsMock).toHaveBeenCalledTimes(1);
  258. });
  259. });