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(
+
+
+
+
+
+
+ ,
+ );
+};