123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- import { render, screen, waitFor } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import React from 'react';
- import { AnnotationEvent, FieldConfigSource, getDefaultTimeRange, LoadingState } from '@grafana/data';
- import { locationService } from '@grafana/runtime';
- import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
- import { backendSrv } from '../../../core/services/backend_srv';
- import { setDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
- import { AnnoListPanel, Props } from './AnnoListPanel';
- import { AnnoOptions } from './types';
- jest.mock('@grafana/runtime', () => ({
- ...(jest.requireActual('@grafana/runtime') as unknown as object),
- getBackendSrv: () => backendSrv,
- }));
- const defaultOptions: AnnoOptions = {
- limit: 10,
- navigateAfter: '10m',
- navigateBefore: '10m',
- navigateToPanel: true,
- onlyFromThisDashboard: true,
- onlyInTimeRange: false,
- showTags: true,
- showTime: true,
- showUser: true,
- tags: ['tag A', 'tag B'],
- };
- const defaultResult: any = {
- text: 'Result text',
- userId: 1,
- login: 'Result login',
- email: 'Result email',
- avatarUrl: 'Result avatarUrl',
- tags: ['Result tag A', 'Result tag B'],
- time: Date.UTC(2021, 0, 1, 0, 0, 0, 0),
- panelId: 13,
- dashboardId: 14, // deliberately different from panelId
- id: 14,
- url: '/d/asdkjhajksd/some-dash',
- };
- async function setupTestContext({
- options = defaultOptions,
- results = [defaultResult],
- }: { options?: AnnoOptions; results?: AnnotationEvent[] } = {}) {
- jest.clearAllMocks();
- const getMock = jest.spyOn(backendSrv, 'get');
- getMock.mockResolvedValue(results);
- const dash: any = { id: 1, formatDate: (time: number) => new Date(time).toISOString() };
- const dashSrv: any = { getCurrent: () => dash };
- setDashboardSrv(dashSrv);
- const pushSpy = jest.spyOn(locationService, 'push');
- const props: Props = {
- data: { state: LoadingState.Done, timeRange: getDefaultTimeRange(), series: [] },
- eventBus: {
- subscribe: jest.fn(),
- getStream: () =>
- ({
- subscribe: jest.fn(),
- } as any),
- publish: jest.fn(),
- removeAllListeners: jest.fn(),
- newScopedBus: jest.fn(),
- },
- fieldConfig: {} as unknown as FieldConfigSource,
- height: 400,
- id: 1,
- onChangeTimeRange: jest.fn(),
- onFieldConfigChange: jest.fn(),
- onOptionsChange: jest.fn(),
- options,
- renderCounter: 1,
- replaceVariables: (str: string) => str,
- timeRange: getDefaultTimeRange(),
- timeZone: 'utc',
- title: 'Test Title',
- transparent: false,
- width: 320,
- };
- const { rerender } = render(<AnnoListPanel {...props} />);
- await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
- return { props, rerender, getMock, pushSpy };
- }
- describe('AnnoListPanel', () => {
- describe('when mounted', () => {
- it('then it should fetch annotations', async () => {
- const { getMock } = await setupTestContext();
- expect(getMock).toHaveBeenCalledWith(
- '/api/annotations',
- {
- dashboardId: 1,
- limit: 10,
- tags: ['tag A', 'tag B'],
- type: 'annotation',
- },
- 'anno-list-panel-1'
- );
- });
- });
- describe('when there are no annotations', () => {
- it('then it should show a no annotations message', async () => {
- await setupTestContext({ results: [] });
- expect(screen.getByText(/no annotations found/i)).toBeInTheDocument();
- });
- });
- describe('when there are annotations', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext();
- expect(screen.queryByText(/no annotations found/i)).not.toBeInTheDocument();
- expect(screen.queryByText(/result email/i)).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByRole('img')).toBeInTheDocument();
- expect(screen.getByText('Result tag A')).toBeInTheDocument();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
- });
- describe('and login property is missing in annotation', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext({ results: [{ ...defaultResult, login: undefined }] });
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByText('Result tag A')).toBeInTheDocument();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
- });
- });
- describe('and property is missing in annotation', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext({ results: [{ ...defaultResult, time: undefined }] });
- expect(screen.queryByText(/2021-01-01T00:00:00.000Z/i)).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByRole('img')).toBeInTheDocument();
- expect(screen.getByText('Result tag A')).toBeInTheDocument();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- });
- });
- describe('and show user option is off', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext({
- options: { ...defaultOptions, showUser: false },
- });
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByText('Result tag A')).toBeInTheDocument();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
- });
- });
- describe('and show time option is off', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext({
- options: { ...defaultOptions, showTime: false },
- });
- expect(screen.queryByText(/2021-01-01T00:00:00.000Z/i)).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByRole('img')).toBeInTheDocument();
- expect(screen.getByText('Result tag A')).toBeInTheDocument();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- });
- });
- describe('and show tags option is off', () => {
- it('then it renders the annotations correctly', async () => {
- await setupTestContext({
- options: { ...defaultOptions, showTags: false },
- });
- expect(screen.queryByText('Result tag A')).not.toBeInTheDocument();
- expect(screen.queryByText('Result tag B')).not.toBeInTheDocument();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- expect(screen.getByRole('img')).toBeInTheDocument();
- expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
- });
- });
- describe('and the user clicks on the annotation', () => {
- it('then it should navigate to the dashboard connected to the annotation', async () => {
- const { getMock, pushSpy } = await setupTestContext();
- getMock.mockClear();
- expect(screen.getByText(/result text/i)).toBeInTheDocument();
- await userEvent.click(screen.getByText(/result text/i));
- await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
- expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardIds: 14 });
- expect(pushSpy).toHaveBeenCalledTimes(1);
- expect(pushSpy).toHaveBeenCalledWith('/d/asdkjhajksd/some-dash?from=1609458600000&to=1609459800000');
- });
- });
- describe('and the user clicks on a tag', () => {
- it('then it should navigate to the dashboard connected to the annotation', async () => {
- const { getMock } = await setupTestContext();
- getMock.mockClear();
- expect(screen.getByText('Result tag B')).toBeInTheDocument();
- await userEvent.click(screen.getByText('Result tag B'));
- expect(getMock).toHaveBeenCalledTimes(1);
- expect(getMock).toHaveBeenCalledWith(
- '/api/annotations',
- {
- dashboardId: 1,
- limit: 10,
- tags: ['tag A', 'tag B', 'Result tag B'],
- type: 'annotation',
- },
- 'anno-list-panel-1'
- );
- expect(screen.getByText(/filter:/i)).toBeInTheDocument();
- expect(screen.getAllByText(/result tag b/i)).toHaveLength(2);
- });
- });
- describe('and the user clicks on the user avatar', () => {
- it('then it should filter annotations by login and the filter should show', async () => {
- const { getMock } = await setupTestContext();
- getMock.mockClear();
- expect(screen.getByRole('img')).toBeInTheDocument();
- await userEvent.click(screen.getByRole('img'));
- expect(getMock).toHaveBeenCalledTimes(1);
- expect(getMock).toHaveBeenCalledWith(
- '/api/annotations',
- {
- dashboardId: 1,
- limit: 10,
- tags: ['tag A', 'tag B'],
- type: 'annotation',
- userId: 1,
- },
- 'anno-list-panel-1'
- );
- expect(screen.getByText(/filter:/i)).toBeInTheDocument();
- expect(screen.getByText(/result email/i)).toBeInTheDocument();
- });
- });
- describe('and the user hovers over the user avatar', () => {
- silenceConsoleOutput(); // Popper throws an act error, but if we add act around the hover here it doesn't matter
- it('then it should filter annotations by login', async () => {
- const { getMock } = await setupTestContext();
- getMock.mockClear();
- expect(screen.getByRole('img')).toBeInTheDocument();
- });
- });
- });
- });
|