import { render, waitFor } from '@testing-library/react'; import { dateTime, EventBusSrv } from '@grafana/data'; import { getAppEvents } from '@grafana/runtime'; import { AbsoluteTimeEvent, CopyTimeEvent, PasteTimeEvent, ShiftTimeEvent, ShiftTimeEventDirection, ZoomOutEvent, } from 'app/types/events'; import { TestProvider } from '../../../../test/helpers/TestProvider'; import { configureStore } from '../../../store/configureStore'; import { initialExploreState } from '../state/main'; import { makeExplorePaneState } from '../state/utils'; import { useKeyboardShortcuts } from './useKeyboardShortcuts'; const testEventBus = new EventBusSrv(); jest.mock('@grafana/runtime', () => { return { ...jest.requireActual('@grafana/runtime'), reportInteraction: jest.fn(), getAppEvents: () => testEventBus, }; }); const mockClipboard = { writeText: jest.fn(), readText: jest.fn(), }; Object.defineProperty(global.navigator, 'clipboard', { value: mockClipboard, }); const NOW = new Date('2020-10-10T00:00:00.000Z'); function daysFromNow(daysDiff: number) { return new Date(NOW.getTime() + daysDiff * 86400000); } function setup() { const store = configureStore({ explore: { ...initialExploreState, panes: { left: makeExplorePaneState({ range: { from: dateTime(), to: dateTime(), raw: { from: 'now-1d', to: 'now' }, }, }), right: makeExplorePaneState({ range: { from: dateTime(), to: dateTime(), raw: { from: 'now-2d', to: 'now' }, }, }), }, }, }); const Wrapper = () => { useKeyboardShortcuts(); return
; }; render( ); return store; } describe('useKeyboardShortcuts', () => { beforeEach(() => { jest.useFakeTimers().setSystemTime(NOW); }); afterEach(() => { jest.useRealTimers(); }); it('changes both panes to absolute time range', () => { const store = setup(); getAppEvents().publish(new AbsoluteTimeEvent({ updateUrl: false })); const exploreState = store.getState().explore; const panes = Object.values(exploreState.panes); expect(panes[0]!.absoluteRange.from).toBe(daysFromNow(-1).getTime()); expect(panes[0]!.absoluteRange.to).toBe(daysFromNow(0).getTime()); expect(panes[1]!.absoluteRange.from).toBe(daysFromNow(-2).getTime()); expect(panes[1]!.absoluteRange.to).toBe(daysFromNow(0).getTime()); }); it('shifts time range in both panes', () => { const store = setup(); getAppEvents().publish(new ShiftTimeEvent({ direction: ShiftTimeEventDirection.Left })); const exploreState = store.getState().explore; const panes = Object.values(exploreState.panes); expect(panes[0]!.absoluteRange.from).toBe(daysFromNow(-1.5).getTime()); expect(panes[0]!.absoluteRange.to).toBe(daysFromNow(-0.5).getTime()); expect(panes[1]!.absoluteRange.from).toBe(daysFromNow(-3).getTime()); expect(panes[1]!.absoluteRange.to).toBe(daysFromNow(-1).getTime()); }); it('zooms out the time range in both panes', () => { const store = setup(); getAppEvents().publish(new ZoomOutEvent({ scale: 2 })); const exploreState = store.getState().explore; const panes = Object.values(exploreState.panes); expect(panes[0]!.absoluteRange.from).toBe(daysFromNow(-1.5).getTime()); expect(panes[0]!.absoluteRange.to).toBe(daysFromNow(0.5).getTime()); expect(panes[1]!.absoluteRange.from).toBe(daysFromNow(-3).getTime()); expect(panes[1]!.absoluteRange.to).toBe(daysFromNow(1).getTime()); }); it('copies the time range from the left pane', () => { const store = setup(); getAppEvents().publish(new CopyTimeEvent()); const fromValue = store.getState().explore.panes.left!.range.raw.from; const toValue = store.getState().explore.panes.left!.range.raw.to; expect(global.navigator.clipboard.writeText).toHaveBeenCalledWith(JSON.stringify({ from: fromValue, to: toValue })); }); it('pastes the time range to left pane', async () => { const store = setup(); const fromValue = 'now-3d'; const toValue = 'now'; mockClipboard.readText.mockResolvedValue(JSON.stringify({ from: fromValue, to: toValue })); getAppEvents().publish(new PasteTimeEvent({ updateUrl: false })); await waitFor(() => { const raw = store.getState().explore.panes.left!.range.raw; expect(raw.from).toBe(fromValue); expect(raw.to).toBe(toValue); }); }); });