index.spec.jsx 9.3 KB

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