mirror of https://github.com/coder/coder.git
205 lines
4.9 KiB
TypeScript
205 lines
4.9 KiB
TypeScript
import {
|
|
render as testingLibraryRender,
|
|
screen,
|
|
waitFor,
|
|
} from "@testing-library/react";
|
|
import type { ReactNode } from "react";
|
|
import { QueryClient } from "react-query";
|
|
import {
|
|
createMemoryRouter,
|
|
RouterProvider,
|
|
type RouteObject,
|
|
} from "react-router-dom";
|
|
import { AppProviders } from "App";
|
|
import { RequireAuth } from "contexts/auth/RequireAuth";
|
|
import { ThemeProvider } from "contexts/ThemeProvider";
|
|
import { DashboardLayout } from "modules/dashboard/DashboardLayout";
|
|
import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout";
|
|
import { WorkspaceSettingsLayout } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
|
|
import { MockUser } from "./entities";
|
|
|
|
export function createTestQueryClient() {
|
|
// Helps create one query client for each test case, to make sure that tests
|
|
// are isolated and can't affect each other
|
|
return new QueryClient({
|
|
logger: {
|
|
...console,
|
|
// Some tests are designed to throw errors as part of their functionality.
|
|
// To avoid unnecessary noise from these expected errors, the code is
|
|
// structured to suppress them. If this suppression becomes problematic,
|
|
// the code can be refactored to handle query errors on a per-test basis.
|
|
error: () => {},
|
|
},
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: false,
|
|
cacheTime: 0,
|
|
refetchOnWindowFocus: false,
|
|
networkMode: "offlineFirst",
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
export const renderWithRouter = (
|
|
router: ReturnType<typeof createMemoryRouter>,
|
|
) => {
|
|
const queryClient = createTestQueryClient();
|
|
|
|
return {
|
|
...testingLibraryRender(
|
|
<AppProviders queryClient={queryClient}>
|
|
<RouterProvider router={router} />
|
|
</AppProviders>,
|
|
),
|
|
router,
|
|
};
|
|
};
|
|
|
|
export const render = (element: ReactNode) => {
|
|
return renderWithRouter(
|
|
createMemoryRouter(
|
|
[
|
|
{
|
|
path: "/",
|
|
element,
|
|
},
|
|
],
|
|
{ initialEntries: ["/"] },
|
|
),
|
|
);
|
|
};
|
|
|
|
export type RenderWithAuthOptions = {
|
|
// The current URL, /workspaces/123
|
|
route?: string;
|
|
// The route path, /workspaces/:workspaceId
|
|
path?: string;
|
|
// Extra routes to add to the router. It is helpful when having redirecting
|
|
// routes or multiple routes during the test flow
|
|
extraRoutes?: RouteObject[];
|
|
// The same as extraRoutes but for routes that don't require authentication
|
|
nonAuthenticatedRoutes?: RouteObject[];
|
|
// In case you want to render a layout inside of it
|
|
children?: RouteObject["children"];
|
|
};
|
|
|
|
export function renderWithAuth(
|
|
element: JSX.Element,
|
|
{
|
|
path = "/",
|
|
route = "/",
|
|
extraRoutes = [],
|
|
nonAuthenticatedRoutes = [],
|
|
children,
|
|
}: RenderWithAuthOptions = {},
|
|
) {
|
|
const routes: RouteObject[] = [
|
|
{
|
|
element: <RequireAuth />,
|
|
children: [{ path, element, children }, ...extraRoutes],
|
|
},
|
|
...nonAuthenticatedRoutes,
|
|
];
|
|
|
|
const renderResult = renderWithRouter(
|
|
createMemoryRouter(routes, { initialEntries: [route] }),
|
|
);
|
|
|
|
return {
|
|
user: MockUser,
|
|
...renderResult,
|
|
};
|
|
}
|
|
|
|
export function renderWithTemplateSettingsLayout(
|
|
element: JSX.Element,
|
|
{
|
|
path = "/",
|
|
route = "/",
|
|
extraRoutes = [],
|
|
nonAuthenticatedRoutes = [],
|
|
}: RenderWithAuthOptions = {},
|
|
) {
|
|
const routes: RouteObject[] = [
|
|
{
|
|
element: <RequireAuth />,
|
|
children: [
|
|
{
|
|
element: <DashboardLayout />,
|
|
children: [
|
|
{
|
|
element: <TemplateSettingsLayout />,
|
|
children: [{ path, element }, ...extraRoutes],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
...nonAuthenticatedRoutes,
|
|
];
|
|
|
|
const renderResult = renderWithRouter(
|
|
createMemoryRouter(routes, { initialEntries: [route] }),
|
|
);
|
|
|
|
return {
|
|
user: MockUser,
|
|
...renderResult,
|
|
};
|
|
}
|
|
|
|
export function renderWithWorkspaceSettingsLayout(
|
|
element: JSX.Element,
|
|
{
|
|
path = "/",
|
|
route = "/",
|
|
extraRoutes = [],
|
|
nonAuthenticatedRoutes = [],
|
|
}: RenderWithAuthOptions = {},
|
|
) {
|
|
const routes: RouteObject[] = [
|
|
{
|
|
element: <RequireAuth />,
|
|
children: [
|
|
{
|
|
element: <DashboardLayout />,
|
|
children: [
|
|
{
|
|
element: <WorkspaceSettingsLayout />,
|
|
children: [{ element, path }, ...extraRoutes],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
...nonAuthenticatedRoutes,
|
|
];
|
|
|
|
const renderResult = renderWithRouter(
|
|
createMemoryRouter(routes, { initialEntries: [route] }),
|
|
);
|
|
|
|
return {
|
|
user: MockUser,
|
|
...renderResult,
|
|
};
|
|
}
|
|
|
|
export const waitForLoaderToBeRemoved = async (): Promise<void> => {
|
|
return waitFor(
|
|
() => {
|
|
expect(screen.queryByTestId("loader")).not.toBeInTheDocument();
|
|
},
|
|
{
|
|
timeout: 5_000,
|
|
},
|
|
);
|
|
};
|
|
|
|
export const renderComponent = (component: React.ReactElement) => {
|
|
return testingLibraryRender(component, {
|
|
wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
|
|
});
|
|
};
|