mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 00:25:46 -06:00
171 lines
4.3 KiB
TypeScript
171 lines
4.3 KiB
TypeScript
import { act, render, screen } from '@testing-library/react';
|
|
import React, { Component } from 'react';
|
|
import { StoreState } from 'app/types';
|
|
import { Provider } from 'react-redux';
|
|
import configureStore from 'redux-mock-store';
|
|
import AppRootPage from './AppRootPage';
|
|
import { getPluginSettings } from './PluginSettingsCache';
|
|
import { importAppPlugin } from './plugin_loader';
|
|
import { getMockPlugin } from './__mocks__/pluginMocks';
|
|
import { AppPlugin, PluginType, AppRootProps, NavModelItem } from '@grafana/data';
|
|
import { updateLocation } from 'app/core/actions';
|
|
import { createRootReducer } from 'app/core/reducers/root';
|
|
import { createStore } from 'redux';
|
|
|
|
jest.mock('./PluginSettingsCache', () => ({
|
|
getPluginSettings: jest.fn(),
|
|
}));
|
|
jest.mock('./plugin_loader', () => ({
|
|
importAppPlugin: jest.fn(),
|
|
}));
|
|
|
|
const importAppPluginMock = importAppPlugin as jest.Mock<
|
|
ReturnType<typeof importAppPlugin>,
|
|
Parameters<typeof importAppPlugin>
|
|
>;
|
|
|
|
const getPluginSettingsMock = getPluginSettings as jest.Mock<
|
|
ReturnType<typeof getPluginSettings>,
|
|
Parameters<typeof getPluginSettings>
|
|
>;
|
|
|
|
const initialState: Partial<StoreState> = {
|
|
location: {
|
|
routeParams: {
|
|
pluginId: 'my-awesome-plugin',
|
|
slug: 'my-awesome-plugin',
|
|
},
|
|
query: {},
|
|
path: '/a/my-awesome-plugin',
|
|
url: '',
|
|
replace: false,
|
|
lastUpdated: 1,
|
|
},
|
|
};
|
|
|
|
function renderWithStore(soreState: Partial<StoreState> = initialState) {
|
|
const store = configureStore<StoreState>()(soreState as StoreState);
|
|
render(
|
|
<Provider store={store}>
|
|
<AppRootPage />
|
|
</Provider>
|
|
);
|
|
return store;
|
|
}
|
|
|
|
describe('AppRootPage', () => {
|
|
beforeEach(() => {
|
|
jest.resetAllMocks();
|
|
});
|
|
|
|
it('should not mount plugin twice if nav is changed', async () => {
|
|
// reproduces https://github.com/grafana/grafana/pull/28105
|
|
|
|
getPluginSettingsMock.mockResolvedValue(
|
|
getMockPlugin({
|
|
type: PluginType.app,
|
|
enabled: true,
|
|
})
|
|
);
|
|
|
|
let timesMounted = 0;
|
|
|
|
// a very basic component that does what most plugins do:
|
|
// immediately update nav on mounting
|
|
class RootComponent extends Component<AppRootProps> {
|
|
componentDidMount() {
|
|
timesMounted++;
|
|
const node: NavModelItem = {
|
|
text: 'My Great plugin',
|
|
children: [
|
|
{
|
|
text: 'A page',
|
|
url: '/apage',
|
|
id: 'a',
|
|
},
|
|
{
|
|
text: 'Another page',
|
|
url: '/anotherpage',
|
|
id: 'b',
|
|
},
|
|
],
|
|
};
|
|
this.props.onNavChanged({
|
|
main: node,
|
|
node,
|
|
});
|
|
}
|
|
render() {
|
|
return <p>my great plugin</p>;
|
|
}
|
|
}
|
|
|
|
const plugin = new AppPlugin();
|
|
plugin.root = RootComponent;
|
|
|
|
importAppPluginMock.mockResolvedValue(plugin);
|
|
|
|
renderWithStore();
|
|
|
|
// check that plugin and nav links were rendered, and plugin is mounted only once
|
|
await screen.findByText('my great plugin');
|
|
await screen.findAllByRole('link', { name: /A page/ });
|
|
await screen.findAllByRole('link', { name: /Another page/ });
|
|
expect(timesMounted).toEqual(1);
|
|
});
|
|
|
|
it('should not render component if not at plugin path', async () => {
|
|
getPluginSettingsMock.mockResolvedValue(
|
|
getMockPlugin({
|
|
type: PluginType.app,
|
|
enabled: true,
|
|
})
|
|
);
|
|
|
|
let timesRendered = 0;
|
|
class RootComponent extends Component<AppRootProps> {
|
|
render() {
|
|
timesRendered += 1;
|
|
return <p>my great component</p>;
|
|
}
|
|
}
|
|
|
|
const plugin = new AppPlugin();
|
|
plugin.root = RootComponent;
|
|
|
|
importAppPluginMock.mockResolvedValue(plugin);
|
|
|
|
const store = createStore(createRootReducer());
|
|
store.dispatch(updateLocation({ path: '/a/foo' }));
|
|
render(
|
|
<Provider store={store}>
|
|
<AppRootPage />
|
|
</Provider>
|
|
);
|
|
await screen.findByText('my great component');
|
|
|
|
// renders the first time
|
|
expect(timesRendered).toEqual(1);
|
|
|
|
await act(async () => {
|
|
await store.dispatch(
|
|
updateLocation({
|
|
path: '/foo',
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(timesRendered).toEqual(1);
|
|
|
|
await act(async () => {
|
|
await store.dispatch(
|
|
updateLocation({
|
|
path: '/a/foo',
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(timesRendered).toEqual(2);
|
|
});
|
|
});
|