diff --git a/js/src/__mock__/leaderboard/index.ts b/js/src/__mock__/leaderboard/index.ts index 1162ac73b..6bb39daf9 100644 --- a/js/src/__mock__/leaderboard/index.ts +++ b/js/src/__mock__/leaderboard/index.ts @@ -7,6 +7,13 @@ const currentMetadata = ApiURL.create("/api/leaderboard/current/metadata", { method: "GET", }); +const currentLeaderboardUsers = ApiURL.create( + "/api/leaderboard/current/user/all", + { + method: "GET", + }, +); + export const MOCK_LEADERBOARD_ID = uuid(); const metadataById = ApiURL.create( "/api/leaderboard/{leaderboardId}/metadata", @@ -107,3 +114,59 @@ export const catastrophicLeaderboardHandlers = [ return HttpResponse.error(); }), ]; + +export const currentLeaderboardUsersHandler = http.get( + currentLeaderboardUsers.url.toString(), + () => { + return HttpResponse.json({ + success: true, + message: "Leaderboard users loaded!", + payload: { + hasNextPage: false, + pages: 1, + pageSize: 20, + items: [ + { + index: 1, + id: "user-1", + discordId: "discord-1", + discordName: "aphrodite", + leetcodeUsername: "aphrodite", + nickname: null, + admin: false, + profileUrl: null, + tags: [], + achievements: [], + totalScore: 120, + }, + { + index: 2, + id: "user-2", + discordId: "discord-2", + discordName: "poseidon", + leetcodeUsername: "poseidon", + nickname: null, + admin: false, + profileUrl: null, + tags: [], + achievements: [], + totalScore: 110, + }, + { + index: 3, + id: "user-3", + discordId: "discord-3", + discordName: "hermes", + leetcodeUsername: "hermes", + nickname: null, + admin: false, + profileUrl: null, + tags: [], + achievements: [], + totalScore: 100, + }, + ], + }, + } satisfies ReturnType); + }, +); diff --git a/js/src/app/embed/leaderboard/_components/OrgEmbedView.test.tsx b/js/src/app/embed/leaderboard/_components/OrgEmbedView.test.tsx new file mode 100644 index 000000000..61f8e3681 --- /dev/null +++ b/js/src/app/embed/leaderboard/_components/OrgEmbedView.test.tsx @@ -0,0 +1,80 @@ +import { + currentLeaderboardUsersHandler, + failedLeaderboardHandlers, + successfulLeaderboardHandlers, +} from "@/__mock__/leaderboard"; +import OrgEmbedView from "@/app/embed/leaderboard/_components/OrgEmbedView"; +import { TestUtils, TestUtilTypes } from "@/lib/test"; +import { screen, waitFor } from "@testing-library/react"; +import { setupServer } from "msw/node"; + +describe("OrgEmbedView with date range", () => { + const server = setupServer( + currentLeaderboardUsersHandler, + ...successfulLeaderboardHandlers, + ); + + beforeAll(() => server.listen()); + afterEach(() => { + server.resetHandlers(); + }); + afterAll(() => server.close()); + + let renderProviderFn: TestUtilTypes.RenderWithAllProvidersFn | null = null; + beforeEach(() => { + renderProviderFn = TestUtils.getRenderWithAllProvidersFn(); + }); + + it("should include startDate and endDate when metadata is available", async () => { + renderProviderFn?.(); + + await waitFor(() => { + const aphrodite = screen.getByText("aphrodite"); + expect(aphrodite).toBeInTheDocument(); + }); + + const aphroditeLink = screen.getByText("aphrodite").closest("a"); + + expect(aphroditeLink).toBeInTheDocument(); + expect(aphroditeLink).toHaveAttribute( + "href", + expect.stringContaining("/user/user-1/submissions?startDate="), + ); + expect(aphroditeLink).toHaveAttribute( + "href", + expect.stringContaining("endDate="), + ); + }); +}); + +describe("OrgEmbedView without date range", () => { + const server = setupServer( + currentLeaderboardUsersHandler, + ...failedLeaderboardHandlers, + ); + + beforeAll(() => server.listen()); + afterEach(() => { + server.resetHandlers(); + }); + afterAll(() => server.close()); + + let renderProviderFn: TestUtilTypes.RenderWithAllProvidersFn | null = null; + beforeEach(() => { + renderProviderFn = TestUtils.getRenderWithAllProvidersFn(); + }); + + it("should omit date range when metadata is unavailable", async () => { + renderProviderFn?.(); + + await waitFor(() => { + const aphrodite = screen.getByText("aphrodite"); + expect(aphrodite).toBeInTheDocument(); + }); + + const aphroditeLink = screen.getByText("aphrodite").closest("a"); + + expect(aphroditeLink).toBeInTheDocument(); + expect(aphroditeLink).toHaveAttribute("href", "/user/user-1/submissions"); + }); +}); diff --git a/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx b/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx index d9dc0cdd6..d5ceb141f 100644 --- a/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx +++ b/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx @@ -4,7 +4,11 @@ import LeaderboardCard from "@/components/ui/LeaderboardCard"; import CustomPagination from "@/components/ui/table/CustomPagination"; import SearchBox from "@/components/ui/table/SearchBox"; import Toast from "@/components/ui/toast/Toast"; -import { useCurrentLeaderboardUsersQuery } from "@/lib/api/queries/leaderboard"; +import { + useCurrentLeaderboardMetadataQuery, + useCurrentLeaderboardUsersQuery, +} from "@/lib/api/queries/leaderboard"; +import { formatLeaderboardDateRange } from "@/lib/helper/leaderboardDateRange"; import getOrdinal from "@/lib/helper/ordinal"; import { theme } from "@/lib/theme"; import { @@ -45,6 +49,13 @@ export default function OrgLeaderboardEmbed() { onFilterReset, } = useCurrentLeaderboardUsersQuery({ pageSize }); + const metadataQuery = useCurrentLeaderboardMetadataQuery(); + + const dateRange = + metadataQuery.data?.success ? + formatLeaderboardDateRange(metadataQuery.data.payload) + : undefined; + const activeFilter = useMemo(() => { const active = Object.typedEntries(filters).filter( ([, enabled]) => enabled, @@ -106,22 +117,28 @@ export default function OrgLeaderboardEmbed() { mb="md" > {page === 1 && second && !debouncedQuery && ( - + + + )} {page === 1 && first && !debouncedQuery && (