index.spec.jsx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import React from 'react';
  2. import {mount, shallow} from 'enzyme';
  3. import IncidentStore from 'app/stores/incidentStore';
  4. import ConfigStore from 'app/stores/configStore';
  5. import SidebarContainer, {Sidebar} from 'app/components/sidebar';
  6. describe('Sidebar', function() {
  7. let wrapper;
  8. let routerContext = TestStubs.routerContext();
  9. let {organization, router} = routerContext.context;
  10. let user = TestStubs.User();
  11. let apiMocks = {};
  12. let createWrapper = props =>
  13. mount(
  14. <Sidebar
  15. organization={organization}
  16. user={user}
  17. router={router}
  18. location={router.location}
  19. {...props}
  20. />,
  21. routerContext
  22. );
  23. beforeEach(function() {
  24. apiMocks.broadcasts = MockApiClient.addMockResponse({
  25. url: '/broadcasts/',
  26. body: [TestStubs.Broadcast()],
  27. });
  28. apiMocks.broadcastsMarkAsSeen = MockApiClient.addMockResponse({
  29. url: '/broadcasts/',
  30. method: 'PUT',
  31. });
  32. });
  33. it('renders', function() {
  34. wrapper = shallow(
  35. <Sidebar organization={organization} user={user} router={router} />,
  36. TestStubs.routerContext()
  37. );
  38. expect(wrapper.find('StyledSidebar')).toHaveLength(1);
  39. });
  40. it('renders without org and router', function() {
  41. wrapper = createWrapper({
  42. organization: null,
  43. router: null,
  44. });
  45. // no org displays user details
  46. expect(wrapper.find('OrgOrUserName').text()).toContain(user.name);
  47. expect(wrapper.find('UserNameOrEmail').text()).toContain(user.email);
  48. wrapper.find('SidebarDropdownActor').simulate('click');
  49. expect(wrapper.find('OrgAndUserMenu')).toMatchSnapshot();
  50. });
  51. it('can toggle collapsed state', async function() {
  52. wrapper = mount(
  53. <SidebarContainer organization={organization} user={user} router={router} />,
  54. routerContext
  55. );
  56. expect(wrapper.find('OrgOrUserName').text()).toContain(organization.name);
  57. expect(wrapper.find('UserNameOrEmail').text()).toContain(user.name);
  58. wrapper.find('SidebarCollapseItem').simulate('click');
  59. await tick();
  60. wrapper.update();
  61. // Because of HoCs, we can't access the collapsed prop
  62. // Instead check for `SidebarItemLabel` which doesn't exist in collapsed state
  63. expect(wrapper.find('SidebarItemLabel')).toHaveLength(0);
  64. wrapper.find('SidebarCollapseItem').simulate('click');
  65. await tick();
  66. wrapper.update();
  67. expect(wrapper.find('SidebarItemLabel').length).toBeGreaterThan(0);
  68. });
  69. it('can have onboarding feature', function() {
  70. wrapper = mount(
  71. <SidebarContainer
  72. organization={{...organization, features: ['onboarding']}}
  73. user={user}
  74. router={router}
  75. />,
  76. routerContext
  77. );
  78. expect(wrapper.find('[data-test-id="onboarding-progress-bar"]')).toHaveLength(1);
  79. wrapper.find('[data-test-id="onboarding-progress-bar"]').simulate('click');
  80. wrapper.update();
  81. expect(wrapper.find('OnboardingStatus SidebarPanel')).toMatchSnapshot();
  82. });
  83. describe('SidebarHelp', function() {
  84. it('can toggle help menu', function() {
  85. wrapper = createWrapper();
  86. wrapper.find('HelpActor').simulate('click');
  87. let menu = wrapper.find('HelpMenu');
  88. expect(menu).toHaveLength(1);
  89. expect(menu).toMatchSnapshot();
  90. expect(menu.find('SidebarMenuItem')).toHaveLength(3);
  91. wrapper.find('HelpActor').simulate('click');
  92. expect(wrapper.find('HelpMenu')).toHaveLength(0);
  93. });
  94. });
  95. describe('SidebarDropdown', function() {
  96. it('can open Sidebar org/name dropdown menu', function() {
  97. wrapper = createWrapper();
  98. wrapper.find('SidebarDropdownActor').simulate('click');
  99. expect(wrapper.find('OrgAndUserMenu')).toHaveLength(1);
  100. expect(wrapper.find('OrgAndUserMenu')).toMatchSnapshot();
  101. });
  102. it('has link to Members settings with `member:write`', function() {
  103. let org = TestStubs.Organization();
  104. org = {
  105. ...org,
  106. access: [...org.access, 'member:read'],
  107. };
  108. wrapper = createWrapper({
  109. organization: org,
  110. });
  111. wrapper.find('SidebarDropdownActor').simulate('click');
  112. expect(wrapper.find('OrgAndUserMenu')).toHaveLength(1);
  113. expect(
  114. wrapper.find('SidebarMenuItem[to="/settings/org-slug/members/"]')
  115. ).toHaveLength(1);
  116. });
  117. it('can open "Switch Organization" sub-menu', function() {
  118. ConfigStore.set('features', new Set(['organizations:create']));
  119. jest.useFakeTimers();
  120. wrapper = createWrapper();
  121. wrapper.find('SidebarDropdownActor').simulate('click');
  122. wrapper.find('SwitchOrganizationMenuActor').simulate('mouseEnter');
  123. jest.advanceTimersByTime(500);
  124. wrapper.update();
  125. expect(wrapper.find('SwitchOrganizationMenu')).toHaveLength(1);
  126. expect(wrapper.find('SwitchOrganizationMenu')).toMatchSnapshot();
  127. jest.useRealTimers();
  128. });
  129. it('has can logout', function() {
  130. let mock = MockApiClient.addMockResponse({
  131. url: '/auth/',
  132. method: 'DELETE',
  133. status: 204,
  134. });
  135. let org = TestStubs.Organization();
  136. org = {
  137. ...org,
  138. access: [...org.access, 'member:read'],
  139. };
  140. wrapper = createWrapper({
  141. organization: org,
  142. user: TestStubs.User(),
  143. });
  144. wrapper.find('SidebarDropdownActor').simulate('click');
  145. wrapper.find('SidebarMenuItem[data-test-id="sidebarSignout"]').simulate('click');
  146. expect(mock).toHaveBeenCalled();
  147. });
  148. });
  149. describe('SidebarPanel', function() {
  150. it('displays empty panel when there are no Broadcasts', async function() {
  151. MockApiClient.addMockResponse({
  152. url: '/broadcasts/',
  153. body: [],
  154. });
  155. wrapper = createWrapper();
  156. await wrapper.find('Broadcasts SidebarItem').simulate('click');
  157. wrapper.update();
  158. expect(wrapper.find('SidebarPanel')).toHaveLength(1);
  159. expect(wrapper.find('SidebarPanelItem')).toHaveLength(0);
  160. expect(wrapper.find('SidebarPanelEmpty')).toHaveLength(1);
  161. });
  162. it('can display Broadcasts panel and mark as seen', async function() {
  163. jest.useFakeTimers();
  164. wrapper = createWrapper();
  165. expect(apiMocks.broadcasts).toHaveBeenCalled();
  166. await wrapper.find('Broadcasts SidebarItem').simulate('click');
  167. wrapper.update();
  168. expect(wrapper.find('SidebarPanel')).toHaveLength(1);
  169. expect(wrapper.find('SidebarPanelItem')).toHaveLength(1);
  170. expect(wrapper.find('SidebarPanelItem').prop('hasSeen')).toBe(false);
  171. expect(wrapper.find('SidebarPanelItem')).toMatchSnapshot();
  172. // Should mark as seen after a delay
  173. jest.advanceTimersByTime(2000);
  174. expect(apiMocks.broadcastsMarkAsSeen).toHaveBeenCalledWith(
  175. '/broadcasts/',
  176. expect.objectContaining({
  177. data: {
  178. hasSeen: '1',
  179. },
  180. query: {
  181. id: ['8'],
  182. },
  183. })
  184. );
  185. jest.useRealTimers();
  186. });
  187. it('can toggle display of Broadcasts SidebarPanel', function() {
  188. wrapper = createWrapper();
  189. // Show Broadcasts Panel
  190. wrapper.find('Broadcasts SidebarItem').simulate('click');
  191. wrapper.update();
  192. expect(wrapper.find('SidebarPanel')).toHaveLength(1);
  193. // Hide Broadcasts Panel
  194. wrapper.find('Broadcasts SidebarItem').simulate('click');
  195. wrapper.update();
  196. expect(wrapper.find('SidebarPanel')).toHaveLength(0);
  197. });
  198. it('can unmount Sidebar (and Broadcasts) and kills Broadcast timers', async function() {
  199. jest.useFakeTimers();
  200. wrapper = createWrapper();
  201. let broadcasts = wrapper.find('Broadcasts').instance();
  202. // This will start timer to mark as seen
  203. await wrapper.find('Broadcasts SidebarItem').simulate('click');
  204. wrapper.update();
  205. jest.advanceTimersByTime(500);
  206. expect(broadcasts.poller).toBeDefined();
  207. expect(broadcasts.timer).toBeDefined();
  208. // Unmounting will cancel timers
  209. wrapper.unmount();
  210. expect(broadcasts.poller).toBe(null);
  211. expect(broadcasts.timer).toBe(null);
  212. // This advances timers enough so that mark as seen should be called if it wasn't unmounted
  213. jest.advanceTimersByTime(600);
  214. expect(apiMocks.broadcastsMarkAsSeen).not.toHaveBeenCalled();
  215. });
  216. it('can show Incidents in Sidebar Panel', async function() {
  217. wrapper = createWrapper();
  218. IncidentStore.onUpdateSuccess({
  219. status: {incidents: [TestStubs.Incident()]},
  220. });
  221. wrapper.update();
  222. wrapper.find('Incidents').simulate('click');
  223. wrapper.update();
  224. expect(wrapper.find('SidebarPanel')).toHaveLength(1);
  225. expect(wrapper.find('IncidentList')).toMatchSnapshot();
  226. });
  227. it('hides when path changes', async function() {
  228. wrapper = createWrapper();
  229. wrapper.update();
  230. wrapper.find('Broadcasts SidebarItem').simulate('click');
  231. wrapper.update();
  232. expect(wrapper.find('SidebarPanel')).toHaveLength(1);
  233. wrapper.setProps({
  234. location: {
  235. pathname: 'new-path-name',
  236. },
  237. });
  238. wrapper.update();
  239. expect(wrapper.find('SidebarPanel')).toHaveLength(0);
  240. });
  241. it('hides assigned, bookmarks, history, activity and stats for sentry10', function() {
  242. const sentry10Org = TestStubs.Organization({features: ['sentry10']});
  243. wrapper = createWrapper();
  244. wrapper.setProps({organization: sentry10Org});
  245. wrapper.update();
  246. const labels = wrapper.find('SidebarItemLabel').map(node => node.text());
  247. expect(labels).toHaveLength(10);
  248. expect(labels).not.toContain('Assigned to me');
  249. expect(labels).not.toContain('Bookmarked issues');
  250. expect(labels).not.toContain('Recently viewed');
  251. });
  252. });
  253. });