diff --git a/frontend/__tests__/components/common/AsyncContent.spec.tsx b/frontend/__tests__/components/common/AsyncContent.spec.tsx index 3e71764f398d..ba04b762903a 100644 --- a/frontend/__tests__/components/common/AsyncContent.spec.tsx +++ b/frontend/__tests__/components/common/AsyncContent.spec.tsx @@ -1,5 +1,10 @@ import { render, screen, waitFor } from "@solidjs/testing-library"; -import { createResource, Resource, Show } from "solid-js"; +import { + QueryClient, + QueryClientProvider, + useQuery, +} from "@tanstack/solid-query"; +import { JSXElement, Show } from "solid-js"; import { beforeEach, describe, expect, it, vi } from "vitest"; import AsyncContent from "../../../src/ts/components/common/AsyncContent"; @@ -11,14 +16,12 @@ describe("AsyncContent", () => { beforeEach(() => { addNotificationMock.mockClear(); }); - describe("with resource", () => { - it("renders loading state while resource is pending", () => { - const [resource] = createResource(async () => { - await new Promise((resolve) => setTimeout(resolve, 100)); - return "data"; - }); - const { container } = renderWithResource(resource); + describe("with single query", () => { + const queryClient = new QueryClient(); + + it("renders loading state while pending", () => { + const { container } = renderWithQuery({ result: "data" }); const preloader = container.querySelector(".preloader"); expect(preloader).toBeInTheDocument(); @@ -31,24 +34,64 @@ describe("AsyncContent", () => { ); }); - it("renders data when resource resolves", async () => { - const [resource] = createResource(async () => { - return "Test Data"; + it("renders on resolve", async () => { + renderWithQuery({ result: "Test Data" }); + + await waitFor(() => { + expect(screen.getByTestId("content")).toHaveTextContent("Test Data"); }); + }); - renderWithResource(resource); + it("renders default error message on fail", async () => { + renderWithQuery({ result: new Error("Test error") }); + + await waitFor(() => { + expect(screen.getByText(/An error occurred/)).toBeInTheDocument(); + }); + expect(addNotificationMock).toHaveBeenCalledWith( + "An error occurred: Test error", + -1, + ); + }); + + it("renders custom error message on fail", async () => { + renderWithQuery( + { result: new Error("Test error") }, + { errorMessage: "Custom error message" }, + ); + + await waitFor(() => { + expect(screen.getByText(/Custom error message/)).toBeInTheDocument(); + }); + expect(addNotificationMock).toHaveBeenCalledWith( + "Custom error message: Test error", + -1, + ); + }); + + it("renders on pending if alwaysShowContent", async () => { + const { container } = renderWithQuery({ result: "Test Data" }); await waitFor(() => { expect(screen.getByTestId("content")).toHaveTextContent("Test Data"); }); + const preloader = container.querySelector(".preloader"); + expect(preloader).not.toBeInTheDocument(); }); - it("renders error message when resource fails", async () => { - const [resource] = createResource(async () => { - throw new Error("Test error"); + it("renders on resolve if alwaysShowContent", async () => { + renderWithQuery({ result: "Test Data" }, { alwaysShowContent: true }); + + await waitFor(() => { + expect(screen.getByTestId("content")).toHaveTextContent("Test Data"); }); + }); - renderWithResource(resource, "Custom error message"); + it("renders on fail if alwaysShowContent", async () => { + renderWithQuery( + { result: new Error("Test error") }, + { errorMessage: "Custom error message" }, + ); await waitFor(() => { expect(screen.getByText(/Custom error message/)).toBeInTheDocument(); @@ -59,12 +102,89 @@ describe("AsyncContent", () => { ); }); - it("renders default error message when no custom message provided", async () => { - const [resource] = createResource(async () => { - throw new Error("Test error"); + function renderWithQuery( + query: { + result: string | Error; + }, + options?: { + errorMessage?: string; + alwaysShowContent?: true; + }, + ): { + container: HTMLElement; + } { + const wrapper = (): JSXElement => { + const myQuery = useQuery(() => ({ + queryKey: ["test", Math.random() * 1000], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + if (query.result instanceof Error) { + throw query.result; + } + return query.result; + }, + retry: 0, + })); + + return ( + + {(data: string | undefined) => ( + <> + foo + no data}> +
{data}
+
+ + )} +
+ ); + }; + const { container } = render(() => ( + + {wrapper()} + + )); + + return { + container, + }; + } + }); + + describe("with multiple queries", () => { + const queryClient = new QueryClient(); + + it("renders loading state while pending", () => { + const { container } = renderWithQuery({ first: "data", second: "data" }); + + const preloader = container.querySelector(".preloader"); + expect(preloader).toBeInTheDocument(); + expect(preloader).toHaveClass("preloader"); + expect(preloader?.querySelector("i")).toHaveClass( + "fas", + "fa-fw", + "fa-spin", + "fa-circle-notch", + ); + }); + + it("renders on resolve", async () => { + renderWithQuery({ first: "First Data", second: "Second Data" }); + + await waitFor(() => { + expect(screen.getByTestId("first")).toHaveTextContent("First Data"); }); + await waitFor(() => { + expect(screen.getByTestId("second")).toHaveTextContent("Second Data"); + }); + }); - renderWithResource(resource); + it("renders default error message on fail", async () => { + renderWithQuery({ first: "data", second: new Error("Test error") }); await waitFor(() => { expect(screen.getByText(/An error occurred/)).toBeInTheDocument(); @@ -75,42 +195,56 @@ describe("AsyncContent", () => { ); }); - it("renders content while resource is pending if alwaysShowContent", () => { - const [resource] = createResource(async () => { - await new Promise((resolve) => setTimeout(resolve, 100)); - return "data"; + it("renders custom error message on fail", async () => { + renderWithQuery( + { first: new Error("Test error"), second: new Error("Test error") }, + { errorMessage: "Custom error message" }, + ); + + await waitFor(() => { + expect(screen.getByText(/Custom error message/)).toBeInTheDocument(); }); + expect(addNotificationMock).toHaveBeenCalledWith( + "Custom error message: Test error", + -1, + ); + }); - const { container } = renderWithResource(resource, undefined, true); + it("renders on pending if alwaysShowContent", async () => { + const { container } = renderWithQuery( + { + first: undefined, + second: undefined, + }, + { alwaysShowContent: true }, + ); const preloader = container.querySelector(".preloader"); expect(preloader).not.toBeInTheDocument(); - expect(container.querySelector("div")).toHaveTextContent("no data"); - }); - it("renders data when resource resolves if alwaysShowContent", async () => { - const [resource] = createResource(async () => { - return "Test Data"; + await waitFor(() => { + expect(screen.getByText(/no data/)).toBeInTheDocument(); }); + }); - const { container } = renderWithResource(resource, undefined, true); + it("renders on resolve if alwaysShowContent", async () => { + renderWithQuery({ + first: "First Data", + second: "Second Data", + }); await waitFor(() => { - expect(screen.getByTestId("content")).toHaveTextContent("Test Data"); + expect(screen.getByTestId("first")).toHaveTextContent("First Data"); }); - const preloader = container.querySelector(".preloader"); - expect(preloader).not.toBeInTheDocument(); - }); - - it("renders error message when resource fails if alwaysShowContent", async () => { - const [resource] = createResource(async () => { - throw new Error("Test error"); + await waitFor(() => { + expect(screen.getByTestId("second")).toHaveTextContent("Second Data"); }); + }); - const { container } = renderWithResource( - resource, - "Custom error message", - true, + it("renders on fail if alwaysShowContent", async () => { + renderWithQuery( + { first: "data", second: new Error("Test error") }, + { errorMessage: "Custom error message" }, ); await waitFor(() => { @@ -120,30 +254,73 @@ describe("AsyncContent", () => { "Custom error message: Test error", -1, ); - console.log(container.innerHTML); }); - function renderWithResource( - resource: Resource, - errorMessage?: string, - alwaysShowContent?: true, + + function renderWithQuery( + queries: { + first: string | Error | undefined; + second: string | Error | undefined; + }, + options?: { + errorMessage?: string; + alwaysShowContent?: true; + }, ): { container: HTMLElement; } { + const wrapper = (): JSXElement => { + const firstQuery = useQuery(() => ({ + queryKey: ["first", Math.random() * 1000], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + if (queries.first instanceof Error) { + throw queries.first; + } + return queries.first; + }, + retry: 0, + })); + const secondQuery = useQuery(() => ({ + queryKey: ["second", Math.random() * 1000], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + if (queries.second instanceof Error) { + throw queries.second; + } + return queries.second; + }, + retry: 0, + })); + + return ( + + {(results: { + first: string | undefined; + second: string | undefined; + }) => ( + <> + no data} + > +
{results.first}
+
{results.second}
+
+ + )} +
+ ); + }; const { container } = render(() => ( - - {(data: T | undefined) => ( - <> - foo - no data}> -
{String(data)}
-
- - )} -
+ + {wrapper()} + )); return { diff --git a/frontend/src/html/pages/leaderboards.html b/frontend/src/html/pages/leaderboards.html deleted file mode 100644 index 4bb34916f4b4..000000000000 --- a/frontend/src/html/pages/leaderboards.html +++ /dev/null @@ -1,262 +0,0 @@ - diff --git a/frontend/src/index.html b/frontend/src/index.html index b7ad14bba257..a6dad26b94d2 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -38,7 +38,7 @@
-