grafana/public/app/features/plugins/components/AppPluginLoader.test.tsx
Levente Balogh 9a85a2e441
Data Connections: Create a new top-level page (#50018)
* Feature Flags: introduce a flag for enabling the Data Connections page

* Feature Flags: generate schemas

* Navigation: add navigation weight for the Data Connections page

* NavLink: add a comment pointing out where icon names can be looked up

* NavTree: add a new page called Data Connections

* fix(Api): prefix the navigation IDs with the parent ("data-connections")

* feat(Frontend): add a basic page with four tabs

* feat(Plugins): add a hook for importing an app plugin

* feat(Plugins): add a component for loading app plugins anywhere

* feat(Data Connections): load the cloud-onboarding app under the "Cloud onboarding" tab

* feat(Data Connections): generate a proper nav model to highlight active tabs

* test(Data Connections): add tests

* refactor(Data Connections): update temporary text content

This is only used as a placeholder until the tabs are under development.

* refactor(Data Cnnnections): move /pages to /tabs

* refactor(Data Connections): remove the `types.ts` file as it is not referenced by any module

* feat(Data Connections): only register routes if feature is enabled
2022-06-10 12:13:31 +02:00

133 lines
3.7 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import React, { Component } from 'react';
import { Router } from 'react-router-dom';
import { AppPlugin, PluginType, AppRootProps, NavModelItem } from '@grafana/data';
import { locationService, setEchoSrv } from '@grafana/runtime';
import { Echo } from 'app/core/services/echo/Echo';
import { getMockPlugin } from '../__mocks__/pluginMocks';
import { useImportAppPlugin } from '../hooks/useImportAppPlugin';
import { AppPluginLoader } from './AppPluginLoader';
jest.mock('../hooks/useImportAppPlugin', () => ({
useImportAppPlugin: jest.fn(),
}));
const useImportAppPluginMock = useImportAppPlugin as jest.Mock<
ReturnType<typeof useImportAppPlugin>,
Parameters<typeof useImportAppPlugin>
>;
const TEXTS = {
PLUGIN_TITLE: 'Amazing App',
PLUGIN_CONTENT: 'This is my amazing app plugin!',
PLUGIN_TAB_TITLE_A: 'Tab (A)',
PLUGIN_TAB_TITLE_B: 'Tab (B)',
};
describe('AppPluginLoader', () => {
beforeEach(() => {
jest.resetAllMocks();
AppPluginComponent.timesMounted = 0;
setEchoSrv(new Echo());
});
test('renders the app plugin correctly', async () => {
useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined });
renderAppPlugin();
expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible();
expect(await screen.findByText(TEXTS.PLUGIN_CONTENT)).toBeVisible();
expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_A}`)).toBeVisible();
expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_B}`)).toBeVisible();
expect(screen.queryByText('Loading ...')).not.toBeInTheDocument();
});
test('renders the app plugin only once', async () => {
useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined });
renderAppPlugin();
expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible();
expect(AppPluginComponent.timesMounted).toEqual(1);
});
test('renders a loader while the plugin is loading', async () => {
useImportAppPluginMock.mockReturnValue({ value: undefined, loading: true, error: undefined });
renderAppPlugin();
expect(await screen.findByText('Loading ...')).toBeVisible();
expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument();
});
test('renders an error message if there are any errors while importing the plugin', async () => {
const errorMsg = 'Unable to find plugin';
useImportAppPluginMock.mockReturnValue({ value: undefined, loading: false, error: new Error(errorMsg) });
renderAppPlugin();
expect(await screen.findByText(errorMsg)).toBeVisible();
expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument();
});
});
function renderAppPlugin() {
render(
<Router history={locationService.getHistory()}>
<AppPluginLoader id="foo" />;
</Router>
);
}
class AppPluginComponent extends Component<AppRootProps> {
static timesMounted = 0;
componentDidMount() {
AppPluginComponent.timesMounted += 1;
const node: NavModelItem = {
text: TEXTS.PLUGIN_TITLE,
children: [
{
text: TEXTS.PLUGIN_TAB_TITLE_A,
url: '/tab-a',
id: 'a',
},
{
text: TEXTS.PLUGIN_TAB_TITLE_B,
url: '/tab-b',
id: 'b',
},
],
};
this.props.onNavChanged({
main: node,
node,
});
}
render() {
return <p>{TEXTS.PLUGIN_CONTENT}</p>;
}
}
function getAppPluginMeta() {
return getMockPlugin({
type: PluginType.app,
enabled: true,
});
}
function getAppPluginMock() {
const plugin = new AppPlugin();
plugin.root = AppPluginComponent;
plugin.init(getAppPluginMeta());
return plugin;
}