AppRootPage.test.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { act, render, screen } from '@testing-library/react';
  2. import React, { Component } from 'react';
  3. import { Route, Router } from 'react-router-dom';
  4. import { AppPlugin, PluginType, AppRootProps, NavModelItem } from '@grafana/data';
  5. import { locationService, setEchoSrv } from '@grafana/runtime';
  6. import { GrafanaRoute } from 'app/core/navigation/GrafanaRoute';
  7. import { Echo } from 'app/core/services/echo/Echo';
  8. import { getMockPlugin } from '../__mocks__/pluginMocks';
  9. import { getPluginSettings } from '../pluginSettings';
  10. import { importAppPlugin } from '../plugin_loader';
  11. import AppRootPage from './AppRootPage';
  12. jest.mock('../pluginSettings', () => ({
  13. getPluginSettings: jest.fn(),
  14. }));
  15. jest.mock('../plugin_loader', () => ({
  16. importAppPlugin: jest.fn(),
  17. }));
  18. const importAppPluginMock = importAppPlugin as jest.Mock<
  19. ReturnType<typeof importAppPlugin>,
  20. Parameters<typeof importAppPlugin>
  21. >;
  22. const getPluginSettingsMock = getPluginSettings as jest.Mock<
  23. ReturnType<typeof getPluginSettings>,
  24. Parameters<typeof getPluginSettings>
  25. >;
  26. class RootComponent extends Component<AppRootProps> {
  27. static timesMounted = 0;
  28. componentDidMount() {
  29. RootComponent.timesMounted += 1;
  30. const node: NavModelItem = {
  31. text: 'My Great plugin',
  32. children: [
  33. {
  34. text: 'A page',
  35. url: '/apage',
  36. id: 'a',
  37. },
  38. {
  39. text: 'Another page',
  40. url: '/anotherpage',
  41. id: 'b',
  42. },
  43. ],
  44. };
  45. this.props.onNavChanged({
  46. main: node,
  47. node,
  48. });
  49. }
  50. render() {
  51. return <p>my great plugin</p>;
  52. }
  53. }
  54. function renderUnderRouter() {
  55. const route = { component: AppRootPage };
  56. locationService.push('/a/my-awesome-plugin');
  57. render(
  58. <Router history={locationService.getHistory()}>
  59. <Route path="/a/:pluginId" exact render={(props) => <GrafanaRoute {...props} route={route as any} />} />
  60. </Router>
  61. );
  62. }
  63. describe('AppRootPage', () => {
  64. beforeEach(() => {
  65. jest.resetAllMocks();
  66. setEchoSrv(new Echo());
  67. });
  68. it('should not mount plugin twice if nav is changed', async () => {
  69. // reproduces https://github.com/grafana/grafana/pull/28105
  70. getPluginSettingsMock.mockResolvedValue(
  71. getMockPlugin({
  72. type: PluginType.app,
  73. enabled: true,
  74. })
  75. );
  76. const plugin = new AppPlugin();
  77. plugin.root = RootComponent;
  78. importAppPluginMock.mockResolvedValue(plugin);
  79. renderUnderRouter();
  80. // check that plugin and nav links were rendered, and plugin is mounted only once
  81. expect(await screen.findByText('my great plugin')).toBeVisible();
  82. expect(await screen.findByLabelText('Tab A page')).toBeVisible();
  83. expect(await screen.findByLabelText('Tab Another page')).toBeVisible();
  84. expect(RootComponent.timesMounted).toEqual(1);
  85. });
  86. it('should not render component if not at plugin path', async () => {
  87. getPluginSettingsMock.mockResolvedValue(
  88. getMockPlugin({
  89. type: PluginType.app,
  90. enabled: true,
  91. })
  92. );
  93. class RootComponent extends Component<AppRootProps> {
  94. static timesRendered = 0;
  95. render() {
  96. RootComponent.timesRendered += 1;
  97. return <p>my great component</p>;
  98. }
  99. }
  100. const plugin = new AppPlugin();
  101. plugin.root = RootComponent;
  102. importAppPluginMock.mockResolvedValue(plugin);
  103. renderUnderRouter();
  104. expect(await screen.findByText('my great component')).toBeVisible();
  105. // renders the first time
  106. expect(RootComponent.timesRendered).toEqual(1);
  107. await act(async () => {
  108. locationService.push('/foo');
  109. });
  110. expect(RootComponent.timesRendered).toEqual(1);
  111. await act(async () => {
  112. locationService.push('/a/my-awesome-plugin');
  113. });
  114. expect(RootComponent.timesRendered).toEqual(2);
  115. });
  116. });