index.spec.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import TimeRangeSelector from 'sentry/components/organizations/timeRangeSelector';
  3. import ConfigStore from 'sentry/stores/configStore';
  4. describe('TimeRangeSelector', function () {
  5. const onChange = jest.fn();
  6. const routerContext = TestStubs.routerContext();
  7. const organization = TestStubs.Organization();
  8. function getComponent(props = {}) {
  9. return (
  10. <TimeRangeSelector
  11. showAbsolute
  12. showRelative
  13. onChange={onChange}
  14. organization={organization}
  15. {...props}
  16. />
  17. );
  18. }
  19. function renderComponent(props = {}) {
  20. return render(getComponent(props), {context: routerContext});
  21. }
  22. beforeEach(function () {
  23. ConfigStore.loadInitialData({
  24. user: {options: {timezone: 'America/New_York'}},
  25. });
  26. onChange.mockReset();
  27. });
  28. it('renders when given relative period not in dropdown', function () {
  29. render(
  30. <TimeRangeSelector
  31. organization={organization}
  32. showAbsolute={false}
  33. showRelative={false}
  34. relative="9d"
  35. />,
  36. {context: routerContext}
  37. );
  38. expect(screen.getByText('Last 9 days')).toBeInTheDocument();
  39. });
  40. it('renders when given an invalid relative period', function () {
  41. render(
  42. <TimeRangeSelector
  43. organization={organization}
  44. showAbsolute={false}
  45. showRelative={false}
  46. relative="1y"
  47. />,
  48. {context: routerContext}
  49. );
  50. expect(screen.getByText('Invalid period')).toBeInTheDocument();
  51. });
  52. it('hides relative and absolute selectors', function () {
  53. render(
  54. <TimeRangeSelector
  55. organization={organization}
  56. showAbsolute={false}
  57. showRelative={false}
  58. />,
  59. {context: routerContext}
  60. );
  61. userEvent.click(screen.getByRole('button'));
  62. // Ensure none of the relative options are shown
  63. expect(screen.queryByTestId('1h')).not.toBeInTheDocument();
  64. expect(screen.queryByTestId('24h')).not.toBeInTheDocument();
  65. expect(screen.queryByTestId('7d')).not.toBeInTheDocument();
  66. expect(screen.queryByTestId('14d')).not.toBeInTheDocument();
  67. expect(screen.queryByTestId('30d')).not.toBeInTheDocument();
  68. expect(screen.queryByTestId('90d')).not.toBeInTheDocument();
  69. // Ensure absolute option not shown
  70. expect(screen.queryByTestId('absolute')).not.toBeInTheDocument();
  71. });
  72. it('does not open selector menu when disabled', function () {
  73. renderComponent({disabled: true});
  74. userEvent.click(screen.getByRole('button'));
  75. // Dropdown not open
  76. expect(screen.queryByText(/last hour/i)).not.toBeInTheDocument();
  77. });
  78. it('selects absolute item', async function () {
  79. renderComponent();
  80. userEvent.click(screen.getByRole('button'));
  81. expect(screen.queryByTestId('date-range')).not.toBeInTheDocument();
  82. userEvent.click(await screen.findByTestId('absolute'));
  83. const newProps = {
  84. relative: null,
  85. start: new Date('2017-10-03T02:41:20.000Z'),
  86. end: new Date('2017-10-17T02:41:20.000Z'),
  87. };
  88. expect(onChange).toHaveBeenLastCalledWith(newProps);
  89. expect(await screen.findByTestId('date-range')).toBeInTheDocument();
  90. });
  91. it('selects absolute item with utc enabled', async function () {
  92. renderComponent({utc: true});
  93. userEvent.click(screen.getByRole('button'));
  94. expect(screen.queryByTestId('date-range')).not.toBeInTheDocument();
  95. userEvent.click(await screen.findByTestId('absolute'));
  96. const newProps = {
  97. relative: null,
  98. start: new Date('2017-10-02T22:41:20.000Z'),
  99. end: new Date('2017-10-16T22:41:20.000Z'),
  100. utc: true,
  101. };
  102. expect(onChange).toHaveBeenLastCalledWith(newProps);
  103. expect(await screen.findByTestId('date-range')).toBeInTheDocument();
  104. });
  105. it('switches from relative to absolute while maintaining equivalent date range', async function () {
  106. const {rerender} = renderComponent({
  107. relative: '7d',
  108. utc: false,
  109. });
  110. userEvent.click(screen.getByRole('button'));
  111. userEvent.click(await screen.findByTestId('absolute'));
  112. expect(onChange).toHaveBeenCalledWith({
  113. relative: null,
  114. start: new Date('2017-10-10T02:41:20.000Z'),
  115. end: new Date('2017-10-17T02:41:20.000Z'),
  116. utc: false,
  117. });
  118. userEvent.click(screen.getByTestId('14d'));
  119. expect(onChange).toHaveBeenLastCalledWith({
  120. relative: '14d',
  121. start: undefined,
  122. end: undefined,
  123. });
  124. rerender(
  125. getComponent({
  126. relative: '14d',
  127. utc: false,
  128. })
  129. );
  130. userEvent.click(screen.getByRole('button'));
  131. userEvent.click(await screen.findByTestId('absolute'));
  132. expect(onChange).toHaveBeenLastCalledWith({
  133. relative: null,
  134. start: new Date('2017-10-03T02:41:20.000Z'),
  135. end: new Date('2017-10-17T02:41:20.000Z'),
  136. utc: false,
  137. });
  138. });
  139. it('switches from relative to absolute while maintaining equivalent date range (in utc)', async function () {
  140. const {rerender} = renderComponent({
  141. relative: '7d',
  142. utc: true,
  143. });
  144. userEvent.click(screen.getByRole('button'));
  145. userEvent.click(await screen.findByTestId('absolute'));
  146. expect(onChange).toHaveBeenCalledWith({
  147. relative: null,
  148. start: new Date('2017-10-09T22:41:20.000Z'),
  149. end: new Date('2017-10-16T22:41:20.000Z'),
  150. utc: true,
  151. });
  152. userEvent.click(screen.getByTestId('14d'));
  153. expect(onChange).toHaveBeenLastCalledWith({
  154. relative: '14d',
  155. start: undefined,
  156. end: undefined,
  157. });
  158. rerender(
  159. getComponent({
  160. relative: '14d',
  161. utc: true,
  162. })
  163. );
  164. userEvent.click(screen.getByRole('button'));
  165. userEvent.click(await screen.findByTestId('absolute'));
  166. expect(onChange).toHaveBeenLastCalledWith({
  167. relative: null,
  168. start: new Date('2017-10-02T22:41:20.000Z'),
  169. end: new Date('2017-10-16T22:41:20.000Z'),
  170. utc: true,
  171. });
  172. });
  173. it('switches from relative to absolute and then toggling UTC (starting with UTC)', async function () {
  174. renderComponent({
  175. relative: '7d',
  176. utc: true,
  177. });
  178. userEvent.click(screen.getByRole('button'));
  179. // Local time is 22:41:20-0500 -- this is what date picker should show
  180. userEvent.click(await screen.findByTestId('absolute'));
  181. expect(onChange).toHaveBeenCalledWith({
  182. relative: null,
  183. start: new Date('2017-10-09T22:41:20.000Z'),
  184. end: new Date('2017-10-16T22:41:20.000Z'),
  185. utc: true,
  186. });
  187. userEvent.click(screen.getByRole('checkbox'));
  188. expect(onChange).toHaveBeenLastCalledWith({
  189. relative: null,
  190. start: new Date('2017-10-09T22:41:20.000Z'),
  191. end: new Date('2017-10-16T22:41:20.000Z'),
  192. utc: false,
  193. });
  194. userEvent.click(screen.getByRole('checkbox'));
  195. expect(onChange).toHaveBeenLastCalledWith({
  196. relative: null,
  197. start: new Date('2017-10-10T02:41:20.000Z'),
  198. end: new Date('2017-10-17T02:41:20.000Z'),
  199. utc: true,
  200. });
  201. });
  202. it('switches from relative to absolute and then toggling UTC (starting with non-UTC)', async function () {
  203. renderComponent({
  204. relative: '7d',
  205. utc: false,
  206. });
  207. userEvent.click(screen.getByRole('button'));
  208. userEvent.click(await screen.findByTestId('absolute'));
  209. expect(onChange).toHaveBeenCalledWith({
  210. relative: null,
  211. start: new Date('2017-10-09T22:41:20.000-0400'),
  212. end: new Date('2017-10-16T22:41:20.000-0400'),
  213. utc: false,
  214. });
  215. userEvent.click(screen.getByRole('checkbox'));
  216. expect(onChange).toHaveBeenLastCalledWith({
  217. relative: null,
  218. start: new Date('2017-10-10T02:41:20.000Z'),
  219. end: new Date('2017-10-17T02:41:20.000Z'),
  220. utc: true,
  221. });
  222. userEvent.click(screen.getByRole('checkbox'));
  223. expect(onChange).toHaveBeenLastCalledWith({
  224. relative: null,
  225. start: new Date('2017-10-09T22:41:20.000Z'),
  226. end: new Date('2017-10-16T22:41:20.000Z'),
  227. utc: false,
  228. });
  229. });
  230. it('maintains time when switching UTC to local time', function () {
  231. // Times should never change when changing UTC option
  232. // Instead, the utc flagged is used when querying to create proper date
  233. let state;
  234. const {rerender} = renderComponent({
  235. relative: null,
  236. start: new Date('2017-10-10T00:00:00.000Z'),
  237. end: new Date('2017-10-17T23:59:59.000Z'),
  238. utc: true,
  239. });
  240. userEvent.click(screen.getByRole('button'));
  241. // Local
  242. userEvent.click(screen.getByRole('checkbox'));
  243. state = {
  244. relative: null,
  245. start: new Date('2017-10-10T00:00:00.000Z'),
  246. end: new Date('2017-10-17T23:59:59.000Z'),
  247. utc: false,
  248. };
  249. expect(onChange).toHaveBeenLastCalledWith(state);
  250. rerender(getComponent(state));
  251. // UTC
  252. userEvent.click(screen.getByRole('checkbox'));
  253. state = {
  254. relative: null,
  255. start: new Date('2017-10-10T00:00:00.000Z'),
  256. end: new Date('2017-10-17T23:59:59.000Z'),
  257. utc: true,
  258. };
  259. expect(onChange).toHaveBeenLastCalledWith(state);
  260. rerender(getComponent(state));
  261. // Local
  262. userEvent.click(screen.getByRole('checkbox'));
  263. expect(onChange).toHaveBeenLastCalledWith({
  264. relative: null,
  265. start: new Date('2017-10-10T00:00:00.000Z'),
  266. end: new Date('2017-10-17T23:59:59.000Z'),
  267. utc: false,
  268. });
  269. });
  270. it('deselects default filter when absolute date selected', async function () {
  271. renderComponent({
  272. relative: '14d',
  273. utc: false,
  274. });
  275. userEvent.click(screen.getByRole('button'));
  276. userEvent.click(await screen.findByTestId('absolute'));
  277. });
  278. it('uses the default absolute date', async function () {
  279. renderComponent({
  280. defaultAbsolute: {
  281. start: new Date('2017-10-10T00:00:00.000Z'),
  282. end: new Date('2017-10-17T23:59:59.000Z'),
  283. },
  284. });
  285. userEvent.click(screen.getByRole('button'));
  286. userEvent.click(await screen.findByTestId('absolute'));
  287. expect(onChange).toHaveBeenCalledWith({
  288. relative: null,
  289. start: new Date('2017-10-10T00:00:00.000Z'),
  290. end: new Date('2017-10-17T23:59:59.000Z'),
  291. });
  292. });
  293. it('uses the current absolute date if provided', async function () {
  294. renderComponent({
  295. start: new Date('2022-06-12T00:00:00.000Z'),
  296. end: new Date('2022-06-14T00:00:00.000Z'),
  297. });
  298. userEvent.click(screen.getByRole('button'));
  299. userEvent.click(await screen.findByTestId('absolute'));
  300. // On change should not be called because start/end did not change
  301. expect(onChange).not.toHaveBeenCalled();
  302. });
  303. it('can select arbitrary relative time ranges', () => {
  304. renderComponent();
  305. userEvent.click(screen.getByRole('button'));
  306. const input = screen.getByRole('textbox');
  307. userEvent.type(input, '5');
  308. // With just the number "5", all unit options should be present
  309. expect(screen.getByText('Last 5 seconds')).toBeInTheDocument();
  310. expect(screen.getByText('Last 5 minutes')).toBeInTheDocument();
  311. expect(screen.getByText('Last 5 hours')).toBeInTheDocument();
  312. expect(screen.getByText('Last 5 days')).toBeInTheDocument();
  313. expect(screen.getByText('Last 5 weeks')).toBeInTheDocument();
  314. userEvent.type(input, 'd');
  315. // With "5d", only "Last 5 days" should be shown
  316. expect(screen.getByText('Last 5 days')).toBeInTheDocument();
  317. expect(screen.queryByText('Last 5 seconds')).not.toBeInTheDocument();
  318. expect(screen.queryByText('Last 5 minutes')).not.toBeInTheDocument();
  319. expect(screen.queryByText('Last 5 hours')).not.toBeInTheDocument();
  320. expect(screen.queryByText('Last 5 weeks')).not.toBeInTheDocument();
  321. userEvent.type(input, 'ays');
  322. // "5days" Should still show "Last 5 days" option
  323. expect(screen.getByText('Last 5 days')).toBeInTheDocument();
  324. userEvent.type(input, '{Enter}');
  325. expect(onChange).toHaveBeenLastCalledWith({
  326. relative: '5d',
  327. start: undefined,
  328. end: undefined,
  329. });
  330. });
  331. it('cannot select arbitrary relative time ranges with disallowArbitraryRelativeRanges', () => {
  332. renderComponent({disallowArbitraryRelativeRanges: true});
  333. userEvent.click(screen.getByRole('button'));
  334. const input = screen.getByRole('textbox');
  335. userEvent.type(input, '5');
  336. expect(screen.getByText('No items found')).toBeInTheDocument();
  337. userEvent.type(input, '{Enter}');
  338. expect(onChange).not.toHaveBeenCalled();
  339. });
  340. });