assigneeSelector.spec.jsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import React from 'react';
  2. import {AssigneeSelectorComponent} from 'app/components/assigneeSelector';
  3. import {Client} from 'app/api';
  4. import {mount} from 'enzyme';
  5. import ConfigStore from 'app/stores/configStore';
  6. import GroupStore from 'app/stores/groupStore';
  7. import MemberListStore from 'app/stores/memberListStore';
  8. import ProjectsStore from 'app/stores/projectsStore';
  9. import TeamStore from 'app/stores/teamStore';
  10. describe('AssigneeSelector', function() {
  11. let sandbox;
  12. let assigneeSelector;
  13. let assignMock;
  14. let openMenu;
  15. let USER_1, USER_2, USER_3;
  16. let TEAM_1;
  17. let PROJECT_1;
  18. let GROUP_1;
  19. beforeEach(function() {
  20. sandbox = sinon.sandbox.create();
  21. USER_1 = TestStubs.User({
  22. id: '1',
  23. name: 'Jane Doe',
  24. email: 'janedoe@example.com',
  25. });
  26. USER_2 = TestStubs.User({
  27. id: '2',
  28. name: 'John Smith',
  29. email: 'johnsmith@example.com',
  30. });
  31. USER_3 = TestStubs.User({
  32. id: '3',
  33. name: 'J J',
  34. email: 'jj@example.com',
  35. });
  36. TEAM_1 = TestStubs.Team({
  37. id: '3',
  38. name: 'COOL TEAM',
  39. slug: 'cool-team',
  40. });
  41. PROJECT_1 = TestStubs.Project({
  42. teams: [TEAM_1],
  43. });
  44. GROUP_1 = TestStubs.Group({
  45. id: '1337',
  46. project: {
  47. id: PROJECT_1.id,
  48. slug: PROJECT_1.slug,
  49. },
  50. });
  51. sandbox.stub(MemberListStore, 'getAll').returns(null);
  52. sandbox.stub(TeamStore, 'getAll').returns([TEAM_1]);
  53. sandbox.stub(ProjectsStore, 'getAll').returns([PROJECT_1]);
  54. sandbox.stub(GroupStore, 'get').returns(GROUP_1);
  55. assignMock = Client.addMockResponse({
  56. method: 'PUT',
  57. url: `/issues/${GROUP_1.id}/`,
  58. body: {
  59. ...GROUP_1,
  60. assignedTo: USER_1,
  61. },
  62. });
  63. MemberListStore.items = null;
  64. MemberListStore.loaded = false;
  65. assigneeSelector = mount(
  66. <AssigneeSelectorComponent id={GROUP_1.id} />,
  67. TestStubs.routerContext()
  68. );
  69. openMenu = () => assigneeSelector.find('DropdownButton').simulate('click');
  70. });
  71. afterEach(function() {
  72. sandbox.restore();
  73. Client.clearMockResponses();
  74. });
  75. describe('putSessionUserFirst()', function() {
  76. const putSessionUserFirst = AssigneeSelectorComponent.putSessionUserFirst;
  77. it('should place the session user at the top of the member list if present', function() {
  78. sandbox
  79. .stub(ConfigStore, 'get')
  80. .withArgs('user')
  81. .returns({
  82. id: '2',
  83. name: 'John Smith',
  84. email: 'johnsmith@example.com',
  85. });
  86. expect(putSessionUserFirst([USER_1, USER_2])).toEqual([USER_2, USER_1]);
  87. });
  88. it("should return the same member list if the session user isn't present", function() {
  89. sandbox
  90. .stub(ConfigStore, 'get')
  91. .withArgs('user')
  92. .returns({
  93. id: '555',
  94. name: 'Here Comes a New Challenger',
  95. email: 'guile@mail.us.af.mil',
  96. });
  97. expect(putSessionUserFirst([USER_1, USER_2])).toEqual([USER_1, USER_2]);
  98. });
  99. });
  100. it('should initially have loading state', function() {
  101. openMenu();
  102. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(1);
  103. });
  104. it('does not have loading state and shows member list after calling MemberListStore.loadInitialData', async function() {
  105. openMenu();
  106. MemberListStore.loadInitialData([USER_1, USER_2]);
  107. assigneeSelector.update();
  108. expect(assigneeSelector.instance().assignableTeams()).toHaveLength(1);
  109. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(0);
  110. expect(assigneeSelector.find('Avatar')).toHaveLength(3);
  111. expect(assigneeSelector.find('UserAvatar')).toHaveLength(2);
  112. expect(assigneeSelector.find('TeamAvatar')).toHaveLength(1);
  113. });
  114. it('does NOT update member list after initial load', function() {
  115. openMenu();
  116. MemberListStore.loadInitialData([USER_1, USER_2]);
  117. assigneeSelector.update();
  118. expect(assigneeSelector.find('Avatar')).toHaveLength(3);
  119. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  120. MemberListStore.loadInitialData([USER_1, USER_2, USER_3]);
  121. assigneeSelector.update();
  122. expect(assigneeSelector.find('Avatar')).toHaveLength(3);
  123. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  124. });
  125. it('successfully assigns users', async function() {
  126. openMenu();
  127. MemberListStore.loadInitialData([USER_1, USER_2]);
  128. assigneeSelector.update();
  129. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  130. assigneeSelector
  131. .find('UserAvatar')
  132. .first()
  133. .simulate('click');
  134. expect(assignMock).toHaveBeenLastCalledWith(
  135. '/issues/1337/',
  136. expect.objectContaining({
  137. data: {assignedTo: 'user:1'},
  138. })
  139. );
  140. assigneeSelector.update();
  141. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(1);
  142. // Flakey with 1 tick
  143. await tick();
  144. await tick();
  145. assigneeSelector.update();
  146. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(0);
  147. expect(assigneeSelector.find('ActorAvatar')).toHaveLength(1);
  148. });
  149. it('successfully assigns teams', async function() {
  150. openMenu();
  151. MemberListStore.loadInitialData([USER_1, USER_2]);
  152. assigneeSelector.update();
  153. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  154. assigneeSelector
  155. .find('TeamAvatar')
  156. .first()
  157. .simulate('click');
  158. assigneeSelector.update();
  159. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(true);
  160. expect(assignMock).toHaveBeenCalledWith(
  161. '/issues/1337/',
  162. expect.objectContaining({
  163. data: {assignedTo: 'team:3'},
  164. })
  165. );
  166. // Flakey with 1 tick
  167. await tick();
  168. await tick();
  169. assigneeSelector.update();
  170. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  171. expect(assigneeSelector.find('ActorAvatar')).toHaveLength(1);
  172. });
  173. it('successfully clears assignment', async function() {
  174. openMenu();
  175. MemberListStore.loadInitialData([USER_1, USER_2]);
  176. // Assign first item in list, which is TEAM_1
  177. assigneeSelector.update();
  178. assigneeSelector
  179. .find('Avatar')
  180. .first()
  181. .simulate('click');
  182. assigneeSelector.update();
  183. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(true);
  184. expect(assignMock).toHaveBeenCalledWith(
  185. '/issues/1337/',
  186. expect.objectContaining({
  187. data: {assignedTo: 'team:3'},
  188. })
  189. );
  190. // Waiting for assignment to finish updating
  191. // Flakey with 1 tick
  192. await tick();
  193. await tick();
  194. assigneeSelector.update();
  195. openMenu();
  196. assigneeSelector
  197. .find('MenuItemWrapper[data-test-id="clear-assignee"]')
  198. .simulate('click');
  199. // api was called with empty string, clearing assignment
  200. expect(assignMock).toHaveBeenLastCalledWith(
  201. '/issues/1337/',
  202. expect.objectContaining({
  203. data: {assignedTo: ''},
  204. })
  205. );
  206. });
  207. it('shows invite member button', async function() {
  208. let routerContext = TestStubs.routerContext();
  209. openMenu();
  210. MemberListStore.loadInitialData([USER_1, USER_2]);
  211. assigneeSelector.update();
  212. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  213. expect(
  214. assigneeSelector.find('InviteMemberLink[data-test-id="invite-member"]')
  215. ).toHaveLength(0);
  216. assigneeSelector.unmount();
  217. sandbox
  218. .stub(ConfigStore, 'get')
  219. .withArgs('invitesEnabled')
  220. .returns(true);
  221. assigneeSelector = mount(
  222. <AssigneeSelectorComponent id={GROUP_1.id} />,
  223. routerContext
  224. );
  225. await tick();
  226. assigneeSelector.update();
  227. openMenu();
  228. expect(
  229. assigneeSelector.find('InviteMemberLink[data-test-id="invite-member"]')
  230. ).toHaveLength(1);
  231. });
  232. it('requires org:write to invite member', async function() {
  233. MemberListStore.loadInitialData([USER_1, USER_2]);
  234. sandbox
  235. .stub(ConfigStore, 'get')
  236. .withArgs('invitesEnabled')
  237. .returns(true);
  238. // Remove org:write access permission and make sure invite member button is not shown.
  239. assigneeSelector.unmount();
  240. assigneeSelector = mount(
  241. <AssigneeSelectorComponent id={GROUP_1.id} />,
  242. TestStubs.routerContext([{organization: TestStubs.Organization({access: []})}])
  243. );
  244. openMenu();
  245. assigneeSelector.update();
  246. expect(
  247. assigneeSelector.find('InviteMemberLink[data-test-id="invite-member"]')
  248. ).toHaveLength(0);
  249. });
  250. it('filters user by email and selects with keyboard', async function() {
  251. openMenu();
  252. MemberListStore.loadInitialData([USER_1, USER_2]);
  253. assigneeSelector.update();
  254. expect(assigneeSelector.find('LoadingIndicator').exists()).toBe(false);
  255. assigneeSelector
  256. .find('StyledInput')
  257. .simulate('change', {target: {value: 'JohnSmith@example.com'}});
  258. expect(assigneeSelector.find('Avatar')).toHaveLength(1);
  259. expect(assigneeSelector.find('Avatar').prop('user')).toEqual(USER_2);
  260. assigneeSelector.find('StyledInput').simulate('keyDown', {key: 'Enter'});
  261. assigneeSelector.update();
  262. expect(assignMock).toHaveBeenLastCalledWith(
  263. '/issues/1337/',
  264. expect.objectContaining({
  265. data: {assignedTo: 'user:2'},
  266. })
  267. );
  268. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(1);
  269. await tick();
  270. await tick();
  271. assigneeSelector.update();
  272. expect(assigneeSelector.find('LoadingIndicator')).toHaveLength(0);
  273. expect(assigneeSelector.find('ActorAvatar')).toHaveLength(1);
  274. });
  275. });