organizationContext.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {ProjectFixture} from 'sentry-fixture/project';
  3. import {TeamFixture} from 'sentry-fixture/team';
  4. import {UserFixture} from 'sentry-fixture/user';
  5. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  6. import * as orgsActionCreators from 'sentry/actionCreators/organizations';
  7. import {openSudo} from 'sentry/actionCreators/sudoModal';
  8. import ConfigStore from 'sentry/stores/configStore';
  9. import OrganizationStore from 'sentry/stores/organizationStore';
  10. import ProjectsStore from 'sentry/stores/projectsStore';
  11. import TeamStore from 'sentry/stores/teamStore';
  12. import type {Organization} from 'sentry/types/organization';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {OrganizationContextProvider} from './organizationContext';
  15. jest.mock('sentry/actionCreators/sudoModal');
  16. describe('OrganizationContext', function () {
  17. let getOrgMock: jest.Mock;
  18. let getProjectsMock: jest.Mock;
  19. let getTeamsMock: jest.Mock;
  20. const organization = OrganizationFixture();
  21. const project = ProjectFixture();
  22. const team = TeamFixture();
  23. function setupOrgMocks(org: Organization) {
  24. const orgMock = MockApiClient.addMockResponse({
  25. url: `/organizations/${org.slug}/`,
  26. body: org,
  27. });
  28. const projectMock = MockApiClient.addMockResponse({
  29. url: `/organizations/${org.slug}/projects/`,
  30. body: [project],
  31. });
  32. const teamMock = MockApiClient.addMockResponse({
  33. url: `/organizations/${org.slug}/teams/`,
  34. body: [team],
  35. });
  36. return {orgMock, projectMock, teamMock};
  37. }
  38. beforeEach(function () {
  39. MockApiClient.clearMockResponses();
  40. const {orgMock, projectMock, teamMock} = setupOrgMocks(organization);
  41. getOrgMock = orgMock;
  42. getProjectsMock = projectMock;
  43. getTeamsMock = teamMock;
  44. TeamStore.reset();
  45. ProjectsStore.reset();
  46. ConfigStore.init();
  47. OrganizationStore.reset();
  48. jest.spyOn(console, 'error').mockImplementation(jest.fn());
  49. });
  50. afterEach(function () {
  51. // eslint-disable-next-line no-console
  52. jest.mocked(console.error).mockRestore();
  53. });
  54. /**
  55. * Used to test that the organization context is propegated
  56. */
  57. function OrganizationName() {
  58. const org = useOrganization({allowNull: true});
  59. return <div>{org?.slug ?? 'no-org'}</div>;
  60. }
  61. it('fetches org, projects, teams, and provides organization context', async function () {
  62. render(
  63. <OrganizationContextProvider>
  64. <OrganizationName />
  65. </OrganizationContextProvider>
  66. );
  67. expect(await screen.findByText(organization.slug)).toBeInTheDocument();
  68. expect(getOrgMock).toHaveBeenCalled();
  69. expect(getProjectsMock).toHaveBeenCalled();
  70. expect(getTeamsMock).toHaveBeenCalled();
  71. });
  72. it('fetches new org when router params change', async function () {
  73. // First render with org-slug
  74. const {router: testRouter} = render(
  75. <OrganizationContextProvider>
  76. <OrganizationName />
  77. </OrganizationContextProvider>,
  78. {
  79. disableRouterMocks: true,
  80. initialRouterConfig: {
  81. route: '/organizations/:orgId/',
  82. location: {
  83. pathname: `/organizations/${organization.slug}/`,
  84. },
  85. },
  86. }
  87. );
  88. expect(await screen.findByText(organization.slug)).toBeInTheDocument();
  89. expect(JSON.stringify(OrganizationStore.getState().organization)).toEqual(
  90. JSON.stringify(organization)
  91. );
  92. const anotherOrg = OrganizationFixture({slug: 'another-org'});
  93. const {orgMock, projectMock, teamMock} = setupOrgMocks(anotherOrg);
  94. const switchOrganization = jest.spyOn(orgsActionCreators, 'switchOrganization');
  95. // re-render with another-org
  96. testRouter.navigate(`/organizations/${anotherOrg.slug}/`);
  97. expect(await screen.findByText(anotherOrg.slug)).toBeInTheDocument();
  98. expect(orgMock).toHaveBeenCalled();
  99. expect(projectMock).toHaveBeenCalled();
  100. expect(teamMock).toHaveBeenCalled();
  101. expect(switchOrganization).toHaveBeenCalled();
  102. expect(JSON.stringify(OrganizationStore.getState().organization)).toEqual(
  103. JSON.stringify(anotherOrg)
  104. );
  105. });
  106. it('opens sudo modal for superusers for nonmember org with active staff', async function () {
  107. ConfigStore.set('user', UserFixture({isSuperuser: true, isStaff: true}));
  108. organization.access = [];
  109. getOrgMock = MockApiClient.addMockResponse({
  110. url: `/organizations/${organization.slug}/`,
  111. body: organization,
  112. });
  113. render(
  114. <OrganizationContextProvider>
  115. <OrganizationName />
  116. </OrganizationContextProvider>
  117. );
  118. await waitFor(() => !OrganizationStore.getState().loading);
  119. await waitFor(() => expect(openSudo).toHaveBeenCalled());
  120. });
  121. it('opens sudo modal for superusers on 403s', async function () {
  122. ConfigStore.set('user', UserFixture({isSuperuser: true}));
  123. getOrgMock = MockApiClient.addMockResponse({
  124. url: '/organizations/org-slug/',
  125. statusCode: 403,
  126. });
  127. render(
  128. <OrganizationContextProvider>
  129. <OrganizationName />
  130. </OrganizationContextProvider>
  131. );
  132. await waitFor(() => !OrganizationStore.getState().loading);
  133. // eslint-disable-next-line no-console
  134. await waitFor(() => expect(console.error).toHaveBeenCalled());
  135. expect(openSudo).toHaveBeenCalled();
  136. });
  137. /**
  138. * This test will rarely happen since most configurations are now using customer domains
  139. */
  140. it('uses last organization slug from ConfigStore', async function () {
  141. const configStoreOrg = OrganizationFixture({slug: 'config-store-org'});
  142. ConfigStore.set('lastOrganization', configStoreOrg.slug);
  143. const {orgMock, projectMock, teamMock} = setupOrgMocks(configStoreOrg);
  144. // orgId is not present in the router.
  145. render(
  146. <OrganizationContextProvider>
  147. <OrganizationName />
  148. </OrganizationContextProvider>,
  149. {
  150. disableRouterMocks: true,
  151. initialRouterConfig: {
  152. route: '/organizations/',
  153. location: {
  154. pathname: `/organizations/`,
  155. },
  156. },
  157. }
  158. );
  159. expect(await screen.findByText(configStoreOrg.slug)).toBeInTheDocument();
  160. expect(orgMock).toHaveBeenCalled();
  161. expect(projectMock).toHaveBeenCalled();
  162. expect(teamMock).toHaveBeenCalled();
  163. });
  164. });