teamProjects.spec.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import {TeamProjects as OrganizationTeamProjects} from 'sentry/views/settings/organizationTeams/teamProjects';
  4. describe('OrganizationTeamProjects', function () {
  5. let team;
  6. let getMock;
  7. let putMock;
  8. let postMock;
  9. let deleteMock;
  10. const project = TestStubs.Project({
  11. teams: [team],
  12. access: ['project:read', 'project:write', 'project:admin'],
  13. });
  14. const project2 = TestStubs.Project({
  15. id: '3',
  16. slug: 'project-slug-2',
  17. name: 'Project Name 2',
  18. access: ['project:read', 'project:write', 'project:admin'],
  19. });
  20. const {routerContext, organization} = initializeOrg({
  21. organization: TestStubs.Organization({slug: 'org-slug'}),
  22. projects: [project, project2],
  23. });
  24. const router = TestStubs.router();
  25. const routerProps = {
  26. router,
  27. routes: router.routes,
  28. params: router.params,
  29. routeParams: router.params,
  30. route: router.routes[0],
  31. location: router.location,
  32. };
  33. beforeEach(function () {
  34. team = TestStubs.Team({slug: 'team-slug'});
  35. getMock = MockApiClient.addMockResponse({
  36. url: '/organizations/org-slug/projects/',
  37. body: [project, project2],
  38. });
  39. putMock = MockApiClient.addMockResponse({
  40. method: 'PUT',
  41. url: '/projects/org-slug/project-slug/',
  42. body: project,
  43. });
  44. postMock = MockApiClient.addMockResponse({
  45. method: 'POST',
  46. url: `/projects/org-slug/${project2.slug}/teams/${team.slug}/`,
  47. body: {...project2, teams: [team]},
  48. status: 201,
  49. });
  50. deleteMock = MockApiClient.addMockResponse({
  51. method: 'DELETE',
  52. url: `/projects/org-slug/${project2.slug}/teams/${team.slug}/`,
  53. body: {...project2, teams: []},
  54. status: 204,
  55. });
  56. });
  57. afterEach(function () {
  58. MockApiClient.clearMockResponses();
  59. });
  60. it('fetches linked and unlinked projects', function () {
  61. render(
  62. <OrganizationTeamProjects
  63. {...routerProps}
  64. api={new MockApiClient()}
  65. organization={organization}
  66. team={team}
  67. params={{teamId: team.slug}}
  68. location={{...routerProps.location, query: {}}}
  69. />,
  70. {context: routerContext}
  71. );
  72. expect(getMock).toHaveBeenCalledTimes(2);
  73. expect(getMock.mock.calls[0][1].query.query).toBe('team:team-slug');
  74. expect(getMock.mock.calls[1][1].query.query).toBe('!team:team-slug');
  75. });
  76. it('Should render', async function () {
  77. const {container} = render(
  78. <OrganizationTeamProjects
  79. {...routerProps}
  80. api={new MockApiClient()}
  81. organization={organization}
  82. team={team}
  83. params={{teamId: team.slug}}
  84. location={{...routerProps.location, query: {}}}
  85. />,
  86. {context: routerContext}
  87. );
  88. expect(await screen.findByText('project-slug')).toBeInTheDocument();
  89. expect(container).toSnapshot();
  90. });
  91. it('Should allow bookmarking', async function () {
  92. render(
  93. <OrganizationTeamProjects
  94. {...routerProps}
  95. api={new MockApiClient()}
  96. organization={organization}
  97. team={team}
  98. params={{teamId: team.slug}}
  99. location={{...routerProps.location, query: {}}}
  100. />,
  101. {context: routerContext}
  102. );
  103. const stars = await screen.findAllByRole('button', {name: 'Bookmark Project'});
  104. expect(stars).toHaveLength(2);
  105. await userEvent.click(stars[0]);
  106. expect(
  107. screen.getByRole('button', {name: 'Bookmark Project', pressed: true})
  108. ).toBeInTheDocument();
  109. expect(putMock).toHaveBeenCalledTimes(1);
  110. });
  111. it('Should allow adding and removing projects', async function () {
  112. render(
  113. <OrganizationTeamProjects
  114. {...routerProps}
  115. api={new MockApiClient()}
  116. organization={organization}
  117. team={team}
  118. params={{teamId: team.slug}}
  119. location={{...routerProps.location, query: {}}}
  120. />,
  121. {context: routerContext}
  122. );
  123. expect(getMock).toHaveBeenCalledTimes(2);
  124. await userEvent.click(await screen.findByText('Add Project'));
  125. // console.log(screen.debug());
  126. await userEvent.click(screen.getByRole('option', {name: 'project-slug-2'}));
  127. expect(postMock).toHaveBeenCalledTimes(1);
  128. // find second project's remove button
  129. const removeButtons = await screen.findAllByRole('button', {name: 'Remove'});
  130. await userEvent.click(removeButtons[1]);
  131. expect(deleteMock).toHaveBeenCalledTimes(1);
  132. });
  133. it('handles filtering unlinked projects', async function () {
  134. render(
  135. <OrganizationTeamProjects
  136. {...routerProps}
  137. api={new MockApiClient()}
  138. organization={organization}
  139. team={team}
  140. params={{teamId: team.slug}}
  141. location={{...routerProps.location, query: {}}}
  142. />,
  143. {context: routerContext}
  144. );
  145. expect(getMock).toHaveBeenCalledTimes(2);
  146. await userEvent.click(await screen.findByText('Add Project'));
  147. await userEvent.type(screen.getByRole('textbox'), 'a');
  148. expect(getMock).toHaveBeenCalledTimes(3);
  149. expect(getMock).toHaveBeenCalledWith(
  150. '/organizations/org-slug/projects/',
  151. expect.objectContaining({
  152. query: expect.objectContaining({
  153. query: '!team:team-slug a',
  154. }),
  155. })
  156. );
  157. });
  158. });