From d5a31fa52e562c3f0ec40e94b729788af9e4d8f5 Mon Sep 17 00:00:00 2001 From: Domas Date: Wed, 6 Jan 2021 09:31:20 +0200 Subject: [PATCH] Plugins: prevent app plugin from rendering with wrong location (#30017) --- .../app/features/plugins/AppRootPage.test.tsx | 59 ++++++++++++++++++- public/app/features/plugins/AppRootPage.tsx | 4 ++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/public/app/features/plugins/AppRootPage.test.tsx b/public/app/features/plugins/AppRootPage.test.tsx index 6c16651c5df..192ef85388d 100644 --- a/public/app/features/plugins/AppRootPage.test.tsx +++ b/public/app/features/plugins/AppRootPage.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import React, { Component } from 'react'; import { StoreState } from 'app/types'; import { Provider } from 'react-redux'; @@ -8,6 +8,9 @@ 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(), @@ -110,4 +113,58 @@ describe('AppRootPage', () => { 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 { + render() { + timesRendered += 1; + return

my great component

; + } + } + + const plugin = new AppPlugin(); + plugin.root = RootComponent; + + importAppPluginMock.mockResolvedValue(plugin); + + const store = createStore(createRootReducer()); + store.dispatch(updateLocation({ path: '/a/foo' })); + render( + + + + ); + 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); + }); }); diff --git a/public/app/features/plugins/AppRootPage.tsx b/public/app/features/plugins/AppRootPage.tsx index 2db6db587c1..3254a63403b 100644 --- a/public/app/features/plugins/AppRootPage.tsx +++ b/public/app/features/plugins/AppRootPage.tsx @@ -50,6 +50,10 @@ class AppRootPage extends Component { }; } + shouldComponentUpdate(nextProps: Props) { + return nextProps.path.startsWith('/a/'); + } + async componentDidMount() { const { pluginId } = this.props;