AppChrome: Unify logic for chromeless pages that should not have NavBar, CommandPalette, Search etc (#62281)

* Keybindings: No global keybindings on chromeless pages

* simplify condition

* Refactoring

* Align name and file

* Move logic into AppChrome

* minor fix

* Update Page.tsx

* Fixing test

* Fixed tests

* More fixes

* Fixed more tests

* Fixing final test

* Fixed search in old nav
This commit is contained in:
Torkel Ödegaard
2023-02-02 09:53:06 +01:00
committed by GitHub
parent ce50168b70
commit b8e7ef48d0
63 changed files with 318 additions and 497 deletions

View File

@@ -5,7 +5,6 @@ import { Router, Route, Redirect, Switch } from 'react-router-dom';
import { config, locationService, navigationLogger, reportInteraction } from '@grafana/runtime';
import { ErrorBoundaryAlert, GlobalStyles, ModalRoot, ModalsProvider, PortalContainer } from '@grafana/ui';
import { SearchWrapper } from 'app/features/search';
import { getAppRoutes } from 'app/routes/routes';
import { store } from 'app/store/store';
@@ -14,13 +13,11 @@ import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabl
import { GrafanaApp } from './app';
import { AppChrome } from './core/components/AppChrome/AppChrome';
import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList';
import { NavBar } from './core/components/NavBar/NavBar';
import { GrafanaContext } from './core/context/GrafanaContext';
import { GrafanaRoute } from './core/navigation/GrafanaRoute';
import { RouteDescriptor } from './core/navigation/types';
import { contextSrv } from './core/services/context_srv';
import { ThemeProvider } from './core/utils/ConfigProvider';
import { CommandPalette } from './features/commandPalette/CommandPalette';
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
interface AppWrapperProps {
@@ -82,23 +79,6 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
return <Switch>{getAppRoutes().map((r) => this.renderRoute(r))}</Switch>;
}
renderNavBar() {
if (config.isPublicDashboardView || !this.state.ready || config.featureToggles.topnav) {
return null;
}
return <NavBar />;
}
commandPaletteEnabled() {
const isLoginPage = locationService.getLocation().pathname === '/login';
return config.featureToggles.commandPalette && !config.isPublicDashboardView && !isLoginPage;
}
searchBarEnabled() {
return !config.isPublicDashboardView;
}
render() {
const { app } = this.props;
const { ready } = this.state;
@@ -124,18 +104,14 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
>
<ModalsProvider>
<GlobalStyles />
{this.commandPaletteEnabled() && <CommandPalette />}
<div className="grafana-app">
<Router history={locationService.getHistory()}>
{this.renderNavBar()}
<AppChrome>
{pageBanners.map((Banner, index) => (
<Banner key={index.toString()} />
))}
<AngularRoot />
<AppNotificationList />
{this.searchBarEnabled() && <SearchWrapper />}
{ready && this.renderRoutes()}
{bodyRenderHooks.map((Hook, index) => (
<Hook key={index.toString()} />

View File

@@ -11,7 +11,7 @@ import appEvents from 'app/core/app_events';
import config from 'app/core/config';
import { ContextSrv } from 'app/core/services/context_srv';
import { initGrafanaLive } from 'app/features/live';
import { CoreEvents, AppEventEmitter, AppEventConsumer } from 'app/types';
import { AppEventEmitter, AppEventConsumer } from 'app/types';
import { UtilSrv } from './services/UtilSrv';
@@ -89,10 +89,6 @@ export function grafanaAppDirective() {
// see https://github.com/zenorocha/clipboard.js/issues/155
$.fn.modal.Constructor.prototype.enforceFocus = () => {};
appEvents.on(CoreEvents.toggleSidemenuHidden, () => {
body.toggleClass('sidemenu-hidden');
});
// handle in active view state class
let lastActivity = new Date().getTime();
let activeUser = true;

View File

@@ -5,9 +5,12 @@ import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext';
import { CommandPalette } from 'app/features/commandPalette/CommandPalette';
import { SearchWrapper } from 'app/features/search';
import { KioskMode } from 'app/types';
import { MegaMenu } from '../MegaMenu/MegaMenu';
import { NavBar } from '../NavBar/NavBar';
import { NavToolbar } from './NavToolbar';
import { TopSearchBar } from './TopSearchBar';
@@ -19,9 +22,21 @@ export function AppChrome({ children }: Props) {
const styles = useStyles2(getStyles);
const { chrome } = useGrafana();
const state = chrome.useState();
const featureToggles = config.featureToggles;
if (!config.featureToggles.topnav) {
return <main className="main-view">{children}</main>;
return (
<>
{!state.chromeless && (
<>
<NavBar />
<SearchWrapper />
{featureToggles.commandPalette && <CommandPalette />}
</>
)}
<main className="main-view">{children}</main>
</>
);
}
const searchBarHidden = state.searchBarHidden || state.kioskMode === KioskMode.TV;
@@ -32,24 +47,33 @@ export function AppChrome({ children }: Props) {
[styles.contentChromeless]: state.chromeless,
});
// Chromeless routes are without topNav, mega menu, search & command palette
if (state.chromeless) {
return (
<main className="main-view">
<div className={contentClass}>{children}</div>
</main>
);
}
return (
<main className="main-view">
{!state.chromeless && (
<div className={cx(styles.topNav)}>
{!searchBarHidden && <TopSearchBar />}
<NavToolbar
searchBarHidden={searchBarHidden}
sectionNav={state.sectionNav}
pageNav={state.pageNav}
actions={state.actions}
onToggleSearchBar={chrome.onToggleSearchBar}
onToggleMegaMenu={chrome.onToggleMegaMenu}
onToggleKioskMode={chrome.onToggleKioskMode}
/>
</div>
)}
<div className={cx(styles.topNav)}>
{!searchBarHidden && <TopSearchBar />}
<NavToolbar
searchBarHidden={searchBarHidden}
sectionNav={state.sectionNav}
pageNav={state.pageNav}
actions={state.actions}
onToggleSearchBar={chrome.onToggleSearchBar}
onToggleMegaMenu={chrome.onToggleMegaMenu}
onToggleKioskMode={chrome.onToggleKioskMode}
/>
</div>
<div className={contentClass}>{children}</div>
{!state.chromeless && <MegaMenu searchBarHidden={searchBarHidden} onClose={() => chrome.setMegaMenu(false)} />}
<MegaMenu searchBarHidden={searchBarHidden} onClose={() => chrome.setMegaMenu(false)} />
{featureToggles.commandPalette && <CommandPalette />}
{!featureToggles.topNavCommandPalette && <SearchWrapper />}
</main>
);
}

View File

@@ -1,9 +1,8 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { config } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { NavLandingPage } from './NavLandingPage';
@@ -51,11 +50,10 @@ describe('NavLandingPage', () => {
},
];
const store = configureStore();
return render(
<Provider store={store}>
<TestProvider>
<NavLandingPage navId={mockId} />
</Provider>
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { OrgRole } from '@grafana/data';
import { ContextSrv, setContextSrv } from 'app/core/services/context_srv';
import { getUserOrganizations } from 'app/features/org/state/actions';
import { configureStore } from 'app/store/configureStore';
import * as appTypes from 'app/types';
import { OrganizationSwitcher } from './OrganizationSwitcher';
@@ -22,12 +21,10 @@ jest.mock('app/types', () => ({
}));
const renderWithProvider = ({ initialState }: { initialState?: Partial<appTypes.StoreState> }) => {
const store = configureStore(initialState);
render(
<Provider store={store}>
<TestProvider storeState={initialState}>
<OrganizationSwitcher />
</Provider>
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModelItem, NavSection } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { QuickAdd } from './QuickAdd';
@@ -37,12 +36,10 @@ const setup = () => {
},
];
const store = configureStore({ navBarTree });
return render(
<Provider store={store}>
<TestProvider storeState={{ navBarTree }}>
<QuickAdd />
</Provider>
</TestProvider>
);
};

View File

@@ -1,15 +1,12 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { NavModelItem, NavSection } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { configureStore } from 'app/store/configureStore';
import TestProvider from '../../../../test/helpers/TestProvider';
import { TestProvider } from '../../../../test/helpers/TestProvider';
import { MegaMenu } from './MegaMenu';
@@ -33,21 +30,15 @@ const setup = () => {
},
];
const context = getGrafanaContextMock();
const store = configureStore({ navBarTree });
context.chrome.onToggleMegaMenu();
const grafanaContext = getGrafanaContextMock();
grafanaContext.chrome.onToggleMegaMenu();
return render(
<Provider store={store}>
<GrafanaContext.Provider value={context}>
<TestProvider>
<Router history={locationService.getHistory()}>
<MegaMenu onClose={() => {}} />
</Router>
</TestProvider>
</GrafanaContext.Provider>
</Provider>
<TestProvider storeState={{ navBarTree }} grafanaContext={grafanaContext}>
<Router history={locationService.getHistory()}>
<MegaMenu onClose={() => {}} />
</Router>
</TestProvider>
);
};

View File

@@ -1,12 +1,9 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { locationService } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import TestProvider from '../../../../test/helpers/TestProvider';
import { TestProvider } from '../../../../test/helpers/TestProvider';
import { NavBar } from './NavBar';
@@ -22,16 +19,10 @@ jest.mock('app/core/services/context_srv', () => ({
}));
const setup = () => {
const store = configureStore();
return render(
<Provider store={store}>
<TestProvider>
<Router history={locationService.getHistory()}>
<NavBar />
</Router>
</TestProvider>
</Provider>
<TestProvider>
<NavBar />
</TestProvider>
);
};

View File

@@ -192,10 +192,6 @@ const getStyles = (theme: GrafanaTheme2) => ({
navWrapper: css({
position: 'relative',
display: 'flex',
'.sidemenu-hidden &': {
display: 'none',
},
}),
sidemenu: css({
label: 'sidemenu',

View File

@@ -6,8 +6,6 @@ import { BrowserRouter } from 'react-router-dom';
import { locationUtil } from '@grafana/data';
import { config, setLocationService } from '@grafana/runtime';
import TestProvider from '../../../../test/helpers/TestProvider';
// Need to mock createBrowserHistory here to avoid errors
jest.mock('history', () => ({
...jest.requireActual('history'),
@@ -45,18 +43,16 @@ async function getTestContext(overrides: Partial<Props> = {}, subUrl = '', isMen
const props = { ...defaults, ...overrides };
const { rerender } = render(
<TestProvider>
<BrowserRouter>
<NavBarContext.Provider
value={{
menuIdOpen: isMenuOpen ? props.link.id : undefined,
setMenuIdOpen: setMenuIdOpenMock,
}}
>
<NavBarItem {...props} />
</NavBarContext.Provider>
</BrowserRouter>
</TestProvider>
<BrowserRouter>
<NavBarContext.Provider
value={{
menuIdOpen: isMenuOpen ? props.link.id : undefined,
setMenuIdOpen: setMenuIdOpenMock,
}}
>
<NavBarItem {...props} />
</NavBarContext.Provider>
</BrowserRouter>
);
// Need to click this first to set the correct selection range

View File

@@ -1,10 +1,9 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModelItem } from '@grafana/data';
import { config } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { Page } from './Page';
import { PageProps } from './types';
@@ -30,14 +29,12 @@ const setup = (props: Partial<PageProps>) => {
},
];
const store = configureStore();
return render(
<Provider store={store}>
<TestProvider>
<Page {...props}>
<div data-testid="page-children">Children</div>
</Page>
</Provider>
</TestProvider>
);
};

View File

@@ -1,10 +1,11 @@
// Libraries
import { css, cx } from '@emotion/css';
import React from 'react';
import React, { useEffect } from 'react';
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext';
import { Footer } from '../Footer/Footer';
import { PageHeader } from '../PageHeader/PageHeader';
@@ -34,11 +35,21 @@ export const OldPage: PageType = ({
}) => {
const styles = useStyles2(getStyles);
const navModel = usePageNav(navId, oldNavProp);
const { chrome } = useGrafana();
usePageTitle(navModel, pageNav);
const pageHeaderNav = pageNav ?? navModel?.main;
useEffect(() => {
if (navModel) {
// This is needed for chrome to update it's chromeless state
chrome.update({
sectionNav: navModel.node,
});
}
}, [navModel, chrome]);
return (
<div className={cx(styles.wrapper, className)} {...otherProps}>
{layout === PageLayoutType.Standard && (

View File

@@ -1,13 +1,11 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { NavModelItem } from '@grafana/data';
import { config } from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { HOME_NAV_ID } from 'app/core/reducers/navModel';
import { configureStore } from 'app/store/configureStore';
import { PageProps } from '../Page/types';
@@ -38,16 +36,13 @@ const setup = (props: Partial<PageProps>) => {
];
const context = getGrafanaContextMock();
const store = configureStore();
const renderResult = render(
<Provider store={store}>
<GrafanaContext.Provider value={context}>
<Page {...props}>
<div data-testid="page-children">Children</div>
</Page>
</GrafanaContext.Provider>
</Provider>
<TestProvider grafanaContext={context}>
<Page {...props}>
<div data-testid="page-children">Children</div>
</Page>
</TestProvider>
);
return { renderResult, context };

View File

@@ -1,7 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import TestProvider from 'test/helpers/TestProvider';
import { assertInstanceOf } from 'test/helpers/asserts';
import { getSelectParent, selectOptionInTest } from 'test/helpers/selectOptionInTest';
@@ -124,11 +123,7 @@ describe('SharedPreferences', () => {
mockReload.mockReset();
mockPrefsUpdate.mockReset();
render(
<TestProvider>
<SharedPreferences {...props} />
</TestProvider>
);
render(<SharedPreferences {...props} />);
await waitFor(() => expect(mockPrefsLoad).toHaveBeenCalled());
});

View File

@@ -1,19 +1,14 @@
import { render, screen } from '@testing-library/react';
import React, { ComponentType } from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { TestProvider } from 'test/helpers/TestProvider';
import { setEchoSrv } from '@grafana/runtime';
import { configureStore } from '../../store/configureStore';
import { GrafanaContext } from '../context/GrafanaContext';
import { Echo } from '../services/echo/Echo';
import { GrafanaRoute, Props } from './GrafanaRoute';
function setup(overrides: Partial<Props>) {
const store = configureStore();
const props: Props = {
location: { search: '?query=hello&test=asd' } as any,
history: {} as any,
@@ -26,13 +21,9 @@ function setup(overrides: Partial<Props>) {
};
render(
<BrowserRouter>
<GrafanaContext.Provider value={getGrafanaContextMock()}>
<Provider store={store}>
<GrafanaRoute {...props} />
</Provider>
</GrafanaContext.Provider>
</BrowserRouter>
<TestProvider>
<GrafanaRoute {...props} />
</TestProvider>
);
}

View File

@@ -19,8 +19,8 @@ export function GrafanaRoute(props: Props) {
chrome.setMatchedRoute(props.route);
useLayoutEffect(() => {
keybindings.clearAndInitGlobalBindings();
}, [keybindings]);
keybindings.clearAndInitGlobalBindings(props.route);
}, [keybindings, props.route]);
useEffect(() => {
updateBodyClassNames(props.route);

View File

@@ -23,6 +23,7 @@ import {
import { AppChromeService } from '../components/AppChrome/AppChromeService';
import { HelpModal } from '../components/help/HelpModal';
import { contextSrv } from '../core';
import { RouteDescriptor } from '../navigation/types';
import { toggleTheme } from './theme';
import { withFocusedPanel } from './withFocusedPanelId';
@@ -30,10 +31,11 @@ import { withFocusedPanel } from './withFocusedPanelId';
export class KeybindingSrv {
constructor(private locationService: LocationService, private chromeService: AppChromeService) {}
clearAndInitGlobalBindings() {
clearAndInitGlobalBindings(route: RouteDescriptor) {
Mousetrap.reset();
if (this.locationService.getLocation().pathname !== '/login') {
// Chromeless pages like login and signup page don't get any global bindings
if (!route.chromeless) {
this.bind(['?', 'h'], this.showHelpModal);
this.bind('g h', this.goToHome);
this.bind('g a', this.openAlerting);

View File

@@ -1,15 +1,14 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { openMenu } from 'react-select-event';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { TestProvider } from 'test/helpers/TestProvider';
import { locationService } from '@grafana/runtime';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import appEvents from '../../core/app_events';
import { configureStore } from '../../store/configureStore';
import { ShowModalReactEvent } from '../../types/events';
import { AlertHowToModal } from './AlertHowToModal';
@@ -31,20 +30,19 @@ const defaultProps: Props = {
};
const setup = (propOverrides?: object) => {
const store = configureStore();
const props: Props = {
...defaultProps,
...propOverrides,
};
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<AlertRuleListUnconnected {...props} />
</Provider>
</TestProvider>
);
return {
rerender: (element: JSX.Element) => rerender(<Provider store={store}>{element}</Provider>),
rerender: (element: JSX.Element) => rerender(<TestProvider>{element}</TestProvider>),
};
};

View File

@@ -1,12 +1,10 @@
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { locationService, setDataSourceSrv } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { setDataSourceSrv } from '@grafana/runtime';
import AlertGroups from './AlertGroups';
import { fetchAlertGroups } from './api/alertmanager';
@@ -28,14 +26,10 @@ const mocks = {
};
const renderAmNotifications = () => {
const store = configureStore();
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<AlertGroups />
</Router>
</Provider>
<TestProvider>
<AlertGroups />
</TestProvider>
);
};

View File

@@ -1,11 +1,9 @@
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byTestId } from 'testing-library-selector';
import { configureStore } from 'app/store/configureStore';
import { FolderState } from 'app/types';
import { CombinedRuleNamespace } from 'app/types/unified-alerting';
@@ -47,7 +45,6 @@ const mockFolder = (folderOverride: Partial<FolderState> = {}): FolderState => {
describe('AlertsFolderView tests', () => {
it('Should display grafana alert rules when the namespace name matches the folder name', () => {
// Arrange
const store = configureStore();
const folder = mockFolder();
const grafanaNamespace: CombinedRuleNamespace = {
@@ -77,11 +74,9 @@ describe('AlertsFolderView tests', () => {
// Act
render(
<Provider store={store}>
<MemoryRouter>
<AlertsFolderView folder={folder} />
</MemoryRouter>
</Provider>
<TestProvider>
<AlertsFolderView folder={folder} />
</TestProvider>
);
// Assert
@@ -97,7 +92,6 @@ describe('AlertsFolderView tests', () => {
it('Should not display alert rules when the namespace name does not match the folder name', () => {
// Arrange
const store = configureStore();
const folder = mockFolder();
const grafanaNamespace: CombinedRuleNamespace = {
@@ -118,11 +112,9 @@ describe('AlertsFolderView tests', () => {
// Act
render(
<Provider store={store}>
<MemoryRouter>
<AlertsFolderView folder={folder} />
</MemoryRouter>
</Provider>
<TestProvider>
<AlertsFolderView folder={folder} />
</TestProvider>
);
// Assert
@@ -131,7 +123,6 @@ describe('AlertsFolderView tests', () => {
it('Should filter alert rules by the name, case insensitive', async () => {
// Arrange
const store = configureStore();
const folder = mockFolder();
const grafanaNamespace: CombinedRuleNamespace = {
@@ -149,11 +140,9 @@ describe('AlertsFolderView tests', () => {
// Act
render(
<Provider store={store}>
<MemoryRouter>
<AlertsFolderView folder={folder} />
</MemoryRouter>
</Provider>
<TestProvider>
<AlertsFolderView folder={folder} />
</TestProvider>
);
await userEvent.type(ui.filter.name.get(), 'cpu');
@@ -165,7 +154,6 @@ describe('AlertsFolderView tests', () => {
it('Should filter alert rule by labels', async () => {
// Arrange
const store = configureStore();
const folder = mockFolder();
const grafanaNamespace: CombinedRuleNamespace = {
@@ -186,11 +174,9 @@ describe('AlertsFolderView tests', () => {
// Act
render(
<Provider store={store}>
<MemoryRouter>
<AlertsFolderView folder={folder} />
</MemoryRouter>
</Provider>
<TestProvider>
<AlertsFolderView folder={folder} />
</TestProvider>
);
await userEvent.type(ui.filter.label.get(), 'severity=critical');

View File

@@ -1,8 +1,7 @@
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector';
@@ -15,7 +14,6 @@ import {
MuteTimeInterval,
Route,
} from 'app/plugins/datasource/alertmanager/types';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import AmRoutes from './AmRoutes';
@@ -47,19 +45,15 @@ const mocks = {
const useGetGrafanaReceiverTypeCheckerMock = jest.spyOn(grafanaApp, 'useGetGrafanaReceiverTypeChecker');
const renderAmRoutes = (alertManagerSourceName?: string) => {
const store = configureStore();
locationService.push(location);
locationService.push(
'/alerting/routes' + (alertManagerSourceName ? `?${ALERTMANAGER_NAME_QUERY_KEY}=${alertManagerSourceName}` : '')
);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<AmRoutes />
</Router>
</Provider>
<TestProvider>
<AmRoutes />
</TestProvider>
);
};

View File

@@ -2,8 +2,7 @@ import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/rea
import { setupServer } from 'msw/node';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { selectors } from '@grafana/e2e-selectors/src';
@@ -91,11 +90,9 @@ function getProvidersWrapper() {
const formApi = useForm<RuleFormValues>({ defaultValues: getDefaultFormValues() });
return (
<MemoryRouter>
<Provider store={store}>
<FormProvider {...formApi}>{children}</FormProvider>
</Provider>
</MemoryRouter>
<TestProvider store={store}>
<FormProvider {...formApi}>{children}</FormProvider>
</TestProvider>
);
};
}

View File

@@ -1,13 +1,11 @@
import { render, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { locationService, setDataSourceSrv } from '@grafana/runtime';
import { AlertManagerCortexConfig, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
import { configureStore } from 'app/store/configureStore';
import MuteTimings from './MuteTimings';
import { fetchAlertManagerConfig, updateAlertManagerConfig } from './api/alertmanager';
@@ -24,15 +22,12 @@ const mocks = {
};
const renderMuteTimings = (location = '/alerting/routes/mute-timing/new') => {
const store = configureStore();
locationService.push(location);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<MuteTimings />
</Router>
</Provider>
<TestProvider>
<MuteTimings />
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, act, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byTestId } from 'testing-library-selector';
import { DataSourceApi } from '@grafana/data';
import { locationService, setDataSourceSrv } from '@grafana/runtime';
import { setDataSourceSrv } from '@grafana/runtime';
import * as ruleActionButtons from 'app/features/alerting/unified/components/rules/RuleActionsButtons';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
@@ -69,15 +68,11 @@ const renderAlertTabContent = (
panel: PanelModel,
initialStore?: ReturnType<typeof configureStore>
) => {
const store = initialStore ?? configureStore();
return act(async () => {
render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<PanelAlertTabContent dashboard={dashboard} panel={panel} />
</Router>
</Provider>
<TestProvider store={initialStore}>
<PanelAlertTabContent dashboard={dashboard} panel={panel} />
</TestProvider>
);
});
};

View File

@@ -2,8 +2,7 @@ import { render, waitFor, within, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { setupServer } from 'msw/node';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector';
@@ -17,7 +16,6 @@ import {
AlertManagerDataSourceJsonData,
AlertManagerImplementation,
} from 'app/plugins/datasource/alertmanager/types';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction, ContactPointsState } from 'app/types';
import 'whatwg-fetch';
@@ -70,19 +68,15 @@ const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
};
const renderReceivers = (alertManagerSourceName?: string) => {
const store = configureStore();
locationService.push(
'/alerting/notifications' +
(alertManagerSourceName ? `?${ALERTMANAGER_NAME_QUERY_KEY}=${alertManagerSourceName}` : '')
);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<Receivers />
</Router>
</Provider>
<TestProvider>
<Receivers />
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { useLocation } from 'react-use';
import { TestProvider } from 'test/helpers/TestProvider';
import { DataSourceJsonData, PluginMeta } from '@grafana/data';
import { configureStore } from 'app/store/configureStore';
import { locationService } from '@grafana/runtime';
import { CombinedRule, Rule } from '../../../types/unified-alerting';
import { PromRuleType } from '../../../types/unified-alerting-dto';
@@ -24,16 +23,15 @@ jest.mock('react-router-dom', () => ({
jest.mock('react-use');
const store = configureStore();
const renderRedirectToRuleViewer = (pathname: string) => {
jest.mocked(useLocation).mockReturnValue({ pathname, trigger: '' });
locationService.push(pathname);
return render(
<Provider store={store}>
<MemoryRouter initialEntries={[pathname]}>
<RedirectToRuleViewer />
</MemoryRouter>
</Provider>
<TestProvider>
<RedirectToRuleViewer />
</TestProvider>
);
};

View File

@@ -1,8 +1,8 @@
import { render, waitFor, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Route, Router } from 'react-router-dom';
import { Route } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { ui } from 'test/helpers/alertingRuleEditor';
import { clickSelectOptionMatch } from 'test/helpers/selectOptionInTest';
import { byRole } from 'testing-library-selector';
@@ -11,7 +11,6 @@ import { locationService, setDataSourceSrv } from '@grafana/runtime';
import { ADD_NEW_FOLER_OPTION } from 'app/core/components/Select/FolderPicker';
import { contextSrv } from 'app/core/services/context_srv';
import { DashboardSearchHit } from 'app/features/search/types';
import { configureStore } from 'app/store/configureStore';
import { GrafanaAlertStateDecision } from 'app/types/unified-alerting-dto';
import { searchFolders } from '../../../../app/features/manage-dashboards/state/actions';
@@ -64,16 +63,12 @@ const mocks = {
};
function renderRuleEditor(identifier?: string) {
const store = configureStore();
locationService.push(identifier ? `/alerting/${identifier}/edit` : `/alerting/new`);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<Route path={['/alerting/new', '/alerting/:id/edit']} component={RuleEditor} />
</Router>
</Provider>
<TestProvider>
<Route path={['/alerting/new', '/alerting/:id/edit']} component={RuleEditor} />
</TestProvider>
);
}

View File

@@ -2,8 +2,7 @@ import { SerializedError } from '@reduxjs/toolkit';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { locationService, logInfo, setBackendSrv, setDataSourceSrv } from '@grafana/runtime';
@@ -11,7 +10,6 @@ import { backendSrv } from 'app/core/services/backend_srv';
import { contextSrv } from 'app/core/services/context_srv';
import * as ruleActionButtons from 'app/features/alerting/unified/components/rules/RuleActionsButtons';
import * as actions from 'app/features/alerting/unified/state/actions';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import { PromAlertingRuleState, PromApplication } from 'app/types/unified-alerting-dto';
@@ -76,15 +74,12 @@ const mocks = {
};
const renderRuleList = () => {
const store = configureStore();
locationService.push('/');
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<RuleList />
</Router>
</Provider>
<TestProvider>
<RuleList />
</TestProvider>
);
};

View File

@@ -1,14 +1,12 @@
import { act, render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole } from 'testing-library-selector';
import { locationService, setBackendSrv } from '@grafana/runtime';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { backendSrv } from 'app/core/services/backend_srv';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import { CombinedRule } from 'app/types/unified-alerting';
@@ -45,15 +43,12 @@ jest.mock('@grafana/runtime', () => ({
},
}));
const store = configureStore();
const renderRuleViewer = () => {
return act(async () => {
render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<RuleViewer {...mockRoute} />
</Router>
</Provider>
<TestProvider>
<RuleViewer {...mockRoute} />
</TestProvider>
);
});
};

View File

@@ -1,15 +1,13 @@
import { render, waitFor } from '@testing-library/react';
import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector';
import { dateTime } from '@grafana/data';
import { locationService, setDataSourceSrv, config } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { AlertState, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import Silences from './Silences';
@@ -33,15 +31,12 @@ const mocks = {
};
const renderSilences = (location = '/alerting/silences/') => {
const store = configureStore();
locationService.push(location);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<Silences />
</Router>
</Provider>
<TestProvider>
<Silences />
</TestProvider>
);
};

View File

@@ -1,8 +1,7 @@
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { byLabelText, byRole, byTestId } from 'testing-library-selector';
import { locationService, setDataSourceSrv } from '@grafana/runtime';
@@ -13,7 +12,6 @@ import {
AlertManagerDataSourceJsonData,
AlertManagerImplementation,
} from 'app/plugins/datasource/alertmanager/types';
import { configureStore } from 'app/store/configureStore';
import {
fetchAlertManagerConfig,
@@ -50,19 +48,15 @@ const mocks = {
};
const renderAdminPage = (alertManagerSourceName?: string) => {
const store = configureStore();
locationService.push(
'/alerting/notifications' +
(alertManagerSourceName ? `?${ALERTMANAGER_NAME_QUERY_KEY}=${alertManagerSourceName}` : '')
);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<AlertmanagerConfig />
</Router>
</Provider>
<TestProvider>
<AlertmanagerConfig />
</TestProvider>
);
};

View File

@@ -1,9 +1,7 @@
import { screen, render, within } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { locationService } from '@grafana/runtime';
import {
AlertManagerCortexConfig,
GrafanaManagedReceiverConfig,
@@ -31,11 +29,9 @@ const renderReceieversTable = async (receivers: Receiver[], notifiers: NotifierD
await store.dispatch(fetchGrafanaNotifiersAction.fulfilled(notifiers, 'initial'));
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<ReceiversTable config={config} alertManagerName="alertmanager-1" />
</Router>
</Provider>
<TestProvider store={store}>
<ReceiversTable config={config} alertManagerName="alertmanager-1" />
</TestProvider>
);
};

View File

@@ -4,7 +4,7 @@ import { rest } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId } from 'testing-library-selector';
import { setBackendSrv } from '@grafana/runtime';
@@ -63,11 +63,11 @@ function FormWrapper({ formValues }: { formValues?: Partial<RuleFormValues> }) {
const formApi = useForm<RuleFormValues>({ defaultValues: { ...getDefaultFormValues(), ...formValues } });
return (
<Provider store={store}>
<TestProvider store={store}>
<FormProvider {...formApi}>
<AnnotationsField />
</FormProvider>
</Provider>
</TestProvider>
);
}

View File

@@ -1,14 +1,13 @@
import { render, screen, within } from '@testing-library/react';
import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { selectors } from '@grafana/e2e-selectors';
import { ApiKey, OrgRole } from 'app/types';
import { mockToolkitActionCreator } from '../../../test/core/redux/mocks';
import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput';
import { configureStore } from '../../store/configureStore';
import { ApiKeysPageUnconnected, Props } from './ApiKeysPage';
import { getMultipleMockKeys } from './__mocks__/apiKeysMock';
@@ -24,7 +23,6 @@ jest.mock('app/core/core', () => {
});
const setup = (propOverrides: Partial<Props>) => {
const store = configureStore();
const loadApiKeysMock = jest.fn();
const deleteApiKeyMock = jest.fn();
const migrateApiKeyMock = jest.fn();
@@ -58,12 +56,12 @@ const setup = (propOverrides: Partial<Props>) => {
Object.assign(props, propOverrides);
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<ApiKeysPageUnconnected {...props} />
</Provider>
</TestProvider>
);
return {
rerender: (element: JSX.Element) => rerender(<Provider store={store}>{element}</Provider>),
rerender: (element: JSX.Element) => rerender(<TestProvider>{element}</TestProvider>),
props,
loadApiKeysMock,
setSearchQueryMock,

View File

@@ -26,7 +26,7 @@ import useActions from './actions/useActions';
import { CommandPaletteAction } from './types';
import { useMatches } from './useMatches';
export const CommandPalette = () => {
export function CommandPalette() {
const styles = useStyles2(getSearchStyles);
const { query, showing, searchQuery } = useKBar((state) => ({
@@ -43,6 +43,7 @@ export const CommandPalette = () => {
{ isOpen: showing, onClose: () => query.setVisualState(VisualState.animatingOut) },
ref
);
const { dialogProps } = useDialog({}, ref);
// Report interaction when opened
@@ -72,7 +73,7 @@ export const CommandPalette = () => {
</KBarPositioner>
</KBarPortal>
) : null;
};
}
interface RenderResultsProps {
dashboardResults: CommandPaletteAction[];

View File

@@ -1,7 +1,6 @@
import { render, RenderResult, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { locationService } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
@@ -25,11 +24,9 @@ const renderPage = (
locationService.push(path);
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<Connections />
</Router>
</Provider>
<TestProvider store={store}>
<Connections />
</TestProvider>
);
};
@@ -86,6 +83,7 @@ describe('Connections', () => {
url: '/connections/connect-data',
pluginId: 'grafana-easystart-app',
};
const connections = {
...navIndex.connections,
children: navIndex.connections.children?.map((child) => {
@@ -96,6 +94,7 @@ describe('Connections', () => {
return child;
}),
};
const store = configureStore({
navIndex: { ...navIndex, connections, [standalonePluginPage.id]: standalonePluginPage },
plugins: getPluginsStateMock([]),

View File

@@ -11,8 +11,8 @@ import {
import { merge, uniqueId } from 'lodash';
import React from 'react';
import { DeepPartial } from 'react-hook-form';
import { Provider } from 'react-redux';
import { Observable } from 'rxjs';
import { TestProvider } from 'test/helpers/TestProvider';
import { MockDataSourceApi } from 'test/mocks/datasource_srv';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
@@ -25,7 +25,6 @@ import {
BackendSrvRequest,
reportInteraction,
} from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
@@ -150,11 +149,9 @@ const renderWithContext = async (
setDataSourceSrv(dsServer);
const renderResult = render(
<Provider store={configureStore({})}>
<GrafanaContext.Provider value={grafanaContext}>
<CorrelationsPage />
</GrafanaContext.Provider>
</Provider>,
<TestProvider store={configureStore({})} grafanaContext={grafanaContext}>
<CorrelationsPage />
</TestProvider>,
{
queries: {
/**

View File

@@ -2,23 +2,18 @@ import { within } from '@testing-library/dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { TestProvider } from 'test/helpers/TestProvider';
import { selectors } from '@grafana/e2e-selectors';
import { locationService, setAngularLoader, setDataSourceSrv } from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks';
import { configureStore } from '../../../../store/configureStore';
import { DashboardModel } from '../../state/DashboardModel';
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
import { AnnotationsSettings } from './AnnotationsSettings';
function setup(dashboard: DashboardModel, editIndex?: number) {
const store = configureStore();
const sectionNav = {
main: { text: 'Dashboard' },
node: {
@@ -27,13 +22,9 @@ function setup(dashboard: DashboardModel, editIndex?: number) {
};
return render(
<GrafanaContext.Provider value={getGrafanaContextMock()}>
<Provider store={store}>
<BrowserRouter>
<AnnotationsSettings sectionNav={sectionNav} dashboard={dashboard} editIndex={editIndex} />
</BrowserRouter>
</Provider>
</GrafanaContext.Provider>
<TestProvider>
<AnnotationsSettings sectionNav={sectionNav} dashboard={dashboard} editIndex={editIndex} />
</TestProvider>
);
}

View File

@@ -1,26 +1,14 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModel, NavModelItem } from '@grafana/data';
import { BackendSrv, setBackendSrv } from '@grafana/runtime';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { configureStore } from 'app/store/configureStore';
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
import { DashboardSettings } from './DashboardSettings';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
locationService: {
getSearchObject: jest.fn().mockResolvedValue({}),
partial: jest.fn(),
},
}));
setBackendSrv({
get: jest.fn().mockResolvedValue([]),
} as unknown as BackendSrv);
@@ -36,19 +24,13 @@ describe('DashboardSettings', () => {
}
);
const store = configureStore();
const context = getGrafanaContextMock();
const sectionNav: NavModel = { main: { text: 'Dashboards' }, node: { text: 'Dashboards' } };
const pageNav: NavModelItem = { text: 'My cool dashboard' };
render(
<GrafanaContext.Provider value={context}>
<Provider store={store}>
<BrowserRouter>
<DashboardSettings editview="settings" dashboard={dashboard} sectionNav={sectionNav} pageNav={pageNav} />
</BrowserRouter>
</Provider>
</GrafanaContext.Provider>
<TestProvider>
<DashboardSettings editview="settings" dashboard={dashboard} sectionNav={sectionNav} pageNav={pageNav} />
</TestProvider>
);
expect(await screen.findByRole('tab', { name: 'Tab Settings' })).toBeInTheDocument();

View File

@@ -3,13 +3,12 @@ import { rest } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { DeepPartial } from 'react-hook-form';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { DataSourcePluginMeta } from '@grafana/data';
import * as runtime from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import 'whatwg-fetch';
import { EmptyStateNoDatasource } from './EmptyStateNoDatasource';
@@ -60,9 +59,9 @@ describe('EmptyStateNoDatasource', () => {
})
);
render(
<Provider store={configureStore()}>
<TestProvider>
<EmptyStateNoDatasource title="A Title" CTAText="CTA" />
</Provider>
</TestProvider>
);
await waitFor(() => {

View File

@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { setAngularLoader } from '@grafana/runtime';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
@@ -23,7 +23,7 @@ jest.mock('app/core/services/context_srv', () => ({
const setup = (uid: string, store: Store) =>
render(
<Provider store={store}>
<TestProvider store={store}>
<DataSourceDashboardsPage
{...getRouteComponentProps({
// @ts-ignore
@@ -34,7 +34,7 @@ const setup = (uid: string, store: Store) =>
},
})}
/>
</Provider>
</TestProvider>
);
describe('<DataSourceDashboardsPage>', () => {

View File

@@ -1,10 +1,9 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { LayoutModes } from '@grafana/data';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { navIndex, getMockDataSources } from '../__mocks__';
import { getDataSources } from '../api';
@@ -21,19 +20,19 @@ jest.mock('../api', () => ({
const getDataSourcesMock = getDataSources as jest.Mock;
const setup = (options: { isSortAscending: boolean }) => {
const store = configureStore({
const storeState = {
dataSources: {
...initialState,
layoutMode: LayoutModes.Grid,
isSortAscending: options.isSortAscending,
},
navIndex,
});
};
return render(
<Provider store={store}>
<TestProvider storeState={storeState}>
<DataSourcesListPage />
</Provider>
</TestProvider>
);
};

View File

@@ -1,7 +1,7 @@
import { screen, render } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { LayoutModes } from '@grafana/data';
import { setAngularLoader } from '@grafana/runtime';
@@ -36,7 +36,7 @@ jest.mock('@grafana/runtime', () => {
const setup = (uid: string, store: Store) =>
render(
<Provider store={store}>
<TestProvider store={store}>
<EditDataSourcePage
{...getRouteComponentProps({
// @ts-ignore
@@ -47,7 +47,7 @@ const setup = (uid: string, store: Store) =>
},
})}
/>
</Provider>
</TestProvider>
);
describe('<EditDataSourcePage>', () => {

View File

@@ -1,13 +1,12 @@
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModel } from '@grafana/data';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { ModalManager } from 'app/core/services/ModalManager';
import { configureStore } from 'app/store/configureStore';
import { FolderSettingsPage, Props } from './FolderSettingsPage';
import { setFolderTitle } from './state/reducers';
@@ -38,9 +37,9 @@ const setup = (propOverrides?: object) => {
Object.assign(props, propOverrides);
render(
<Provider store={configureStore()}>
<TestProvider>
<FolderSettingsPage {...props} />
</Provider>
</TestProvider>
);
};

View File

@@ -1,12 +1,11 @@
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';
import { TestProvider } from 'test/helpers/TestProvider';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { backendSrv } from '../../core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { SignupInvitedPage, Props } from './SignupInvited';
@@ -30,7 +29,6 @@ const defaultGet = {
async function setupTestContext({ get = defaultGet }: { get?: typeof defaultGet | null } = {}) {
jest.clearAllMocks();
const store = configureStore();
const getSpy = jest.spyOn(backendSrv, 'get');
getSpy.mockResolvedValue(get);
@@ -47,9 +45,9 @@ async function setupTestContext({ get = defaultGet }: { get?: typeof defaultGet
};
render(
<Provider store={store}>
<TestProvider>
<SignupInvitedPage {...props} />
</Provider>
</TestProvider>
);
await waitFor(() => expect(getSpy).toHaveBeenCalled());

View File

@@ -1,14 +1,13 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModel } from '@grafana/data';
import { ModalManager } from 'app/core/services/ModalManager';
import { backendSrv } from '../../core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { Organization } from '../../types';
import { OrgDetailsPage, Props } from './OrgDetailsPage';
@@ -37,7 +36,6 @@ jest.mock('@grafana/runtime', () => {
});
const setup = (propOverrides?: object) => {
const store = configureStore();
jest.clearAllMocks();
// needed because SharedPreferences is rendered in the test
jest.spyOn(backendSrv, 'put');
@@ -63,9 +61,9 @@ const setup = (propOverrides?: object) => {
Object.assign(props, propOverrides);
render(
<Provider store={store}>
<TestProvider>
<OrgDetailsPage {...props} />
</Provider>
</TestProvider>
);
};

View File

@@ -1,13 +1,11 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { locationService } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { PlaylistEditPage } from './PlaylistEditPage';
import { Playlist } from './types';
@@ -24,7 +22,6 @@ jest.mock('app/core/components/TagFilter/TagFilter', () => ({
async function getTestContext({ name, interval, items, uid }: Partial<Playlist> = {}) {
jest.clearAllMocks();
const store = configureStore();
const playlist = { name, items, interval, uid } as unknown as Playlist;
const queryParams = {};
const route: any = {};
@@ -33,16 +30,18 @@ async function getTestContext({ name, interval, items, uid }: Partial<Playlist>
const history: any = {};
const getMock = jest.spyOn(backendSrv, 'get');
const putMock = jest.spyOn(backendSrv, 'put');
getMock.mockResolvedValue({
name: 'Test Playlist',
interval: '5s',
items: [{ title: 'First item', type: 'dashboard_by_uid', order: 1, value: '1' }],
uid: 'foo',
});
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<PlaylistEditPage queryParams={queryParams} route={route} match={match} location={location} history={history} />
</Provider>
</TestProvider>
);
await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));

View File

@@ -1,13 +1,12 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { selectors } from '@grafana/e2e-selectors';
import { locationService } from '@grafana/runtime';
import { backendSrv } from '../../core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { PlaylistNewPage } from './PlaylistNewPage';
import { Playlist } from './types';
@@ -24,15 +23,14 @@ jest.mock('app/core/components/TagFilter/TagFilter', () => ({
}));
function getTestContext({ name, interval, items }: Partial<Playlist> = {}) {
const store = configureStore();
jest.clearAllMocks();
const playlist = { name, items, interval } as unknown as Playlist;
const backendSrvMock = jest.spyOn(backendSrv, 'post');
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<PlaylistNewPage />
</Provider>
</TestProvider>
);
return { playlist, rerender, backendSrvMock };

View File

@@ -1,11 +1,9 @@
import { render, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from '../../store/configureStore';
import { PlaylistPage } from './PlaylistPage';
const fnMock = jest.fn();
@@ -24,11 +22,10 @@ jest.mock('app/core/services/context_srv', () => ({
}));
function getTestContext() {
const store = configureStore();
return render(
<Provider store={store}>
<TestProvider>
<PlaylistPage />
</Provider>
</TestProvider>
);
}

View File

@@ -1,8 +1,7 @@
import { render, RenderResult, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { PluginType, escapeStringForRegex } from '@grafana/data';
import { locationService } from '@grafana/runtime';
@@ -37,11 +36,9 @@ const renderBrowse = (
});
return render(
<Provider store={store}>
<Router history={locationService.getHistory()}>
<BrowsePage {...props} />
</Router>
</Provider>
<TestProvider store={store}>
<BrowsePage {...props} />
</TestProvider>
);
};

View File

@@ -1,8 +1,8 @@
import { getDefaultNormalizer, render, RenderResult, SelectorMatcherOptions, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { MemoryRouter, Route } from 'react-router-dom';
import { Route } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import {
PluginErrorCode,
@@ -12,7 +12,7 @@ import {
WithAccessControlMetadata,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { config } from '@grafana/runtime';
import { config, locationService } from '@grafana/runtime';
import { configureStore } from 'app/store/configureStore';
import { mockPluginApis, getCatalogPluginMock, getPluginsStateMock, mockUserPermissions } from '../__mocks__';
@@ -73,12 +73,12 @@ const renderPluginDetails = (
plugins: pluginsStateOverride || getPluginsStateMock([plugin]),
});
locationService.push({ pathname: `/plugins/${id}`, search: pageId ? `?page=${pageId}` : '' });
return render(
<MemoryRouter initialEntries={[{ pathname: `/plugins/${id}`, search: pageId ? `?page=${pageId}` : '' }]}>
<Provider store={store}>
<Route path="/plugins/:pluginId" component={PluginDetailsPage} />
</Provider>
</MemoryRouter>
<TestProvider store={store}>
<Route path="/plugins/:pluginId" component={PluginDetailsPage} />
</TestProvider>
);
};

View File

@@ -1,12 +1,11 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import config from 'app/core/config';
import { backendSrv } from '../../core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { Props, ChangePasswordPage } from './ChangePasswordPage';
import { initialUserState } from './state/reducers';
@@ -28,7 +27,6 @@ const defaultProps: Props = {
};
async function getTestContext(overrides: Partial<Props> = {}) {
const store = configureStore();
jest.clearAllMocks();
jest.spyOn(backendSrv, 'get').mockResolvedValue({
id: 1,
@@ -42,9 +40,9 @@ async function getTestContext(overrides: Partial<Props> = {}) {
const props = { ...defaultProps, ...overrides };
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<ChangePasswordPage {...props} />
</Provider>
</TestProvider>
);
await waitFor(() => expect(props.loadUser).toHaveBeenCalledTimes(1));

View File

@@ -2,14 +2,12 @@ import { within } from '@testing-library/dom';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { OrgRole } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import TestProvider from '../../../test/helpers/TestProvider';
import { TestProvider } from '../../../test/helpers/TestProvider';
import { backendSrv } from '../../core/services/backend_srv';
import { configureStore } from '../../store/configureStore';
import { TeamPermissionLevel } from '../../types';
import { getMockTeam } from '../teams/__mocks__/teamMocks';
@@ -98,7 +96,6 @@ function getSelectors() {
}
async function getTestContext(overrides: Partial<Props> = {}) {
const store = configureStore();
jest.clearAllMocks();
const putSpy = jest.spyOn(backendSrv, 'put');
const getSpy = jest
@@ -108,11 +105,9 @@ async function getTestContext(overrides: Partial<Props> = {}) {
const props = { ...defaultProps, ...overrides };
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<UserProfileEditPage {...props} />
</TestProvider>
</Provider>
<TestProvider>
<UserProfileEditPage {...props} />
</TestProvider>
);
await waitFor(() => expect(props.initUserProfilePage).toHaveBeenCalledTimes(1));

View File

@@ -1,9 +1,7 @@
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '../../store/configureStore';
import { TestProvider } from 'test/helpers/TestProvider';
import { ServiceAccountCreatePage, Props } from './ServiceAccountCreatePage';
@@ -45,7 +43,6 @@ jest.mock('app/core/core', () => ({
}));
const setup = (propOverrides: Partial<Props>) => {
const store = configureStore();
const props: Props = {
navModel: {
main: {
@@ -60,9 +57,9 @@ const setup = (propOverrides: Partial<Props>) => {
Object.assign(props, propOverrides);
render(
<Provider store={store}>
<TestProvider>
<ServiceAccountCreatePage {...props} />
</Provider>
</TestProvider>
);
};

View File

@@ -1,12 +1,10 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { ApiKey, OrgRole, ServiceAccountDTO } from 'app/types';
import { configureStore } from '../../store/configureStore';
import { ServiceAccountPageUnconnected, Props } from './ServiceAccountPage';
jest.mock('app/core/core', () => ({
@@ -19,7 +17,6 @@ jest.mock('app/core/core', () => ({
}));
const setup = (propOverrides: Partial<Props>) => {
const store = configureStore();
const createServiceAccountTokenMock = jest.fn();
const deleteServiceAccountMock = jest.fn();
const deleteServiceAccountTokenMock = jest.fn();
@@ -54,9 +51,9 @@ const setup = (propOverrides: Partial<Props>) => {
Object.assign(props, propOverrides);
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<ServiceAccountPageUnconnected {...props} />
</Provider>
</TestProvider>
);
return {
rerender,

View File

@@ -1,12 +1,10 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { OrgRole, ServiceAccountDTO, ServiceAccountStateFilter } from 'app/types';
import { configureStore } from '../../store/configureStore';
import { Props, ServiceAccountsListPageUnconnected } from './ServiceAccountsListPage';
jest.mock('app/core/core', () => ({
@@ -18,7 +16,6 @@ jest.mock('app/core/core', () => ({
}));
const setup = (propOverrides: Partial<Props>) => {
const store = configureStore();
const changeQueryMock = jest.fn();
const fetchACOptionsMock = jest.fn();
const fetchServiceAccountsMock = jest.fn();
@@ -56,12 +53,12 @@ const setup = (propOverrides: Partial<Props>) => {
Object.assign(props, propOverrides);
const { rerender } = render(
<Provider store={store}>
<TestProvider>
<ServiceAccountsListPageUnconnected {...props} />
</Provider>
</TestProvider>
);
return {
rerender: (element: JSX.Element) => rerender(<Provider store={store}>{element}</Provider>),
rerender: (element: JSX.Element) => rerender(<TestProvider>{element}</TestProvider>),
props,
changeQueryMock,
fetchACOptionsMock,

View File

@@ -1,12 +1,10 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { BackendSrv, setBackendSrv } from '@grafana/runtime';
import { configureStore } from '../../store/configureStore';
import { CreateTeam } from './CreateTeam';
beforeEach(() => {
@@ -35,11 +33,10 @@ setBackendSrv({
} as unknown as BackendSrv);
const setup = () => {
const store = configureStore();
return render(
<Provider store={store}>
<TestProvider>
<CreateTeam />
</Provider>
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { contextSrv, User } from 'app/core/services/context_srv';
import { configureStore } from '../../store/configureStore';
import { OrgRole, Team } from '../../types';
import { Props, TeamList } from './TeamList';
@@ -17,7 +16,6 @@ jest.mock('app/core/config', () => ({
}));
const setup = (propOverrides?: object) => {
const store = configureStore();
const props: Props = {
teams: [] as Team[],
noTeams: false,
@@ -41,9 +39,9 @@ const setup = (propOverrides?: object) => {
contextSrv.user = props.signedInUser;
render(
<Provider store={store}>
<TestProvider>
<TeamList {...props} />
</Provider>
</TestProvider>
);
};

View File

@@ -1,11 +1,10 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { TestProvider } from 'test/helpers/TestProvider';
import { createTheme } from '@grafana/data';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { User } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { OrgRole, Team, TeamMember } from '../../types';
@@ -64,7 +63,6 @@ jest.mock('./TeamGroupSync', () => {
});
const setup = (propOverrides?: object) => {
const store = configureStore();
const props: Props = {
...getRouteComponentProps({
match: {
@@ -93,9 +91,9 @@ const setup = (propOverrides?: object) => {
Object.assign(props, propOverrides);
render(
<Provider store={store}>
<TestProvider>
<TeamPages {...props} />
</Provider>
</TestProvider>
);
};

View File

@@ -401,7 +401,7 @@ export function getAppRoutes(): RouteDescriptor[] {
{
path: '/login',
component: LoginPage,
pageClass: 'login-page sidemenu-hidden',
pageClass: 'login-page',
chromeless: true,
},
{
@@ -409,7 +409,6 @@ export function getAppRoutes(): RouteDescriptor[] {
component: SafeDynamicImport(
() => import(/* webpackChunkName: "SignupInvited" */ 'app/features/invites/SignupInvited')
),
pageClass: 'sidemenu-hidden',
chromeless: true,
},
{
@@ -419,7 +418,7 @@ export function getAppRoutes(): RouteDescriptor[] {
: SafeDynamicImport(
() => import(/* webpackChunkName "VerifyEmailPage"*/ 'app/core/components/Signup/VerifyEmailPage')
),
pageClass: 'login-page sidemenu-hidden',
pageClass: 'login-page',
chromeless: true,
},
{
@@ -427,12 +426,11 @@ export function getAppRoutes(): RouteDescriptor[] {
component: config.disableUserSignUp
? () => <Redirect to="/login" />
: SafeDynamicImport(() => import(/* webpackChunkName "SignupPage"*/ 'app/core/components/Signup/SignupPage')),
pageClass: 'sidemenu-hidden login-page',
pageClass: 'login-page',
chromeless: true,
},
{
path: '/user/password/send-reset-email',
pageClass: 'sidemenu-hidden',
chromeless: true,
component: SafeDynamicImport(
() =>
@@ -447,7 +445,7 @@ export function getAppRoutes(): RouteDescriptor[] {
/* webpackChunkName: "ChangePasswordPage" */ 'app/core/components/ForgottenPassword/ChangePasswordPage'
)
),
pageClass: 'sidemenu-hidden login-page',
pageClass: 'login-page',
chromeless: true,
},
{

View File

@@ -87,7 +87,6 @@ export interface PanelChangeViewPayload {}
export const dsRequestResponse = eventFactory<DataSourceResponsePayload>('ds-request-response');
export const dsRequestError = eventFactory<any>('ds-request-error');
export const toggleSidemenuHidden = eventFactory('toggle-sidemenu-hidden');
export const templateVariableValueUpdated = eventFactory('template-variable-value-updated');
export const graphClicked = eventFactory<GraphClickedPayload>('graph-click');