mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
Chore: Upgrade to react 18 (#64428)
* update react 18 related deps * fix some types * make sure we're on react-router-dom >= 5.3.3 * Use new root API * Remove StrictMode for now - react 18 double rendering causes issues * fix + ignore some @grafana/ui types * fix some more types * use renderHook from @testing-library/react in almost all cases * fix storybook types * rewrite useDashboardSave to not use useEffect * make props optional * only render if props are provided * add correct type for useCallback * make resourcepicker tests more robust * fix ModalManager rendering * fix some more unit tests * store the click coordinates in a ref as setState is NOT synchronous * fix remaining e2e tests * rewrite dashboardpage tests to avoid act warnings * undo lint ignores * fix ExpanderCell types * set SymbolCell type correctly * fix QueryAndExpressionsStep * looks like the types were actually wrong instead :D * undo this for now... * remove spinner waits * more robust tests * rewrite errorboundary test to not explicitly count the number of renders * make urlParam expect async * increase timeout in waitFor * revert ExplorePage test changes * Update public/app/features/dashboard/containers/DashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * skip fakeTimer test, ignore table types for now + other review comments * update package peerDeps * small tweak to resourcepicker test * update lockfile... * increase timeout in sharepublicdashboard tests * ensure ExplorePaneContainer passes correct queries to initializeExplore * fix LokiContextUI test * fix unit tests * make importDashboard flow more consistent * wait for dashboard name before continuing * more test fixes * readd dashboard name to variable e2e tests * wait for switches to be enabled before clicking * fix modal rendering * don't use @testing-library/dom directly * quick fix for rendering of panels in firefox * make PromQueryField test more robust * don't wait for chartData - in react 18 this can happen before the wait code even gets executed --------- Co-authored-by: kay delaney <kay@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
co-authored by
Alex Khomenko
kay delaney
parent
a5499bbf70
commit
1261345b81
+1
-2
@@ -1,5 +1,4 @@
|
||||
import { within } from '@testing-library/dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { within } from '@testing-library/dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
import { within } from '@testing-library/dom';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { render, screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
+23
-10
@@ -1,4 +1,4 @@
|
||||
import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
@@ -311,6 +311,7 @@ describe('SharePublic - Already persisted', () => {
|
||||
|
||||
describe('SharePublic - Report interactions', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
server.use(getExistentPublicDashboardResponse());
|
||||
server.use(
|
||||
rest.put('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', (req, res, ctx) =>
|
||||
@@ -327,29 +328,41 @@ describe('SharePublic - Report interactions', () => {
|
||||
|
||||
it('reports interaction when time range is clicked', async () => {
|
||||
await renderSharePublicDashboard();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId(selectors.EnableTimeRangeSwitch)).toBeEnabled();
|
||||
});
|
||||
await userEvent.click(screen.getByTestId(selectors.EnableTimeRangeSwitch));
|
||||
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
|
||||
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_time_selection_clicked', {
|
||||
action: pubdashResponse.timeSelectionEnabled ? 'disable' : 'enable',
|
||||
await waitFor(() => {
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_time_selection_clicked', {
|
||||
action: pubdashResponse.timeSelectionEnabled ? 'disable' : 'enable',
|
||||
});
|
||||
});
|
||||
});
|
||||
it('reports interaction when show annotations is clicked', async () => {
|
||||
await renderSharePublicDashboard();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId(selectors.EnableAnnotationsSwitch)).toBeEnabled();
|
||||
});
|
||||
await userEvent.click(screen.getByTestId(selectors.EnableAnnotationsSwitch));
|
||||
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
|
||||
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_annotations_clicked', {
|
||||
action: pubdashResponse.annotationsEnabled ? 'disable' : 'enable',
|
||||
await waitFor(() => {
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_annotations_clicked', {
|
||||
action: pubdashResponse.annotationsEnabled ? 'disable' : 'enable',
|
||||
});
|
||||
});
|
||||
});
|
||||
it('reports interaction when pause is clicked', async () => {
|
||||
await renderSharePublicDashboard();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId(selectors.PauseSwitch)).toBeEnabled();
|
||||
});
|
||||
await userEvent.click(screen.getByTestId(selectors.PauseSwitch));
|
||||
await waitForElementToBeRemoved(screen.getByTestId('Spinner'));
|
||||
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_enable_clicked', {
|
||||
action: pubdashResponse.isEnabled ? 'disable' : 'enable',
|
||||
await waitFor(() => {
|
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_enable_clicked', {
|
||||
action: pubdashResponse.isEnabled ? 'disable' : 'enable',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { match, Router } from 'react-router-dom';
|
||||
@@ -66,16 +66,6 @@ jest.mock('react-virtualized-auto-sizer', () => {
|
||||
return ({ children }: AutoSizerProps) => children({ height: 1, width: 1 });
|
||||
});
|
||||
|
||||
interface ScenarioContext {
|
||||
dashboard?: DashboardModel | null;
|
||||
container?: HTMLElement;
|
||||
mount: (propOverrides?: Partial<Props>) => void;
|
||||
unmount: () => void;
|
||||
props: Props;
|
||||
rerender: (propOverrides?: Partial<Props>) => void;
|
||||
setup: (fn: () => void) => void;
|
||||
}
|
||||
|
||||
function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partial<DashboardMeta>): DashboardModel {
|
||||
const data = Object.assign(
|
||||
{
|
||||
@@ -95,123 +85,98 @@ function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partia
|
||||
return createDashboardModelFixture(data, metaOverrides);
|
||||
}
|
||||
|
||||
function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioContext) => void) {
|
||||
describe(description, () => {
|
||||
let setupFn: () => void;
|
||||
const mockInitDashboard = jest.fn();
|
||||
const mockCleanUpDashboardAndVariables = jest.fn();
|
||||
|
||||
const ctx: ScenarioContext = {
|
||||
setup: (fn) => {
|
||||
setupFn = fn;
|
||||
},
|
||||
mount: (propOverrides?: Partial<Props>) => {
|
||||
config.bootData.navTree = [
|
||||
{ text: 'Dashboards', id: 'dashboards' },
|
||||
{ text: 'Home', id: HOME_NAV_ID },
|
||||
];
|
||||
function setup(propOverrides?: Partial<Props>) {
|
||||
config.bootData.navTree = [
|
||||
{ text: 'Dashboards', id: 'dashboards' },
|
||||
{ text: 'Home', id: HOME_NAV_ID },
|
||||
];
|
||||
|
||||
const store = configureStore();
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
match: { params: { slug: 'my-dash', uid: '11' } } as unknown as match,
|
||||
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
||||
}),
|
||||
navIndex: {
|
||||
dashboards: { text: 'Dashboards', id: 'dashboards', parentItem: { text: 'Home', id: HOME_NAV_ID } },
|
||||
[HOME_NAV_ID]: { text: 'Home', id: HOME_NAV_ID },
|
||||
},
|
||||
initPhase: DashboardInitPhase.NotStarted,
|
||||
initError: null,
|
||||
initDashboard: jest.fn(),
|
||||
notifyApp: mockToolkitActionCreator(notifyApp),
|
||||
cleanUpDashboardAndVariables: jest.fn(),
|
||||
cancelVariables: jest.fn(),
|
||||
templateVarsChangedInUrl: jest.fn(),
|
||||
dashboard: null,
|
||||
theme: createTheme(),
|
||||
};
|
||||
const store = configureStore();
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
match: { params: { slug: 'my-dash', uid: '11' } } as unknown as match,
|
||||
route: { routeName: DashboardRoutes.Normal } as RouteDescriptor,
|
||||
}),
|
||||
navIndex: {
|
||||
dashboards: { text: 'Dashboards', id: 'dashboards', parentItem: { text: 'Home', id: HOME_NAV_ID } },
|
||||
[HOME_NAV_ID]: { text: 'Home', id: HOME_NAV_ID },
|
||||
},
|
||||
initPhase: DashboardInitPhase.NotStarted,
|
||||
initError: null,
|
||||
initDashboard: mockInitDashboard,
|
||||
notifyApp: mockToolkitActionCreator(notifyApp),
|
||||
cleanUpDashboardAndVariables: mockCleanUpDashboardAndVariables,
|
||||
cancelVariables: jest.fn(),
|
||||
templateVarsChangedInUrl: jest.fn(),
|
||||
dashboard: null,
|
||||
theme: createTheme(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
ctx.props = props;
|
||||
ctx.dashboard = props.dashboard;
|
||||
const context = getGrafanaContextMock();
|
||||
|
||||
const context = getGrafanaContextMock();
|
||||
const { unmount, rerender } = render(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<UnthemedDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
|
||||
const { container, rerender, unmount } = render(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<UnthemedDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
const wrappedRerender = (newProps: Partial<Props>) => {
|
||||
Object.assign(props, newProps);
|
||||
return rerender(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<UnthemedDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ctx.container = container;
|
||||
|
||||
ctx.rerender = (newProps?: Partial<Props>) => {
|
||||
Object.assign(props, newProps);
|
||||
|
||||
rerender(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<UnthemedDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ctx.unmount = unmount;
|
||||
},
|
||||
props: {} as Props,
|
||||
rerender: () => {},
|
||||
unmount: () => {},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupFn();
|
||||
});
|
||||
|
||||
scenarioFn(ctx);
|
||||
});
|
||||
return { rerender: wrappedRerender, unmount };
|
||||
}
|
||||
|
||||
describe('DashboardPage', () => {
|
||||
dashboardPageScenario('Given initial state', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('Should call initDashboard on mount', () => {
|
||||
setup();
|
||||
expect(mockInitDashboard).toBeCalledWith({
|
||||
fixUrl: true,
|
||||
routeName: 'normal-dashboard',
|
||||
urlSlug: 'my-dash',
|
||||
urlUid: '11',
|
||||
keybindingSrv: expect.anything(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('Given a simple dashboard', () => {
|
||||
it('Should render panels', async () => {
|
||||
setup({ dashboard: getTestDashboard() });
|
||||
expect(await screen.findByText('My panel title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should call initDashboard on mount', () => {
|
||||
expect(ctx.props.initDashboard).toBeCalledWith({
|
||||
fixUrl: true,
|
||||
routeName: 'normal-dashboard',
|
||||
urlSlug: 'my-dash',
|
||||
urlUid: '11',
|
||||
keybindingSrv: expect.anything(),
|
||||
it('Should update title', async () => {
|
||||
setup({ dashboard: getTestDashboard() });
|
||||
await waitFor(() => {
|
||||
expect(document.title).toBe('My dashboard - Dashboards - Grafana');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('Given a simple dashboard', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({ dashboard: getTestDashboard() });
|
||||
});
|
||||
|
||||
it('Should render panels', () => {
|
||||
expect(screen.getByText('My panel title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should update title', () => {
|
||||
expect(document.title).toBe('My dashboard - Dashboards - Grafana');
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('When going into view mode', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
describe('When going into view mode', () => {
|
||||
beforeEach(() => {
|
||||
setDataSourceSrv({
|
||||
get: jest.fn().mockResolvedValue({ getRef: jest.fn(), query: jest.fn().mockResolvedValue([]) }),
|
||||
getInstanceSettings: jest.fn().mockReturnValue({ meta: {} }),
|
||||
@@ -221,101 +186,110 @@ describe('DashboardPage', () => {
|
||||
setDashboardSrv({
|
||||
getCurrent: () => getTestDashboard(),
|
||||
} as DashboardSrv);
|
||||
ctx.mount({
|
||||
dashboard: getTestDashboard(),
|
||||
});
|
||||
|
||||
it('Should render panel in view mode', async () => {
|
||||
const dashboard = getTestDashboard();
|
||||
setup({
|
||||
dashboard,
|
||||
queryParams: { viewPanel: '1' },
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(dashboard.panelInView).toBeDefined();
|
||||
expect(dashboard.panels[0].isViewing).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render panel in view mode', () => {
|
||||
expect(ctx.dashboard?.panelInView).toBeDefined();
|
||||
expect(ctx.dashboard?.panels[0].isViewing).toBe(true);
|
||||
});
|
||||
it('Should reset state when leaving', async () => {
|
||||
const dashboard = getTestDashboard();
|
||||
const { rerender } = setup({
|
||||
dashboard,
|
||||
queryParams: { viewPanel: '1' },
|
||||
});
|
||||
rerender({ queryParams: {}, dashboard });
|
||||
|
||||
it('Should reset state when leaving', () => {
|
||||
ctx.rerender({ queryParams: {} });
|
||||
|
||||
expect(ctx.dashboard?.panelInView).toBeUndefined();
|
||||
expect(ctx.dashboard?.panels[0].isViewing).toBe(false);
|
||||
await waitFor(() => {
|
||||
expect(dashboard.panelInView).toBeUndefined();
|
||||
expect(dashboard.panels[0].isViewing).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('When going into edit mode', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount({
|
||||
dashboard: getTestDashboard(),
|
||||
describe('When going into edit mode', () => {
|
||||
it('Should render panel in edit mode', async () => {
|
||||
const dashboard = getTestDashboard();
|
||||
setup({
|
||||
dashboard,
|
||||
queryParams: { editPanel: '1' },
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(dashboard.panelInEdit).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render panel in edit mode', () => {
|
||||
expect(ctx.dashboard?.panelInEdit).toBeDefined();
|
||||
it('Should render panel editor', async () => {
|
||||
const dashboard = getTestDashboard();
|
||||
setup({
|
||||
dashboard,
|
||||
queryParams: { editPanel: '1' },
|
||||
});
|
||||
expect(await screen.findByTitle('Apply changes and go back to dashboard')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should render panel editor', () => {
|
||||
expect(screen.getByTitle('Apply changes and go back to dashboard')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should reset state when leaving', () => {
|
||||
ctx.rerender({ queryParams: {} });
|
||||
expect(screen.queryByTitle('Apply changes and go back to dashboard')).not.toBeInTheDocument();
|
||||
it('Should reset state when leaving', async () => {
|
||||
const dashboard = getTestDashboard();
|
||||
const { rerender } = setup({
|
||||
dashboard,
|
||||
queryParams: { editPanel: '1' },
|
||||
});
|
||||
rerender({ queryParams: {} });
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTitle('Apply changes and go back to dashboard')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('When dashboard unmounts', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({ dashboard: getTestDashboard() });
|
||||
ctx.unmount();
|
||||
});
|
||||
|
||||
it('Should call close action', () => {
|
||||
expect(ctx.props.cleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
|
||||
describe('When dashboard unmounts', () => {
|
||||
it('Should call close action', async () => {
|
||||
const { rerender, unmount } = setup();
|
||||
rerender({ dashboard: getTestDashboard() });
|
||||
unmount();
|
||||
await waitFor(() => {
|
||||
expect(mockCleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('When dashboard changes', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({ dashboard: getTestDashboard() });
|
||||
ctx.rerender({
|
||||
match: {
|
||||
params: { uid: 'new-uid' },
|
||||
} as unknown as match,
|
||||
describe('When dashboard changes', () => {
|
||||
it('Should call clean up action and init', async () => {
|
||||
const { rerender } = setup();
|
||||
rerender({ dashboard: getTestDashboard() });
|
||||
rerender({
|
||||
match: { params: { uid: 'new-uid' } } as unknown as match,
|
||||
dashboard: getTestDashboard({ title: 'Another dashboard' }),
|
||||
});
|
||||
});
|
||||
|
||||
it('Should call clean up action and init', () => {
|
||||
expect(ctx.props.cleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.props.initDashboard).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('No kiosk mode tv', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount({ dashboard: getTestDashboard() });
|
||||
ctx.rerender({ dashboard: ctx.dashboard });
|
||||
});
|
||||
|
||||
it('should render dashboard page toolbar and submenu', () => {
|
||||
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
|
||||
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('When in full kiosk mode', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount({
|
||||
queryParams: { kiosk: true },
|
||||
dashboard: getTestDashboard(),
|
||||
await waitFor(() => {
|
||||
expect(mockCleanUpDashboardAndVariables).toHaveBeenCalledTimes(1);
|
||||
expect(mockInitDashboard).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
ctx.rerender({ dashboard: ctx.dashboard });
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render page toolbar and submenu', () => {
|
||||
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(0);
|
||||
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
|
||||
describe('No kiosk mode tv', () => {
|
||||
it('should render dashboard page toolbar and submenu', async () => {
|
||||
setup({ dashboard: getTestDashboard() });
|
||||
expect(await screen.findAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(1);
|
||||
expect(screen.getAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When in full kiosk mode', () => {
|
||||
it('should not render page toolbar and submenu', async () => {
|
||||
setup({ dashboard: getTestDashboard(), queryParams: { kiosk: true } });
|
||||
await waitFor(() => {
|
||||
expect(screen.queryAllByTestId(selectors.pages.Dashboard.DashNav.navV2)).toHaveLength(0);
|
||||
expect(screen.queryAllByLabelText(selectors.pages.Dashboard.SubMenu.submenu)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, RenderResult, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Router } from 'react-router-dom';
|
||||
@@ -48,29 +48,27 @@ jest.mock('app/types', () => ({
|
||||
useDispatch: () => jest.fn(),
|
||||
}));
|
||||
|
||||
interface ScenarioContext {
|
||||
mount: () => void;
|
||||
rerender: ({
|
||||
propOverrides,
|
||||
newState,
|
||||
}: {
|
||||
propOverrides?: Partial<Props>;
|
||||
newState?: Partial<appTypes.StoreState>;
|
||||
}) => void;
|
||||
setup: (fn: () => void) => void;
|
||||
}
|
||||
|
||||
const renderWithProvider = ({
|
||||
props,
|
||||
initialState,
|
||||
}: {
|
||||
props: Props;
|
||||
initialState?: Partial<appTypes.StoreState>;
|
||||
}): RenderResult => {
|
||||
const setup = (propOverrides?: Partial<Props>, initialState?: Partial<appTypes.StoreState>) => {
|
||||
const context = getGrafanaContextMock();
|
||||
const store = configureStore(initialState);
|
||||
|
||||
return render(
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
match: { params: { accessToken: 'an-access-token' }, isExact: true, url: '', path: '' },
|
||||
route: {
|
||||
routeName: DashboardRoutes.Public,
|
||||
path: '/public-dashboards/:accessToken',
|
||||
component: SafeDynamicImport(
|
||||
() =>
|
||||
import(/* webpackChunkName: "PublicDashboardPage"*/ 'app/features/dashboard/containers/PublicDashboardPage')
|
||||
),
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const { unmount, rerender } = render(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
@@ -79,6 +77,21 @@ const renderWithProvider = ({
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
|
||||
const wrappedRerender = (newProps: Partial<Props>) => {
|
||||
Object.assign(props, newProps);
|
||||
return rerender(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<PublicDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
return { rerender: wrappedRerender, unmount };
|
||||
};
|
||||
|
||||
const selectors = e2eSelectors.components;
|
||||
@@ -110,185 +123,114 @@ const getTestDashboard = (overrides?: Partial<Dashboard>, metaOverrides?: Partia
|
||||
return new DashboardModel(data, metaOverrides);
|
||||
};
|
||||
|
||||
function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioContext) => void) {
|
||||
describe(description, () => {
|
||||
let setupFn: () => void;
|
||||
describe('PublicDashboardPage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const ctx: ScenarioContext = {
|
||||
setup: (fn) => {
|
||||
setupFn = fn;
|
||||
it('Should call initDashboard on mount', () => {
|
||||
setup();
|
||||
expect(initDashboard).toBeCalledWith({
|
||||
fixUrl: false,
|
||||
accessToken: 'an-access-token',
|
||||
routeName: 'public-dashboard',
|
||||
keybindingSrv: expect.anything(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('Given a simple public dashboard', () => {
|
||||
const newState = {
|
||||
dashboard: {
|
||||
getModel: getTestDashboard,
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
mount: () => {
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
match: { params: { accessToken: 'an-access-token' }, isExact: true, url: '', path: '' },
|
||||
route: {
|
||||
routeName: DashboardRoutes.Public,
|
||||
path: '/public-dashboards/:accessToken',
|
||||
component: SafeDynamicImport(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "PublicDashboardPage"*/ 'app/features/dashboard/containers/PublicDashboardPage'
|
||||
)
|
||||
),
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const { rerender } = renderWithProvider({ props });
|
||||
|
||||
ctx.rerender = ({
|
||||
propsOverride,
|
||||
newState,
|
||||
}: {
|
||||
propsOverride?: Partial<Props>;
|
||||
newState?: Partial<appTypes.StoreState>;
|
||||
}) => {
|
||||
Object.assign(props, propsOverride);
|
||||
|
||||
const context = getGrafanaContextMock();
|
||||
const store = configureStore(newState);
|
||||
|
||||
rerender(
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<Provider store={store}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<PublicDashboardPage {...props} />
|
||||
</Router>
|
||||
</Provider>
|
||||
</GrafanaContext.Provider>
|
||||
);
|
||||
};
|
||||
},
|
||||
rerender: () => {},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupFn();
|
||||
it('Should render panels', async () => {
|
||||
setup(undefined, newState);
|
||||
expect(await screen.findByText('My panel title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
scenarioFn(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
describe('PublicDashboardPage', () => {
|
||||
dashboardPageScenario('Given initial state', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
it('Should update title', async () => {
|
||||
setup(undefined, newState);
|
||||
await waitFor(() => {
|
||||
expect(document.title).toBe('My dashboard - Grafana');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should call initDashboard on mount', () => {
|
||||
expect(initDashboard).toBeCalledWith({
|
||||
fixUrl: false,
|
||||
accessToken: 'an-access-token',
|
||||
routeName: 'public-dashboard',
|
||||
keybindingSrv: expect.anything(),
|
||||
it('Should not render neither time range nor refresh picker buttons', async () => {
|
||||
setup(undefined, newState);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId(selectors.TimePicker.openButton)).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(selectors.RefreshPicker.runButtonV2)).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(selectors.RefreshPicker.intervalButtonV2)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not render paused or deleted screen', async () => {
|
||||
setup(undefined, newState);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.container)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('Given a simple public dashboard', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({
|
||||
newState: {
|
||||
dashboard: {
|
||||
getModel: getTestDashboard,
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
describe('Given a public dashboard with time range enabled', () => {
|
||||
it('Should render time range and refresh picker buttons', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
getModel: () =>
|
||||
getTestDashboard({
|
||||
timepicker: { hidden: false, collapse: false, enable: true, refresh_intervals: [], time_options: [] },
|
||||
}),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render panels', () => {
|
||||
expect(screen.getByText('My panel title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should update title', () => {
|
||||
expect(document.title).toBe('My dashboard - Grafana');
|
||||
});
|
||||
|
||||
it('Should not render neither time range nor refresh picker buttons', () => {
|
||||
expect(screen.queryByTestId(selectors.TimePicker.openButton)).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(selectors.RefreshPicker.runButtonV2)).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(selectors.RefreshPicker.intervalButtonV2)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should not render paused or deleted screen', () => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.container)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('Given a public dashboard with time range enabled', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({
|
||||
newState: {
|
||||
dashboard: {
|
||||
getModel: () =>
|
||||
getTestDashboard({
|
||||
timepicker: { hidden: false, collapse: false, enable: true, refresh_intervals: [], time_options: [] },
|
||||
}),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render time range and refresh picker buttons', () => {
|
||||
expect(screen.getByTestId(selectors.TimePicker.openButton)).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(selectors.TimePicker.openButton)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(selectors.RefreshPicker.runButtonV2)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(selectors.RefreshPicker.intervalButtonV2)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('Given paused public dashboard', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({
|
||||
newState: {
|
||||
dashboard: {
|
||||
getModel: () => getTestDashboard(undefined, { publicDashboardEnabled: false, dashboardNotFound: false }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
describe('Given paused public dashboard', () => {
|
||||
it('Should render public dashboard paused screen', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
getModel: () => getTestDashboard(undefined, { publicDashboardEnabled: false, dashboardNotFound: false }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render public dashboard paused screen', () => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.title)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
dashboardPageScenario('Given deleted public dashboard', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
ctx.mount();
|
||||
ctx.rerender({
|
||||
newState: {
|
||||
dashboard: {
|
||||
getModel: () => getTestDashboard(undefined, { dashboardNotFound: true }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
describe('Given deleted public dashboard', () => {
|
||||
it('Should render public dashboard deleted screen', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
getModel: () => getTestDashboard(undefined, { dashboardNotFound: true }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render public dashboard deleted screen', () => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId(publicDashboardSelector.page)).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId(publicDashboardSelector.NotAvailable.title)).toBeInTheDocument();
|
||||
expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.pausedDescription)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,9 +8,10 @@ import { DashboardModel } from '../state';
|
||||
import { createDashboardModelFixture } from '../state/__fixtures__/dashboardFixtures';
|
||||
|
||||
import { DashboardGrid, Props } from './DashboardGrid';
|
||||
import { Props as LazyLoaderProps } from './LazyLoader';
|
||||
|
||||
jest.mock('app/features/dashboard/dashgrid/LazyLoader', () => {
|
||||
const LazyLoader = ({ children }: React.PropsWithChildren<{}>) => {
|
||||
const LazyLoader = ({ children }: LazyLoaderProps) => {
|
||||
return <>{children}</>;
|
||||
};
|
||||
return { LazyLoader };
|
||||
|
||||
@@ -71,7 +71,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
renderPanel = (isInView: boolean) => {
|
||||
renderPanel = ({ isInView }: { isInView: boolean }) => {
|
||||
const { dashboard, panel, isViewing, isEditing, width, height, plugin, timezone, hideMenu } = this.props;
|
||||
|
||||
if (!plugin) {
|
||||
@@ -118,7 +118,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
|
||||
{this.renderPanel}
|
||||
</LazyLoader>
|
||||
) : (
|
||||
this.renderPanel(true)
|
||||
this.renderPanel({ isInView: true })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ LazyLoader.addCallback = (id: string, c: (e: IntersectionObserverEntry) => void)
|
||||
LazyLoader.observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
LazyLoader.callbacks[entry.target.id](entry);
|
||||
if (LazyLoader.callbacks[entry.target.id]) {
|
||||
LazyLoader.callbacks[entry.target.id](entry);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin: '100px' }
|
||||
|
||||
Reference in New Issue
Block a user