Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion js/src/__mock__/submissions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { ApiURL } from "@/lib/api/common/apiURL";
import { QuestionDifficulty } from "@/lib/api/types/schema";
import {
components,
LeetcodeTopicEnum,
QuestionDifficulty,
} from "@/lib/api/types/schema";
import { http, HttpResponse } from "msw";

export const MOCK_SUBMISSION_ID = "submission-1";
Expand Down Expand Up @@ -80,6 +84,82 @@ export const submissionDetailsProcessingHandler = http.get(
},
);

const TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1000;

export const submissionDetailsVerifiedHandler = http.get(
submissionDetails.url.toString(),
() => {
return HttpResponse.json({
success: true,
message: "Submission details loaded!",
payload: {
id: MOCK_SUBMISSION_ID,
userId: "user-1",
questionSlug: "two-sum",
questionNumber: 1,
questionTitle: "Two Sum",
questionLink: "https://leetcode.com/problems/two-sum",
questionDifficulty: QuestionDifficulty.Medium,
description: "<p>Given an array of integers, return indices.</p>",
acceptanceRate: 0.49,
pointsAwarded: 10,
discordName: "aphrodite",
nickname: "Aphrodite",
code: "class Solution {}",
runtime: "1ms",
memory: "44MB",
language: "java",
createdAt: "",
submittedAt: new Date(Date.now() - TWO_DAYS_MS).toISOString(),
submissionId: null,
topics: [
{
id: "topic-1",
questionId: MOCK_SUBMISSION_ID,
topicSlug: "array",
topic: LeetcodeTopicEnum.ARRAY,
createdAt: "",
},
],
leetcodeUsername: null,
},
} satisfies components["schemas"]["ApiResponderQuestionWithUserDto"]);
},
);

export const submissionDetailsSparseHandler = http.get(
submissionDetails.url.toString(),
() => {
return HttpResponse.json({
success: true,
message: "Submission details loaded!",
payload: {
id: MOCK_SUBMISSION_ID,
userId: "user-1",
questionSlug: "two-sum",
questionNumber: 1,
questionTitle: "Two Sum",
questionLink: "https://leetcode.com/problems/two-sum",
questionDifficulty: QuestionDifficulty.Hard,
description: null,
acceptanceRate: 0.2,
pointsAwarded: null,
discordName: null,
nickname: null,
code: "public class Solution {}",
runtime: "3ms",
memory: "2MB",
language: "csharp",
createdAt: "",
submittedAt: "",
submissionId: null,
topics: [],
leetcodeUsername: null,
},
} satisfies components["schemas"]["ApiResponderQuestionWithUserDto"]);
},
);

export const submissionDetailsFailedHandler = http.get(
submissionDetails.url.toString(),
() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,63 @@
.title {
font-size: clamp(1.375rem, 4vw, 2rem);
}

.spoilerRoot[data-has-spoiler] {
margin-bottom: calc(24px + var(--mantine-spacing-md));
}

.spoilerControl {
margin-top: var(--mantine-spacing-md);
}

.author {
display: inline-flex;
align-items: center;
gap: 6px;
}

.stat {
padding: var(--mantine-spacing-sm) var(--mantine-spacing-md);
border: 1px solid var(--mantine-color-dark-4);
border-radius: var(--mantine-radius-md);
background-color: var(--mantine-color-dark-7);
}

.description {
min-width: 0;
overflow-wrap: anywhere;
line-height: 1.6;
}

.description img {
max-width: 100%;
height: auto;
border-radius: var(--mantine-radius-sm);
}

.description code {
padding: 1px 5px;
border-radius: var(--mantine-radius-xs);
background-color: var(--mantine-color-dark-5);
font-size: 0.9em;
}

.description > pre {
background-color: rgba(51, 48, 48, 0.8);
border-radius: 1rem;
padding: 2rem;
min-width: 0;
padding: var(--mantine-spacing-md);
border-left: 3px solid var(--mantine-color-patina-6);
border-radius: var(--mantine-radius-md);
background-color: var(--mantine-color-dark-8);
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: anywhere;
}

.description > pre code {
padding: 0;
background-color: transparent;
}

.code {
border-radius: var(--mantine-radius-md);
font-size: 0.875rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
submissionDetailsFailedHandler,
submissionDetailsErrorHandler,
submissionDetailsProcessingHandler,
submissionDetailsSparseHandler,
submissionDetailsVerifiedHandler,
MOCK_SUBMISSION_ID,
} from "@/__mock__/submissions";
import SubmissionDetailsContent from "@/app/submission/[submissionId]/_components/SubmissionDetailsContent";
Expand Down Expand Up @@ -51,7 +53,7 @@ describe("SubmissionDetailsContent API Succeeded", () => {
);

await waitFor(() => {
const link = screen.getByRole("link");
const link = screen.getByRole("link", { name: /open on leetcode/i });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute(
"href",
Expand Down Expand Up @@ -102,34 +104,17 @@ describe("SubmissionDetailsContent API Succeeded", () => {
});
});

it("should render go to profile button after successful API call", async () => {
it("should link author name to user profile after successful API call", async () => {
renderProviderFn?.(
<SubmissionDetailsContent submissionId={MOCK_SUBMISSION_ID} />,
);

await waitFor(() => {
expect(
screen.getByRole("button", { name: /go to profile/i }),
).toBeInTheDocument();
});
});

it("should navigate to user profile when go to profile button is clicked", async () => {
const user = userEvent.setup();
renderProviderFn?.(
<SubmissionDetailsContent submissionId={MOCK_SUBMISSION_ID} />,
);

await waitFor(() => {
expect(
screen.queryByTestId("submission-skeleton-question-title"),
).not.toBeInTheDocument();
});

const profileButton = screen.getByRole("button", {
name: /go to profile/i,
expect(screen.getByRole("link", { name: "aphrodite" })).toHaveAttribute(
"href",
"/user/user-1",
);
});
await user.click(profileButton);
});

it("should render language after successful API call", async () => {
Expand All @@ -148,7 +133,7 @@ describe("SubmissionDetailsContent API Succeeded", () => {
);

await waitFor(() => {
expect(screen.getByText("Runtime: 50ms")).toBeInTheDocument();
expect(screen.getByText("50ms")).toBeInTheDocument();
});
});

Expand All @@ -158,7 +143,7 @@ describe("SubmissionDetailsContent API Succeeded", () => {
);

await waitFor(() => {
expect(screen.getByText("Memory: 16MB")).toBeInTheDocument();
expect(screen.getByText("16MB")).toBeInTheDocument();
});
});

Expand Down Expand Up @@ -213,4 +198,60 @@ describe("SubmissionDetailsContent API Succeeded", () => {
expect(screen.getByText("Submission not found.")).toBeInTheDocument();
});
});

it("should render verified nickname, submitted time and topics when available", async () => {
server.use(submissionDetailsVerifiedHandler);
renderProviderFn?.(
<SubmissionDetailsContent submissionId={MOCK_SUBMISSION_ID} />,
);

await waitFor(() => {
expect(screen.getByRole("link", { name: "Aphrodite" })).toHaveAttribute(
"href",
"/user/user-1",
);
});
expect(screen.queryByText("aphrodite")).not.toBeInTheDocument();
expect(screen.getByText("Submitted")).toBeInTheDocument();
expect(screen.getByText("2d ago")).toBeInTheDocument();
expect(screen.getByText("Array")).toBeInTheDocument();
});

it("should render fallbacks when optional submission fields are missing", async () => {
server.use(submissionDetailsSparseHandler);
renderProviderFn?.(
<SubmissionDetailsContent submissionId={MOCK_SUBMISSION_ID} />,
);

await waitFor(() => {
expect(
screen.getByRole("link", { name: "Unknown user" }),
).toBeInTheDocument();
});
expect(screen.getByText("—")).toBeInTheDocument();
expect(
screen.getByText("No available description found."),
).toBeInTheDocument();
expect(screen.getByText("Csharp")).toBeInTheDocument();
expect(screen.queryByText("Submitted")).not.toBeInTheDocument();
});

it("should copy the solution code to the clipboard", async () => {
const user = userEvent.setup();
renderProviderFn?.(
<SubmissionDetailsContent submissionId={MOCK_SUBMISSION_ID} />,
);

const copyButton = await screen.findByRole("button", {
name: "Copy code",
});
await user.click(copyButton);

await waitFor(async () => {
expect(await navigator.clipboard.readText()).toBe(
"def twoSum(nums, target):\n pass",
);
});
expect(await screen.findByText("Copied")).toBeInTheDocument();
});
});
Loading
Loading