diff --git a/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDay.tsx b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDay.tsx index 6586c1c47..721e7346d 100644 --- a/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDay.tsx +++ b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDay.tsx @@ -1,32 +1,13 @@ +import ProblemOfTheDaySkeleton from "@/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton"; import { useFetchPotdQuery } from "@/lib/api/queries/potd"; -import { - Badge, - Button, - Card, - Center, - Flex, - Loader, - Title, -} from "@mantine/core"; +import { Badge, Button, Card, Center, Flex, Title } from "@mantine/core"; import { Link } from "react-router-dom"; export default function ProblemOfTheDay() { const { data, status } = useFetchPotdQuery(); if (status === "pending") { - return ( - - - - - - ); + return ; } if (status === "error") { diff --git a/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.test.tsx b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.test.tsx new file mode 100644 index 000000000..355b1bcd8 --- /dev/null +++ b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.test.tsx @@ -0,0 +1,46 @@ +import ProblemOfTheDaySkeleton from "@/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton"; +import { TestUtils, TestUtilTypes } from "@/lib/test"; +import { screen } from "@testing-library/react"; + +describe("ProblemOfTheDaySkeleton", () => { + let renderProviderFn: TestUtilTypes.RenderWithAllProvidersFn | null = null; + + beforeEach(() => { + renderProviderFn = TestUtils.getRenderWithAllProvidersFn(); + }); + + it("should render POTD title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("potd-skeleton-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render POTD reset time skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("potd-skeleton-reset-time"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render problem title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("potd-skeleton-problem-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render problem multiplier skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("potd-skeleton-problem-multiplier"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render problem link button skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("potd-skeleton-problem-link-button"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); +}); diff --git a/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.tsx b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.tsx new file mode 100644 index 000000000..f686ceae9 --- /dev/null +++ b/js/src/app/dashboard/_components/ProblemOfTheDay/ProblemOfTheDaySkeleton.tsx @@ -0,0 +1,44 @@ +import { Card, Center, Flex, Skeleton } from "@mantine/core"; + +export default function ProblemOfTheDaySkeleton() { + return ( + +
+ +
+
+ +
+ + + + + +
+ ); +} diff --git a/js/src/app/settings/Settings.page.tsx b/js/src/app/settings/Settings.page.tsx index 84d3c4aa8..aa0879dcb 100644 --- a/js/src/app/settings/Settings.page.tsx +++ b/js/src/app/settings/Settings.page.tsx @@ -1,13 +1,14 @@ import ChangeImageSettingsCard from "@/app/settings/_components/ChangeImageSettingsCard"; import LogoutAllSessionsCard from "@/app/settings/_components/LogoutAllSessionsCard"; import SchoolVerifySettingsCard from "@/app/settings/_components/SchoolVerifySettingsCard"; +import SettingsSkeleton from "@/app/settings/_components/SettingsSkeleton"; import DocumentDescription from "@/components/ui/title/DocumentDescription"; import DocumentTitle from "@/components/ui/title/DocumentTitle"; import Toast from "@/components/ui/toast/Toast"; import ToastWithRedirect from "@/components/ui/toast/ToastWithRedirect"; import { useAuthQuery } from "@/lib/api/queries/auth"; import { useBackendCallbackParams } from "@/lib/hooks/useBackendCallbackParams"; -import { Box, Center, Loader, Stack, Title } from "@mantine/core"; +import { Box, Center, Stack, Title } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { useEffect } from "react"; @@ -25,11 +26,7 @@ export default function SettingsPage() { }, [message, success]); if (status === "pending") { - return ( -
- -
- ); + return ; } if (status === "error") { diff --git a/js/src/app/settings/_components/SettingsSkeleton.test.tsx b/js/src/app/settings/_components/SettingsSkeleton.test.tsx new file mode 100644 index 000000000..d4ec29bc6 --- /dev/null +++ b/js/src/app/settings/_components/SettingsSkeleton.test.tsx @@ -0,0 +1,134 @@ +import SettingsSkeleton from "@/app/settings/_components/SettingsSkeleton"; +import { TestUtils, TestUtilTypes } from "@/lib/test"; +import { screen } from "@testing-library/react"; + +describe("SettingsSkeleton", () => { + let renderProviderFn: TestUtilTypes.RenderWithAllProvidersFn | null = null; + + beforeEach(() => { + renderProviderFn = TestUtils.getRenderWithAllProvidersFn(); + }); + + it("should render title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render verify school card skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-verify-school-card"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render verify school title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-verify-school-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render verify school description 1 skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "settings-skeleton-verify-school-description-1", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render verify school description 2 skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "settings-skeleton-verify-school-description-2", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render verify school list skeletons", () => { + renderProviderFn?.(); + const elements = screen.queryAllByTestId( + "settings-skeleton-verify-school-list", + ); + expect(elements).toHaveLength(9); + for (const element of elements) { + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + } + }); + + it("should render verify now button skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-verify-now-button"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render change profile card skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-change-profile-card"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render change profile title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "settings-skeleton-change-profile-title", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render change profile description skeletons", () => { + renderProviderFn?.(); + const description1 = screen.getByTestId( + "settings-skeleton-change-profile-description-1", + ); + const description2 = screen.getByTestId( + "settings-skeleton-change-profile-description-2", + ); + const description3 = screen.getByTestId( + "settings-skeleton-change-profile-description-3", + ); + expect(description1).toBeInTheDocument(); + expect(description1).toBeVisible(); + expect(description2).toBeInTheDocument(); + expect(description2).toBeVisible(); + expect(description3).toBeInTheDocument(); + expect(description3).toBeVisible(); + }); + + it("should render log out card skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-log-out-card"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render log out title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-log-out-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render log out description skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("settings-skeleton-log-out-description"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render log out all sessions button skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "settings-skeleton-log-out-all-sessions-button", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); +}); diff --git a/js/src/app/settings/_components/SettingsSkeleton.tsx b/js/src/app/settings/_components/SettingsSkeleton.tsx new file mode 100644 index 000000000..a58e79b09 --- /dev/null +++ b/js/src/app/settings/_components/SettingsSkeleton.tsx @@ -0,0 +1,135 @@ +import { Box, Card, Center, Flex, Skeleton, Stack } from "@mantine/core"; + +export default function SettingsSkeleton() { + return ( + + +
+ +
+ + + + + + + + {Array(9) + .fill(0) + .map((_, index) => ( + + ))} + + + + + + + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContent.tsx b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContent.tsx index 76a166f2f..2310366d5 100644 --- a/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContent.tsx +++ b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContent.tsx @@ -1,4 +1,5 @@ import classes from "@/app/submission/[submissionId]/_components/SubmissionDetailsContent.module.css"; +import SubmissionDetailsContentSkeleton from "@/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton"; import DocumentDescription from "@/components/ui/title/DocumentDescription"; import DocumentTitle from "@/components/ui/title/DocumentTitle"; import Toast from "@/components/ui/toast/Toast"; @@ -12,7 +13,6 @@ import { Card, Center, Flex, - Loader, Text, Title, } from "@mantine/core"; @@ -32,11 +32,7 @@ export default function SubmissionDetailsContent({ const { data, status } = useSubmissionDetailsQuery({ submissionId }); if (status === "pending") { - return ( -
- -
- ); + return ; } if (status === "error") { diff --git a/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.test.tsx b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.test.tsx new file mode 100644 index 000000000..30f906e7c --- /dev/null +++ b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.test.tsx @@ -0,0 +1,75 @@ +import SubmissionDetailsContentSkeleton from "@/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton"; +import { TestUtils, TestUtilTypes } from "@/lib/test"; +import { screen } from "@testing-library/react"; + +describe("SubmissionDetailsContentSkeleton", () => { + let renderProviderFn: TestUtilTypes.RenderWithAllProvidersFn | null = null; + + beforeEach(() => { + renderProviderFn = TestUtils.getRenderWithAllProvidersFn(); + }); + + it("should render question title skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("submission-skeleton-question-title"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render question link button skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "submission-skeleton-question-link-button", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render question solved by skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "submission-skeleton-question-solved-by", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render question information skeletons", () => { + renderProviderFn?.(); + const info1 = screen.getByTestId("submission-skeleton-question-points"); + const info2 = screen.getByTestId("submission-skeleton-question-difficulty"); + const info3 = screen.getByTestId( + "submission-skeleton-question-acceptance-rate", + ); + + expect(info1).toBeInTheDocument(); + expect(info1).toBeVisible(); + expect(info2).toBeInTheDocument(); + expect(info2).toBeVisible(); + expect(info3).toBeInTheDocument(); + expect(info3).toBeVisible(); + }); + + it("should render user profile button skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId( + "submission-skeleton-user-profile-button", + ); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render question details skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("submission-skeleton-question-details"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); + + it("should render user solution skeleton", () => { + renderProviderFn?.(); + const element = screen.getByTestId("submission-skeleton-user-solution"); + expect(element).toBeInTheDocument(); + expect(element).toBeVisible(); + }); +}); diff --git a/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.tsx b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.tsx new file mode 100644 index 000000000..d891933e4 --- /dev/null +++ b/js/src/app/submission/[submissionId]/_components/SubmissionDetailsContentSkeleton.tsx @@ -0,0 +1,94 @@ +import { Box, Card, Center, Flex, Skeleton } from "@mantine/core"; + +export default function SubmissionDetailsContentSkeleton() { + return ( + +
+ + + + +
+ +
+ + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+
+ ); +}