teamMembers.spec.jsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import {
  4. openInviteMembersModal,
  5. openTeamAccessRequestModal,
  6. } from 'sentry/actionCreators/modal';
  7. import {Client} from 'sentry/api';
  8. import TeamMembers from 'sentry/views/settings/organizationTeams/teamMembers';
  9. jest.mock('sentry/actionCreators/modal', () => ({
  10. openInviteMembersModal: jest.fn(),
  11. openTeamAccessRequestModal: jest.fn(),
  12. }));
  13. describe('TeamMembers', function () {
  14. let createMock;
  15. const organization = TestStubs.Organization();
  16. const team = TestStubs.Team();
  17. const members = TestStubs.Members();
  18. const member = TestStubs.Member({
  19. id: '9',
  20. email: 'sentry9@test.com',
  21. name: 'Sentry 9 Name',
  22. });
  23. beforeEach(function () {
  24. Client.clearMockResponses();
  25. Client.addMockResponse({
  26. url: `/organizations/${organization.slug}/members/`,
  27. method: 'GET',
  28. body: [member],
  29. });
  30. Client.addMockResponse({
  31. url: `/teams/${organization.slug}/${team.slug}/members/`,
  32. method: 'GET',
  33. body: members,
  34. });
  35. createMock = Client.addMockResponse({
  36. url: `/organizations/${organization.slug}/members/${member.id}/teams/${team.slug}/`,
  37. method: 'POST',
  38. });
  39. });
  40. it('can add member to team with open membership', async function () {
  41. const org = TestStubs.Organization({access: [], openMembership: true});
  42. render(
  43. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />
  44. );
  45. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  46. userEvent.click(screen.getAllByTestId('letter_avatar-avatar')[0]);
  47. expect(createMock).toHaveBeenCalled();
  48. });
  49. it('can add member to team with team:admin permission', async function () {
  50. const org = TestStubs.Organization({access: ['team:admin'], openMembership: false});
  51. render(
  52. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />
  53. );
  54. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  55. userEvent.click(screen.getAllByTestId('letter_avatar-avatar')[0]);
  56. expect(createMock).toHaveBeenCalled();
  57. });
  58. it('can add member to team with org:write permission', async function () {
  59. const org = TestStubs.Organization({access: ['org:write'], openMembership: false});
  60. render(
  61. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />
  62. );
  63. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  64. userEvent.click(screen.getAllByTestId('letter_avatar-avatar')[0]);
  65. expect(createMock).toHaveBeenCalled();
  66. });
  67. it('can request access to add member to team without permission', async function () {
  68. const org = TestStubs.Organization({access: [], openMembership: false});
  69. render(
  70. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />
  71. );
  72. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  73. userEvent.click(screen.getAllByTestId('letter_avatar-avatar')[0]);
  74. expect(openTeamAccessRequestModal).toHaveBeenCalled();
  75. });
  76. it('can invite member from team dropdown with access', async function () {
  77. const {organization: org, routerContext} = initializeOrg({
  78. organization: TestStubs.Organization({
  79. access: ['team:admin'],
  80. openMembership: false,
  81. }),
  82. });
  83. render(
  84. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  85. {context: routerContext}
  86. );
  87. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  88. userEvent.click(screen.getByTestId('invite-member'));
  89. expect(openInviteMembersModal).toHaveBeenCalled();
  90. });
  91. it('can invite member from team dropdown with access and `Open Membership` enabled', async function () {
  92. const {organization: org, routerContext} = initializeOrg({
  93. organization: TestStubs.Organization({
  94. access: ['team:admin'],
  95. openMembership: true,
  96. }),
  97. });
  98. render(
  99. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  100. {context: routerContext}
  101. );
  102. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  103. userEvent.click(screen.getByTestId('invite-member'));
  104. expect(openInviteMembersModal).toHaveBeenCalled();
  105. });
  106. it('can invite member from team dropdown without access and `Open Membership` enabled', async function () {
  107. const {organization: org, routerContext} = initializeOrg({
  108. organization: TestStubs.Organization({access: [], openMembership: true}),
  109. });
  110. render(
  111. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  112. {context: routerContext}
  113. );
  114. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  115. userEvent.click(screen.getByTestId('invite-member'));
  116. expect(openInviteMembersModal).toHaveBeenCalled();
  117. });
  118. it('can invite member from team dropdown without access and `Open Membership` disabled', async function () {
  119. const {organization: org, routerContext} = initializeOrg({
  120. organization: TestStubs.Organization({access: [], openMembership: false}),
  121. });
  122. render(
  123. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  124. {context: routerContext}
  125. );
  126. userEvent.click((await screen.findAllByRole('button', {name: 'Add Member'}))[0]);
  127. userEvent.click(screen.getByTestId('invite-member'));
  128. expect(openInviteMembersModal).toHaveBeenCalled();
  129. });
  130. it('can remove member from team', async function () {
  131. const deleteMock = Client.addMockResponse({
  132. url: `/organizations/${organization.slug}/members/${members[0].id}/teams/${team.slug}/`,
  133. method: 'DELETE',
  134. });
  135. render(
  136. <TeamMembers
  137. params={{orgId: organization.slug, teamId: team.slug}}
  138. organization={organization}
  139. />
  140. );
  141. await screen.findAllByRole('button', {name: 'Add Member'});
  142. expect(deleteMock).not.toHaveBeenCalled();
  143. userEvent.click(screen.getAllByRole('button', {name: 'Remove'})[0]);
  144. expect(deleteMock).toHaveBeenCalled();
  145. });
  146. it('can only remove self from team', async function () {
  147. const me = TestStubs.Member({
  148. id: '123',
  149. email: 'foo@example.com',
  150. });
  151. Client.addMockResponse({
  152. url: `/teams/${organization.slug}/${team.slug}/members/`,
  153. method: 'GET',
  154. body: [...members, me],
  155. });
  156. const deleteMock = Client.addMockResponse({
  157. url: `/organizations/${organization.slug}/members/${me.id}/teams/${team.slug}/`,
  158. method: 'DELETE',
  159. });
  160. const organizationMember = TestStubs.Organization({access: []});
  161. render(
  162. <TeamMembers
  163. params={{orgId: organization.slug, teamId: team.slug}}
  164. organization={organizationMember}
  165. />
  166. );
  167. await screen.findAllByRole('button', {name: 'Add Member'});
  168. expect(deleteMock).not.toHaveBeenCalled();
  169. expect(screen.getAllByTestId('letter_avatar-avatar')).toHaveLength(
  170. members.length + 1
  171. );
  172. // Can only remove self
  173. expect(screen.getByRole('button', {name: 'Remove'})).toBeInTheDocument();
  174. userEvent.click(screen.getByRole('button', {name: 'Remove'}));
  175. expect(deleteMock).toHaveBeenCalled();
  176. });
  177. it('does not renders team-level roles', async function () {
  178. const me = TestStubs.Member({
  179. id: '123',
  180. email: 'foo@example.com',
  181. role: 'owner',
  182. });
  183. Client.addMockResponse({
  184. url: `/teams/${organization.slug}/${team.slug}/members/`,
  185. method: 'GET',
  186. body: [...members, me],
  187. });
  188. await render(
  189. <TeamMembers
  190. params={{orgId: organization.slug, teamId: team.slug}}
  191. organization={organization}
  192. />
  193. );
  194. const admins = screen.queryByText('Team Admin');
  195. expect(admins).not.toBeInTheDocument();
  196. const contributors = screen.queryByText('Team Contributor');
  197. expect(contributors).not.toBeInTheDocument();
  198. });
  199. it('renders team-level roles with flag', async function () {
  200. const manager = TestStubs.Member({
  201. id: '123',
  202. email: 'foo@example.com',
  203. orgRole: 'manager',
  204. });
  205. Client.addMockResponse({
  206. url: `/teams/${organization.slug}/${team.slug}/members/`,
  207. method: 'GET',
  208. body: [...members, manager],
  209. });
  210. const orgWithTeamRoles = TestStubs.Organization({features: ['team-roles']});
  211. await render(
  212. <TeamMembers
  213. params={{orgId: orgWithTeamRoles.slug, teamId: team.slug}}
  214. organization={orgWithTeamRoles}
  215. />
  216. );
  217. const admins = screen.queryAllByText('Team Admin');
  218. expect(admins).toHaveLength(3);
  219. const contributors = screen.queryAllByText('Contributor');
  220. expect(contributors).toHaveLength(2);
  221. });
  222. });