diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 992ef767e..c9bd4514d 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -1,32 +1,3 @@ -import { createRoot } from "react-dom/client"; -import { BrowserRouter as Router } from "react-router-dom"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import App from "./App"; -import "./sass/styles.scss"; -import { NotificationProvider } from "@canonical/react-components"; -import { basePath } from "util/basePaths"; -import { removeTrailingSlash } from "util/removeTrailingSlash"; +import { init } from "root"; -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - retry: false, - // Cache queries for 30 seconds by default. - staleTime: 30000, - }, - }, -}); - -const rootElement = document.getElementById("app"); -if (!rootElement) throw new Error("Failed to find the root element"); -const root = createRoot(rootElement); -root.render( - - - - - - - , -); +init(); diff --git a/ui/src/root.test.tsx b/ui/src/root.test.tsx new file mode 100644 index 000000000..5fbd16fa5 --- /dev/null +++ b/ui/src/root.test.tsx @@ -0,0 +1,53 @@ +import { waitFor } from "@testing-library/dom"; +import MockAdapter from "axios-mock-adapter"; + +import { authURLs } from "api/auth"; +import { changeURL } from "test/utils"; + +import { init, Label } from "./root"; +import { axiosInstance } from "./api/axios"; +import { act } from "@testing-library/react"; + +const mock = new MockAdapter(axiosInstance); + +const cleanRootNode = () => + document.querySelectorAll("#app").forEach((root) => { + document.body.removeChild(root); + }); + +const createRootNode = () => { + const root = document.createElement("div"); + root.setAttribute("id", "app"); + document.body.appendChild(root); +}; + +beforeEach(() => { + mock.reset(); + mock.onGet(authURLs.me).reply(200, { + data: { + email: "email", + name: "name", + nonce: "nonce", + sid: "sid", + sub: "sub", + }, + }); + createRootNode(); + changeURL("/ui"); +}); + +afterEach(() => { + cleanRootNode(); +}); + +test("handles no root node", () => { + cleanRootNode(); + expect(init).toThrowError(Label.ERROR); +}); + +test("initialises the index", async () => { + act(() => init()); + await waitFor(() => + expect(document.querySelector("#app-layout")).toBeInTheDocument(), + ); +}); diff --git a/ui/src/root.tsx b/ui/src/root.tsx new file mode 100644 index 000000000..c02819b5a --- /dev/null +++ b/ui/src/root.tsx @@ -0,0 +1,38 @@ +import { createRoot } from "react-dom/client"; +import { BrowserRouter as Router } from "react-router-dom"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import App from "./App"; +import "./sass/styles.scss"; +import { NotificationProvider } from "@canonical/react-components"; +import { basePath } from "util/basePaths"; +import { removeTrailingSlash } from "util/removeTrailingSlash"; + +export enum Label { + ERROR = "Failed to find the root element", +} + +export const init = () => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + // Cache queries for 30 seconds by default. + staleTime: 30000, + }, + }, + }); + + const rootElement = document.getElementById("app"); + if (!rootElement) throw new Error(Label.ERROR); + const root = createRoot(rootElement); + root.render( + + + + + + + , + ); +};