123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- import { act, render, screen, waitFor } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import React, { ReactNode } from 'react';
- import { Provider } from 'react-redux';
- import { DataQuery } from '@grafana/data';
- import { locationService, setEchoSrv } from '@grafana/runtime';
- import { backendSrv } from 'app/core/services/backend_srv';
- import { Echo } from 'app/core/services/echo/Echo';
- import * as initDashboard from 'app/features/dashboard/state/initDashboard';
- import { DashboardSearchItemType } from 'app/features/search/types';
- import { configureStore } from 'app/store/configureStore';
- import { ExploreId, ExploreState } from 'app/types';
- import { createEmptyQueryResponse } from '../state/utils';
- import * as api from './addToDashboard';
- import { AddToDashboard } from '.';
- const setup = (children: ReactNode, queries: DataQuery[] = [{ refId: 'A' }]) => {
- const store = configureStore({
- explore: {
- left: {
- queries,
- queryResponse: createEmptyQueryResponse(),
- },
- } as ExploreState,
- });
- return render(<Provider store={store}>{children}</Provider>);
- };
- const openModal = async () => {
- await userEvent.click(screen.getByRole('button', { name: /add to dashboard/i }));
- expect(await screen.findByRole('dialog', { name: 'Add panel to dashboard' })).toBeInTheDocument();
- };
- describe('AddToDashboardButton', () => {
- beforeAll(() => {
- setEchoSrv(new Echo());
- });
- it('Is disabled if explore pane has no queries', async () => {
- setup(<AddToDashboard exploreId={ExploreId.left} />, []);
- const button = await screen.findByRole('button', { name: /add to dashboard/i });
- expect(button).toBeDisabled();
- await userEvent.click(button);
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
- });
- describe('Success path', () => {
- const addToDashboardResponse = Promise.resolve();
- const waitForAddToDashboardResponse = async () => {
- return act(async () => {
- await addToDashboardResponse;
- });
- };
- beforeEach(() => {
- jest.spyOn(api, 'setDashboardInLocalStorage').mockReturnValue(addToDashboardResponse);
- });
- afterEach(() => {
- jest.restoreAllMocks();
- });
- it('Opens and closes the modal correctly', async () => {
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('button', { name: /cancel/i }));
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
- });
- describe('navigation', () => {
- it('Navigates to dashboard when clicking on "Open"', async () => {
- // @ts-expect-error global.open should return a Window, but is not implemented in js-dom.
- const openSpy = jest.spyOn(global, 'open').mockReturnValue(true);
- const pushSpy = jest.spyOn(locationService, 'push');
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
- await waitForAddToDashboardResponse();
- expect(screen.queryByRole('dialog', { name: 'Add panel to dashboard' })).not.toBeInTheDocument();
- expect(pushSpy).toHaveBeenCalled();
- expect(openSpy).not.toHaveBeenCalled();
- });
- it('Navigates to dashboard in a new tab when clicking on "Open in a new tab"', async () => {
- // @ts-expect-error global.open should return a Window, but is not implemented in js-dom.
- const openSpy = jest.spyOn(global, 'open').mockReturnValue(true);
- const pushSpy = jest.spyOn(locationService, 'push');
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitForAddToDashboardResponse();
- expect(openSpy).toHaveBeenCalledWith(expect.anything(), '_blank');
- expect(pushSpy).not.toHaveBeenCalled();
- });
- });
- describe('Save to new dashboard', () => {
- describe('Navigate to correct dashboard when saving', () => {
- it('Opens the new dashboard in a new tab', async () => {
- // @ts-expect-error global.open should return a Window, but is not implemented in js-dom.
- const openSpy = jest.spyOn(global, 'open').mockReturnValue(true);
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitForAddToDashboardResponse();
- expect(openSpy).toHaveBeenCalledWith('dashboard/new', '_blank');
- });
- it('Navigates to the new dashboard', async () => {
- const pushSpy = jest.spyOn(locationService, 'push');
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
- await waitForAddToDashboardResponse();
- expect(screen.queryByRole('dialog', { name: 'Add panel to dashboard' })).not.toBeInTheDocument();
- expect(pushSpy).toHaveBeenCalledWith('dashboard/new');
- });
- });
- });
- describe('Save to existing dashboard', () => {
- it('Renders the dashboard picker when switching to "Existing Dashboard"', async () => {
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- expect(screen.queryByRole('combobox', { name: /dashboard/ })).not.toBeInTheDocument();
- await userEvent.click(screen.getByRole<HTMLInputElement>('radio', { name: /existing dashboard/i }));
- expect(screen.getByRole('combobox', { name: /dashboard/ })).toBeInTheDocument();
- });
- it('Does not submit if no dashboard is selected', async () => {
- locationService.push = jest.fn();
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole<HTMLInputElement>('radio', { name: /existing dashboard/i }));
- await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
- await waitForAddToDashboardResponse();
- expect(locationService.push).not.toHaveBeenCalled();
- });
- describe('Navigate to correct dashboard when saving', () => {
- it('Opens the selected dashboard in a new tab', async () => {
- // @ts-expect-error global.open should return a Window, but is not implemented in js-dom.
- const openSpy = jest.spyOn(global, 'open').mockReturnValue(true);
- jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({
- dashboard: { templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' },
- meta: {},
- });
- jest.spyOn(backendSrv, 'search').mockResolvedValue([
- {
- id: 1,
- uid: 'someUid',
- isStarred: false,
- items: [],
- title: 'Dashboard Title',
- tags: [],
- type: DashboardSearchItemType.DashDB,
- uri: 'someUri',
- url: 'someUrl',
- },
- ]);
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i }));
- await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i }));
- await waitFor(async () => {
- await screen.findByLabelText('Select option');
- });
- await userEvent.click(screen.getByLabelText('Select option'));
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitFor(async () => {
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
- });
- expect(openSpy).toBeCalledWith('d/someUid', '_blank');
- });
- it('Navigates to the selected dashboard', async () => {
- const pushSpy = jest.spyOn(locationService, 'push');
- jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({
- dashboard: { templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' },
- meta: {},
- });
- jest.spyOn(backendSrv, 'search').mockResolvedValue([
- {
- id: 1,
- uid: 'someUid',
- isStarred: false,
- items: [],
- title: 'Dashboard Title',
- tags: [],
- type: DashboardSearchItemType.DashDB,
- uri: 'someUri',
- url: 'someUrl',
- },
- ]);
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i }));
- await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i }));
- await waitFor(async () => {
- await screen.findByLabelText('Select option');
- });
- await userEvent.click(screen.getByLabelText('Select option'));
- await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
- await waitFor(async () => {
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
- });
- expect(pushSpy).toBeCalledWith('d/someUid');
- });
- });
- });
- });
- describe('Error handling', () => {
- afterEach(() => {
- jest.restoreAllMocks();
- });
- it('Shows an error if opening a new tab fails', async () => {
- jest.spyOn(global, 'open').mockReturnValue(null);
- const removeDashboardSpy = jest.spyOn(initDashboard, 'removeDashboardToFetchFromLocalStorage');
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitFor(async () => {
- expect(await screen.findByRole('alert')).toBeInTheDocument();
- });
- expect(removeDashboardSpy).toHaveBeenCalled();
- });
- it('Shows an error if saving to localStorage fails', async () => {
- jest.spyOn(initDashboard, 'setDashboardToFetchFromLocalStorage').mockImplementation(() => {
- throw 'SOME ERROR';
- });
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitFor(async () => {
- expect(await screen.findByRole('alert')).toBeInTheDocument();
- });
- });
- it('Shows an error if fetching dashboard fails', async () => {
- jest.spyOn(backendSrv, 'getDashboardByUid').mockRejectedValue('SOME ERROR');
- jest.spyOn(backendSrv, 'search').mockResolvedValue([
- {
- id: 1,
- uid: 'someUid',
- isStarred: false,
- items: [],
- title: 'Dashboard Title',
- tags: [],
- type: DashboardSearchItemType.DashDB,
- uri: 'someUri',
- url: 'someUrl',
- },
- ]);
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i }));
- await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i }));
- await waitFor(async () => {
- await screen.findByLabelText('Select option');
- });
- await userEvent.click(screen.getByLabelText('Select option'));
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitFor(async () => {
- expect(await screen.findByRole('alert')).toBeInTheDocument();
- });
- });
- it('Shows an error if an unknown error happens', async () => {
- jest.spyOn(api, 'setDashboardInLocalStorage').mockRejectedValue('SOME ERROR');
- setup(<AddToDashboard exploreId={ExploreId.left} />);
- await openModal();
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- await userEvent.click(screen.getByRole('button', { name: /open in new tab/i }));
- await waitFor(async () => {
- expect(await screen.findByRole('alert')).toBeInTheDocument();
- });
- });
- });
- });
|