You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AdvancedTimeline-test.tsx 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { shallow } from 'enzyme';
  21. import * as React from 'react';
  22. import { ThemeConsumer } from '../../theme';
  23. import AdvancedTimeline from '../AdvancedTimeline';
  24. const newCodeLegendClass = '.new-code-legend';
  25. // Replace scaleTime with scaleUtc to avoid timezone-dependent snapshots
  26. jest.mock('d3-scale', () => {
  27. const { scaleUtc, ...others } = jest.requireActual('d3-scale');
  28. return {
  29. ...others,
  30. scaleTime: scaleUtc,
  31. };
  32. });
  33. jest.mock('lodash', () => {
  34. const lodash = jest.requireActual('lodash');
  35. return { ...lodash, throttle: (f: any) => f };
  36. });
  37. it('should render correctly', () => {
  38. expect(shallowRender()).toMatchSnapshot();
  39. expect(shallowRender({ disableZoom: false, updateZoom: () => {} })).toMatchSnapshot(
  40. 'Zoom enabled'
  41. );
  42. expect(shallowRender({ formatYTick: (t) => `Nicer tick ${t}` })).toMatchSnapshot('format y tick');
  43. expect(shallowRender({ width: undefined })).toMatchSnapshot('no width');
  44. expect(shallowRender({ height: undefined })).toMatchSnapshot('no height');
  45. expect(shallowRender({ showAreas: undefined })).toMatchSnapshot('no areas');
  46. });
  47. it('should render leak correctly', () => {
  48. const wrapper = shallowRender({ leakPeriodDate: new Date('2019-10-02') });
  49. const leakNode = wrapper.find(ThemeConsumer).dive().find('.leak-chart-rect');
  50. expect(leakNode.exists()).toBe(true);
  51. expect(leakNode.getElement().props.width).toBe(15);
  52. });
  53. it('should render leak legend correctly', () => {
  54. const wrapper = shallowRender({
  55. displayNewCodeLegend: true,
  56. leakPeriodDate: new Date('2019-10-02'),
  57. });
  58. const leakNode = wrapper.find(ThemeConsumer).dive();
  59. expect(leakNode.find(newCodeLegendClass).exists()).toBe(true);
  60. expect(leakNode.find(newCodeLegendClass).props().textAnchor).toBe('start');
  61. expect(leakNode).toMatchSnapshot();
  62. });
  63. it('should render leak legend correctly for small leak', () => {
  64. const wrapper = shallowRender({
  65. displayNewCodeLegend: true,
  66. leakPeriodDate: new Date('2020-02-06'),
  67. series: [
  68. mockData(1, '2020-02-01'),
  69. mockData(2, '2020-02-02'),
  70. mockData(3, '2020-02-03'),
  71. mockData(4, '2020-02-04'),
  72. mockData(5, '2020-02-05'),
  73. mockData(6, '2020-02-06'),
  74. mockData(7, '2020-02-07'),
  75. ],
  76. });
  77. const leakNode = wrapper.find(ThemeConsumer).dive();
  78. expect(leakNode.find(newCodeLegendClass).exists()).toBe(true);
  79. expect(leakNode.find(newCodeLegendClass).props().textAnchor).toBe('end');
  80. });
  81. it('should set leakLegendTextWidth correctly', () => {
  82. const wrapper = shallowRender();
  83. wrapper.instance().setLeakLegendTextWidth({
  84. getBoundingClientRect: () => ({ width: 12 } as DOMRect),
  85. } as SVGTextElement);
  86. expect(wrapper.state().leakLegendTextWidth).toBe(12);
  87. wrapper.instance().setLeakLegendTextWidth(null);
  88. expect(wrapper.state().leakLegendTextWidth).toBe(12);
  89. });
  90. it('should render old leak correctly', () => {
  91. const wrapper = shallowRender({ leakPeriodDate: new Date('2014-10-02') });
  92. const leakNode = wrapper.find(ThemeConsumer).dive().find('.leak-chart-rect');
  93. expect(leakNode.exists()).toBe(true);
  94. expect(leakNode.getElement().props.width).toBe(30);
  95. });
  96. it('should find date to display based on mouse location', () => {
  97. const wrapper = shallowRender();
  98. wrapper.instance().updateTooltipPos(0);
  99. expect(wrapper.state().selectedDateIdx).toBeUndefined();
  100. wrapper.instance().handleMouseEnter();
  101. wrapper.instance().updateTooltipPos(10);
  102. expect(wrapper.state().selectedDateIdx).toBe(1);
  103. });
  104. it('should update timeline when width changes', () => {
  105. const updateTooltip = jest.fn();
  106. const wrapper = shallowRender({ selectedDate: new Date('2019-10-02'), updateTooltip });
  107. const { xScale, selectedDateXPos } = wrapper.state();
  108. wrapper.setProps({ width: 200 });
  109. expect(wrapper.state().xScale).not.toBe(xScale);
  110. expect(wrapper.state().xScale).toEqual(expect.any(Function));
  111. expect(wrapper.state().selectedDateXPos).not.toBe(selectedDateXPos);
  112. expect(wrapper.state().selectedDateXPos).toEqual(expect.any(Number));
  113. expect(updateTooltip).toBeCalled();
  114. });
  115. it('should update tootlips when selected date changes', () => {
  116. const updateTooltip = jest.fn();
  117. const wrapper = shallowRender({ selectedDate: new Date('2019-10-01'), updateTooltip });
  118. const { xScale, selectedDateXPos } = wrapper.state();
  119. const selectedDate = new Date('2019-10-02');
  120. wrapper.setProps({ selectedDate });
  121. expect(wrapper.state().xScale).toBe(xScale);
  122. expect(wrapper.state().selectedDate).toBe(selectedDate);
  123. expect(wrapper.state().selectedDateXPos).not.toBe(selectedDateXPos);
  124. expect(wrapper.state().selectedDateXPos).toEqual(expect.any(Number));
  125. expect(updateTooltip).toBeCalled();
  126. });
  127. it('should handle scroll correcly', () => {
  128. let updateZoom = jest.fn();
  129. let preventDefault = jest.fn();
  130. let wrapper = shallowRender({ updateZoom });
  131. wrapper.instance().handleWheel(({
  132. preventDefault,
  133. deltaX: 1,
  134. deltaY: -2,
  135. deltaZ: 0,
  136. pageX: 100,
  137. pageY: 1,
  138. currentTarget: ({
  139. getBoundingClientRect: () => ({
  140. bottom: 0,
  141. height: 100,
  142. width: 50,
  143. left: 0,
  144. right: 0,
  145. top: 10,
  146. x: 12,
  147. y: 23,
  148. toJSON: () => '',
  149. }),
  150. } as any) as SVGElement,
  151. } as any) as React.WheelEvent<SVGElement>);
  152. expect(preventDefault).toBeCalled();
  153. expect(updateZoom).toBeCalledWith(new Date('2019-10-01T06:24:00.000Z'), undefined);
  154. updateZoom = jest.fn();
  155. preventDefault = jest.fn();
  156. wrapper = shallowRender({ updateZoom });
  157. wrapper.instance().handleWheel(({
  158. preventDefault,
  159. deltaX: 1,
  160. deltaY: 2,
  161. deltaZ: 0,
  162. pageX: 100,
  163. pageY: 1,
  164. deltaMode: 25,
  165. currentTarget: ({
  166. getBoundingClientRect: () => ({
  167. bottom: 0,
  168. height: 100,
  169. width: 50,
  170. left: 0,
  171. right: 0,
  172. top: 10,
  173. x: 12,
  174. y: 23,
  175. toJSON: () => '',
  176. }),
  177. } as any) as SVGElement,
  178. } as any) as React.WheelEvent<SVGElement>);
  179. expect(preventDefault).toBeCalled();
  180. expect(updateZoom).toBeCalledWith(undefined, new Date('2019-10-02T20:48:00.000Z'));
  181. });
  182. it('should handle mouse out correcly', () => {
  183. const updateTooltip = jest.fn();
  184. const wrapper = shallowRender({ updateTooltip: undefined });
  185. wrapper.setState({
  186. mouseOver: true,
  187. selectedDate: new Date(),
  188. selectedDateXPos: 1,
  189. selectedDateIdx: 1,
  190. });
  191. wrapper.instance().handleMouseOut();
  192. expect(wrapper.state().mouseOver).toBe(true);
  193. wrapper.setProps({ updateTooltip });
  194. wrapper.instance().handleMouseOut();
  195. expect(wrapper.state().mouseOver).toBe(false);
  196. expect(wrapper.state().selectedDate).toBeUndefined();
  197. expect(wrapper.state().selectedDateXPos).toBeUndefined();
  198. expect(wrapper.state().selectedDateIdx).toBeUndefined();
  199. wrapper.instance().handleMouseOut();
  200. });
  201. it('should handle click correcly', () => {
  202. const updateSelectedDate = jest.fn();
  203. const wrapper = shallowRender({ updateSelectedDate });
  204. wrapper.setState({ selectedDate: new Date() });
  205. wrapper.instance().handleClick();
  206. expect(updateSelectedDate).toBeCalledWith(wrapper.state().selectedDate);
  207. wrapper.setProps({ updateSelectedDate: undefined });
  208. updateSelectedDate.mockClear();
  209. wrapper.instance().handleClick();
  210. expect(updateSelectedDate).not.toBeCalled();
  211. });
  212. function shallowRender(props?: Partial<AdvancedTimeline['props']>) {
  213. return shallow<AdvancedTimeline>(
  214. <AdvancedTimeline
  215. height={100}
  216. maxYTicksCount={10}
  217. metricType="TEST_METRIC"
  218. series={[
  219. {
  220. name: 'test-1',
  221. type: 'test-type-1',
  222. translatedName: '',
  223. data: [
  224. {
  225. x: new Date('2019-10-01'),
  226. y: 1,
  227. },
  228. {
  229. x: new Date('2019-10-02'),
  230. y: 2,
  231. },
  232. ],
  233. },
  234. {
  235. name: 'test-2',
  236. type: 'test-type-2',
  237. translatedName: '',
  238. data: [
  239. {
  240. x: new Date('2019-10-03'),
  241. y: 3,
  242. },
  243. ],
  244. },
  245. ]}
  246. width={100}
  247. zoomSpeed={1}
  248. {...props}
  249. />
  250. );
  251. }
  252. function mockData(i: number, date: string): T.Chart.Serie {
  253. return {
  254. name: `t${i}`,
  255. type: 'type',
  256. translatedName: '',
  257. data: [{ x: new Date(date), y: i }],
  258. };
  259. }