teamMembers.spec.jsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import {mountWithTheme} from 'sentry-test/enzyme';
  2. import {
  3. openInviteMembersModal,
  4. openTeamAccessRequestModal,
  5. } from 'app/actionCreators/modal';
  6. import {Client} from 'app/api';
  7. import TeamMembers from 'app/views/settings/organizationTeams/teamMembers';
  8. jest.mock('app/actionCreators/modal', () => ({
  9. openInviteMembersModal: jest.fn(),
  10. openTeamAccessRequestModal: jest.fn(),
  11. }));
  12. describe('TeamMembers', function () {
  13. let createMock;
  14. const organization = TestStubs.Organization();
  15. const routerContext = TestStubs.routerContext([{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('renders', async function () {
  41. const wrapper = mountWithTheme(
  42. <TeamMembers
  43. params={{orgId: organization.slug, teamId: team.slug}}
  44. organization={organization}
  45. />,
  46. routerContext
  47. );
  48. await tick();
  49. wrapper.update();
  50. });
  51. it('can add member to team with open membership', async function () {
  52. const org = TestStubs.Organization({access: [], openMembership: true});
  53. const wrapper = mountWithTheme(
  54. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  55. routerContext
  56. );
  57. await tick();
  58. wrapper.update();
  59. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  60. wrapper.find('StyledUserListElement').first().simulate('click');
  61. await tick();
  62. wrapper.update();
  63. expect(createMock).toHaveBeenCalled();
  64. });
  65. it('can add member to team with team:admin permission', async function () {
  66. const org = TestStubs.Organization({access: ['team:admin'], openMembership: false});
  67. const wrapper = mountWithTheme(
  68. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  69. routerContext
  70. );
  71. await tick();
  72. wrapper.update();
  73. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  74. wrapper.find('StyledUserListElement').first().simulate('click');
  75. expect(createMock).toHaveBeenCalled();
  76. });
  77. it('can add member to team with org:write permission', async function () {
  78. const org = TestStubs.Organization({access: ['org:write'], openMembership: false});
  79. const wrapper = mountWithTheme(
  80. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  81. routerContext
  82. );
  83. await tick();
  84. wrapper.update();
  85. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  86. wrapper.find('StyledUserListElement').first().simulate('click');
  87. expect(createMock).toHaveBeenCalled();
  88. });
  89. it('can request access to add member to team without permission', async function () {
  90. const org = TestStubs.Organization({access: [], openMembership: false});
  91. const wrapper = mountWithTheme(
  92. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  93. routerContext
  94. );
  95. await tick();
  96. wrapper.update();
  97. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  98. wrapper.find('StyledUserListElement').first().simulate('click');
  99. expect(openTeamAccessRequestModal).toHaveBeenCalled();
  100. });
  101. it('can invite member from team dropdown with access', async function () {
  102. const org = TestStubs.Organization({access: ['team:admin'], openMembership: false});
  103. const wrapper = mountWithTheme(
  104. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  105. routerContext
  106. );
  107. await tick();
  108. wrapper.update();
  109. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  110. wrapper
  111. .find('StyledCreateMemberLink[data-test-id="invite-member"]')
  112. .simulate('click');
  113. expect(openInviteMembersModal).toHaveBeenCalled();
  114. });
  115. it('can invite member from team dropdown with access and `Open Membership` enabled', async function () {
  116. const org = TestStubs.Organization({access: ['team:admin'], openMembership: true});
  117. const wrapper = mountWithTheme(
  118. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  119. routerContext
  120. );
  121. await tick();
  122. wrapper.update();
  123. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  124. wrapper
  125. .find('StyledCreateMemberLink[data-test-id="invite-member"]')
  126. .simulate('click');
  127. expect(openInviteMembersModal).toHaveBeenCalled();
  128. });
  129. it('can invite member from team dropdown without access and `Open Membership` enabled', async function () {
  130. const org = TestStubs.Organization({access: [], openMembership: true});
  131. const wrapper = mountWithTheme(
  132. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  133. routerContext
  134. );
  135. await tick();
  136. wrapper.update();
  137. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  138. wrapper
  139. .find('StyledCreateMemberLink[data-test-id="invite-member"]')
  140. .simulate('click');
  141. expect(openInviteMembersModal).toHaveBeenCalled();
  142. });
  143. it('can invite member from team dropdown without access and `Open Membership` disabled', async function () {
  144. const org = TestStubs.Organization({access: [], openMembership: false});
  145. const wrapper = mountWithTheme(
  146. <TeamMembers params={{orgId: org.slug, teamId: team.slug}} organization={org} />,
  147. routerContext
  148. );
  149. await tick();
  150. wrapper.update();
  151. wrapper.find('DropdownButton[data-test-id="add-member"]').simulate('click');
  152. wrapper
  153. .find('StyledCreateMemberLink[data-test-id="invite-member"]')
  154. .simulate('click');
  155. expect(openInviteMembersModal).toHaveBeenCalled();
  156. });
  157. it('can remove member from team', async function () {
  158. const deleteMock = Client.addMockResponse({
  159. url: `/organizations/${organization.slug}/members/${members[0].id}/teams/${team.slug}/`,
  160. method: 'DELETE',
  161. });
  162. const wrapper = mountWithTheme(
  163. <TeamMembers
  164. params={{orgId: organization.slug, teamId: team.slug}}
  165. organization={organization}
  166. />,
  167. routerContext
  168. );
  169. await tick();
  170. wrapper.update();
  171. expect(deleteMock).not.toHaveBeenCalled();
  172. wrapper.find('Button').at(1).simulate('click');
  173. expect(deleteMock).toHaveBeenCalled();
  174. });
  175. it('can only remove self from team', async function () {
  176. const me = TestStubs.Member({
  177. id: '123',
  178. email: 'foo@example.com',
  179. });
  180. Client.addMockResponse({
  181. url: `/teams/${organization.slug}/${team.slug}/members/`,
  182. method: 'GET',
  183. body: [...members, me],
  184. });
  185. const deleteMock = Client.addMockResponse({
  186. url: `/organizations/${organization.slug}/members/${me.id}/teams/${team.slug}/`,
  187. method: 'DELETE',
  188. });
  189. const organizationMember = TestStubs.Organization({
  190. access: [],
  191. });
  192. const wrapper = mountWithTheme(
  193. <TeamMembers
  194. params={{orgId: organization.slug, teamId: team.slug}}
  195. organization={organizationMember}
  196. />,
  197. routerContext
  198. );
  199. await tick();
  200. wrapper.update();
  201. expect(deleteMock).not.toHaveBeenCalled();
  202. expect(wrapper.find('IdBadge')).toHaveLength(members.length + 1);
  203. // Can only remove self
  204. expect(wrapper.find('button[aria-label="Remove"]')).toHaveLength(1);
  205. wrapper.find('button[aria-label="Remove"]').simulate('click');
  206. expect(deleteMock).toHaveBeenCalled();
  207. });
  208. });