2024-05-10 08:28:51 -05:00
|
|
|
import { Store } from '@reduxjs/toolkit';
|
2024-04-05 11:32:16 -05:00
|
|
|
import { render, RenderOptions } from '@testing-library/react';
|
|
|
|
import userEvent from '@testing-library/user-event';
|
2024-04-26 09:45:48 -05:00
|
|
|
import { createMemoryHistory, MemoryHistoryBuildOptions } from 'history';
|
|
|
|
import React, { Fragment, PropsWithChildren } from 'react';
|
2024-04-05 11:32:16 -05:00
|
|
|
import { Provider } from 'react-redux';
|
2024-04-26 09:45:48 -05:00
|
|
|
import { Router } from 'react-router-dom';
|
2024-04-05 11:32:16 -05:00
|
|
|
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
|
|
|
|
2024-04-26 09:45:48 -05:00
|
|
|
import { HistoryWrapper, setLocationService } from '@grafana/runtime';
|
2024-04-05 11:32:16 -05:00
|
|
|
import { GrafanaContext, GrafanaContextType } from 'app/core/context/GrafanaContext';
|
|
|
|
import { ModalsContextProvider } from 'app/core/context/ModalsContextProvider';
|
|
|
|
import { configureStore } from 'app/store/configureStore';
|
|
|
|
import { StoreState } from 'app/types/store';
|
|
|
|
|
|
|
|
interface ExtendedRenderOptions extends RenderOptions {
|
2024-04-09 07:14:58 -05:00
|
|
|
/**
|
|
|
|
* Optional store to use for rendering. If not provided, a fresh store will be generated
|
|
|
|
* via `configureStore` method
|
|
|
|
*/
|
2024-05-10 08:28:51 -05:00
|
|
|
store?: Store<StoreState>;
|
2024-04-05 11:32:16 -05:00
|
|
|
/**
|
|
|
|
* Partial state to use for preloading store when rendering tests
|
|
|
|
*/
|
2024-05-10 08:28:51 -05:00
|
|
|
preloadedState?: Partial<StoreState>;
|
2024-04-05 11:32:16 -05:00
|
|
|
/**
|
2024-04-09 07:14:58 -05:00
|
|
|
* Should the wrapper be generated with a wrapping Router component?
|
|
|
|
* Useful if you're testing something that needs more nuanced routing behaviour
|
|
|
|
* and you want full control over it instead
|
2024-04-05 11:32:16 -05:00
|
|
|
*/
|
|
|
|
renderWithRouter?: boolean;
|
2024-04-09 07:14:58 -05:00
|
|
|
/**
|
2024-04-26 09:45:48 -05:00
|
|
|
* Props to pass to `createMemoryHistory`, if being used
|
2024-04-09 07:14:58 -05:00
|
|
|
*/
|
2024-04-26 09:45:48 -05:00
|
|
|
historyOptions?: MemoryHistoryBuildOptions;
|
2024-04-05 11:32:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a wrapper component that implements all of the providers that components
|
|
|
|
* within the app will need
|
|
|
|
*/
|
|
|
|
const getWrapper = ({
|
|
|
|
store,
|
|
|
|
renderWithRouter,
|
2024-04-26 09:45:48 -05:00
|
|
|
historyOptions,
|
2024-04-05 11:32:16 -05:00
|
|
|
grafanaContext,
|
2024-04-09 07:14:58 -05:00
|
|
|
}: ExtendedRenderOptions & {
|
2024-04-05 11:32:16 -05:00
|
|
|
grafanaContext?: GrafanaContextType;
|
2024-04-09 07:14:58 -05:00
|
|
|
}) => {
|
2024-04-05 11:32:16 -05:00
|
|
|
const reduxStore = store || configureStore();
|
|
|
|
/**
|
|
|
|
* Conditional router - either a MemoryRouter or just a Fragment
|
|
|
|
*/
|
2024-04-26 09:45:48 -05:00
|
|
|
const PotentialRouter = renderWithRouter ? Router : Fragment;
|
|
|
|
|
|
|
|
// Create a fresh location service for each test - otherwise we run the risk
|
|
|
|
// of it being stateful in between runs
|
|
|
|
const history = createMemoryHistory(historyOptions);
|
|
|
|
const locationService = new HistoryWrapper(history);
|
|
|
|
setLocationService(locationService);
|
2024-04-05 11:32:16 -05:00
|
|
|
|
|
|
|
const context = {
|
|
|
|
...getGrafanaContextMock(),
|
|
|
|
...grafanaContext,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2024-04-15 10:45:05 -05:00
|
|
|
* Returns a wrapper that should (eventually?) match the main `AppWrapper`, so any tests are rendering
|
2024-04-05 11:32:16 -05:00
|
|
|
* in mostly the same providers as a "real" hierarchy
|
|
|
|
*/
|
2024-04-09 07:14:58 -05:00
|
|
|
return function Wrapper({ children }: PropsWithChildren) {
|
2024-04-05 11:32:16 -05:00
|
|
|
return (
|
|
|
|
<Provider store={reduxStore}>
|
2024-04-15 10:45:05 -05:00
|
|
|
<GrafanaContext.Provider value={context}>
|
2024-04-26 09:45:48 -05:00
|
|
|
<PotentialRouter history={history}>
|
2024-04-15 10:45:05 -05:00
|
|
|
<ModalsContextProvider>{children}</ModalsContextProvider>
|
|
|
|
</PotentialRouter>
|
|
|
|
</GrafanaContext.Provider>
|
2024-04-05 11:32:16 -05:00
|
|
|
</Provider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extended [@testing-library/react render](https://testing-library.com/docs/react-testing-library/api/#render)
|
|
|
|
* method which wraps the passed element in all of the necessary Providers,
|
|
|
|
* so it can render correctly in the context of the application
|
|
|
|
*/
|
|
|
|
const customRender = (
|
|
|
|
ui: React.ReactElement,
|
|
|
|
{ renderWithRouter = true, ...renderOptions }: ExtendedRenderOptions = {}
|
|
|
|
) => {
|
2024-05-10 08:28:51 -05:00
|
|
|
const store = renderOptions.preloadedState ? configureStore(renderOptions?.preloadedState) : undefined;
|
2024-04-09 07:14:58 -05:00
|
|
|
const AllTheProviders = renderOptions.wrapper || getWrapper({ store, renderWithRouter, ...renderOptions });
|
2024-04-05 11:32:16 -05:00
|
|
|
|
|
|
|
return {
|
|
|
|
...render(ui, { wrapper: AllTheProviders, ...renderOptions }),
|
|
|
|
store,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export * from '@testing-library/react';
|
|
|
|
export { customRender as render, getWrapper, userEvent };
|