Skip to content

Commit

Permalink
feat: Leaderboard uses fetched data
Browse files Browse the repository at this point in the history
  • Loading branch information
DaleSeo committed Nov 22, 2024
1 parent 6a2fb6f commit 518c9b4
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 108 deletions.
10 changes: 5 additions & 5 deletions src/components/Leaderboard/Leaderboard.module.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.leaderboard {
/* 컴포넌트가 헤더에 가려져서 임의로 지정했으니 컴포넌트 개발자가 설계에 맞게 수정해주시면 됩니다 */
margin-top: 100px;
/* TODO remove after replacing the inline article with the card component */
article {
ul {
li {
margin-bottom: 20px;
}
}
border: 1px solid gray;
border-radius: 16px;
padding: 16px;
margin: 16px;
}
}
154 changes: 77 additions & 77 deletions src/components/Leaderboard/Leaderboard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,80 @@
import { beforeEach, describe, expect, it } from "vitest";
import { render, screen, within } from "@testing-library/react";
import { expect, test, vi } from "vitest";
import { mock } from "vitest-mock-extended";
import { render, screen } from "@testing-library/react";
import type { Member } from "../../api/services/common/types";
import useMembers from "../../hooks/useMembers";
import Leaderboard from "./Leaderboard";

describe("<Leaderboard/>", () => {
beforeEach(() => render(<Leaderboard />));

it("renders the page header", () => {
const header = screen.getByRole("banner");
expect(header).toBeInTheDocument();
});

it("renders the members list section", () => {
expect(
screen.getByRole("article", { name: /members list/i }),
).toBeInTheDocument();
});

it("renders the title", () => {
const heading = screen.getByRole("heading", { level: 1 });
expect(heading).toHaveTextContent("Leaderboard");
});

it("renders the member information", () => {
const members = [
{ name: "DaleSeo", solved: 71, rank: "새싹" },
{ name: "sounmind", solved: 69, rank: "나무" },
{ name: "yolophg", solved: 65, rank: "새싹" },
{ name: "Sunjae95", solved: 63, rank: "나무" },
{ name: "HC-kang", solved: 62, rank: "나무" },
{ name: "SamTheKorean", solved: 60, rank: "나무" },
];

const memberItems = within(
screen.getByRole("article", { name: /members list/i }),
).getAllByRole("listitem");

expect(memberItems).toHaveLength(members.length);

members.forEach((member, index) => {
const memberItem = memberItems[index];
expect(memberItem).toHaveTextContent(`등급: ${member.rank}`);
expect(memberItem).toHaveTextContent(`진행 상황: ${member.solved}`);
});
});

it("renders the links for members", () => {
const members = [
{ name: "DaleSeo" },
{ name: "sounmind" },
{ name: "yolophg" },
{ name: "Sunjae95" },
{ name: "HC-kang" },
{ name: "SamTheKorean" },
];

const progressLinks = screen.getAllByRole("link", { name: "풀이 보기" });
const certificateLinks = screen.getAllByRole("link", {
name: "수료증 보기",
});

expect(progressLinks).toHaveLength(members.length);
expect(certificateLinks).toHaveLength(members.length);

members.forEach((member, index) => {
expect(progressLinks[index]).toHaveAttribute(
"href",
`/progress?member=${member.name}`,
);
expect(certificateLinks[index]).toHaveAttribute(
"href",
`/certificate?member=${member.name}`,
);
});
});

it("renders footer", () => {
expect(screen.getByRole("contentinfo"));
});
vi.mock("../../hooks/useMembers");

test("render the loading message while fetching members", () => {
vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: true, error: null, members: [] }),
);

render(<Leaderboard />);

expect(screen.getByText(/loading/i)).toBeInTheDocument();
});

test("render the error message while fetching members", () => {
vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: false, error: new Error(), members: [] }),
);

render(<Leaderboard />);

expect(screen.getByText(/error/i)).toBeInTheDocument();
});

test("render the site header", () => {
vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: false, error: null, members: [] }),
);

render(<Leaderboard />);

const header = screen.getByRole("banner");
expect(header).toBeInTheDocument();
});

test("render the page title", () => {
vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: false, error: null, members: [] }),
);

render(<Leaderboard />);
const heading = screen.getByRole("heading", { level: 1 });
expect(heading).toHaveTextContent("리더보드");
});

test("render the member cards", () => {
const members = [
mock<Member>(),
mock<Member>(),
mock<Member>(),
mock<Member>(),
mock<Member>(),
mock<Member>(),
];

vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: false, error: null, members }),
);

render(<Leaderboard />);

const memberCards = screen.getAllByRole("article");

expect(memberCards).toHaveLength(members.length);
});

test("render the site footer", () => {
vi.mocked(useMembers).mockReturnValue(
mock({ isLoading: false, error: null, members: [mock<Member>()] }),
);

render(<Leaderboard />);

expect(screen.getByRole("contentinfo", { name: "Site Footer" }));
});
48 changes: 22 additions & 26 deletions src/components/Leaderboard/Leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,37 @@ import useMembers from "../../hooks/useMembers";
import styles from "./Leaderboard.module.css";
export default function Leaderboard() {
const { members, isLoading, error } = useMembers({ getMembers });
console.log({ members, isLoading, error });

const mockMembers = [
{ name: "DaleSeo", solved: 71, rank: "새싹" },
{ name: "sounmind", solved: 69, rank: "나무" },
{ name: "yolophg", solved: 65, rank: "새싹" },
{ name: "Sunjae95", solved: 63, rank: "나무" },
{ name: "HC-kang", solved: 62, rank: "나무" },
{ name: "SamTheKorean", solved: 60, rank: "나무" },
];
if (isLoading) return <p>Loading...</p>; // TODO replace with a proper loading component
if (error) return <p>Error!</p>; // TODO replace with a proper error component

return (
<main className={styles.leaderboard}>
<Header />
<h1>Leaderboard </h1>

<article aria-labelledby="leaderboard">
<h2 id="leaderboard">Members List</h2>
<ul>
{mockMembers.map((member) => (
<li key={member.name}>
<div>등급: {member.rank}</div>
<div>진행 상황: {member.solved}</div>
<div>
<a href={`/progress?member=${member.name}`}>
<h1>리더보드</h1>
<ul>
{members.map((member) => (
<article key={member.name}>
<header>
<h2>{member.name}</h2>
</header>
<dl>
<dt>등급</dt>
<dd>{member.grade}</dd>
<dt>진행 상황</dt>
<dd>{member.progress}</dd>
<footer>
<a href={`/progress?member=${member.id}`}>
<button>풀이 보기</button>
</a>
<a href={`/certificate?member=${member.name}`}>
<a href={`/certificate?member=${member.id}`}>
<button>수료증 보기</button>
</a>
</div>
</li>
))}
</ul>
</article>
</footer>
</dl>
</article>
))}
</ul>
<Footer />
</main>
);
Expand Down

0 comments on commit 518c9b4

Please sign in to comment.