Skip to content

Commit 61a63c4

Browse files
authored
Merge pull request #1857 from AtCoder-NoviSteps/#1856
🚨 Add tests for contest table providers (#1856)
2 parents fc548de + 6fccadb commit 61a63c4

File tree

2 files changed

+425
-0
lines changed

2 files changed

+425
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { describe, test, expect, vi } from 'vitest';
2+
3+
import { ContestType } from '$lib/types/contest';
4+
import type { TaskResult, TaskResults } from '$lib/types/task';
5+
6+
import { contestTableProviders } from '$lib/utils/contest_table_provider';
7+
import { taskResultsForContestTableProvider } from './test_cases/contest_table_provider';
8+
9+
// Mock the imported functions
10+
vi.mock('$lib/utils/contest', () => ({
11+
classifyContest: vi.fn((contestId: string) => {
12+
if (contestId.startsWith('abc')) {
13+
return ContestType.ABC;
14+
}
15+
16+
return ContestType.OTHERS;
17+
}),
18+
19+
getContestNameLabel: vi.fn((contestId: string) => {
20+
if (contestId.startsWith('abc')) {
21+
return `ABC ${contestId.replace('abc', '')}`;
22+
}
23+
24+
return contestId;
25+
}),
26+
}));
27+
28+
vi.mock('$lib/utils/task', () => ({
29+
getTaskTableHeaderName: vi.fn((_: ContestType, task: TaskResult) => {
30+
return `${task.task_table_index}`;
31+
}),
32+
}));
33+
34+
describe('ContestTableProviderBase and implementations', () => {
35+
const mockTaskResults: TaskResults = taskResultsForContestTableProvider;
36+
37+
const getContestRound = (contestId: string): number => {
38+
const roundString = contestId.replace(/^\D+/, '');
39+
const round = parseInt(roundString, 10);
40+
41+
if (isNaN(round)) {
42+
throw new Error(`Invalid contest ID format: ${contestId}`);
43+
}
44+
45+
return round;
46+
};
47+
48+
describe('ABC latest 20 rounds provider', () => {
49+
test('expects to filter tasks to include only ABC contests', () => {
50+
const provider = contestTableProviders.abcLatest20Rounds;
51+
const filtered = provider.filter(mockTaskResults);
52+
53+
expect(filtered.every((task) => task.contest_id.startsWith('abc'))).toBeTruthy();
54+
expect(filtered).not.toContainEqual(expect.objectContaining({ contest_id: 'arc100' }));
55+
});
56+
57+
test('expects to limit results to the latest 20 rounds', () => {
58+
const provider = contestTableProviders.abcLatest20Rounds;
59+
60+
const largeDataset = [...mockTaskResults];
61+
const filtered = provider.filter(largeDataset);
62+
const uniqueContests = new Set(filtered.map((task) => task.contest_id));
63+
expect(uniqueContests.size).toBe(20);
64+
65+
// Verify these are the latest 20 rounds
66+
const contestRounds = Array.from(uniqueContests)
67+
.map((id) => getContestRound(id))
68+
.sort((a, b) => b - a); // Sort in descending order
69+
70+
// Validate if the rounds are sequential and latest
71+
const latestRound = Math.max(...contestRounds);
72+
const expectedRounds = Array.from({ length: 20 }, (_, i) => latestRound - i);
73+
expect(contestRounds).toEqual(expectedRounds);
74+
});
75+
76+
test('expects to generate correct table structure', () => {
77+
const provider = contestTableProviders.abcLatest20Rounds;
78+
const filtered = provider.filter(mockTaskResults);
79+
const table = provider.generateTable(filtered);
80+
81+
expect(table).toHaveProperty('abc378');
82+
expect(table.abc378).toHaveProperty('G');
83+
expect(table.abc378.G).toEqual(
84+
expect.objectContaining({ contest_id: 'abc378', task_id: 'abc378_g' }),
85+
);
86+
87+
expect(table).toHaveProperty('abc397');
88+
expect(table.abc397).toHaveProperty('G');
89+
expect(table.abc397.G).toEqual(
90+
expect.objectContaining({ contest_id: 'abc397', task_id: 'abc397_g' }),
91+
);
92+
});
93+
94+
test('expects to get correct metadata', () => {
95+
const provider = contestTableProviders.abcLatest20Rounds;
96+
const metadata = provider.getMetadata();
97+
98+
expect(metadata.title).toBe('AtCoder Beginner Contest 最新 20 回');
99+
expect(metadata.buttonLabel).toBe('ABC 最新 20 回');
100+
expect(metadata.ariaLabel).toBe('Filter ABC latest 20 rounds');
101+
});
102+
103+
test('expects to format contest round label correctly', () => {
104+
const provider = contestTableProviders.abcLatest20Rounds;
105+
const label = provider.getContestRoundLabel('abc378');
106+
107+
expect(label).toBe('378');
108+
});
109+
});
110+
111+
describe('ABC319 onwards provider', () => {
112+
test('expects to filter tasks to include only ABC319 and later', () => {
113+
const provider = contestTableProviders.abc319Onwards;
114+
const filtered = provider.filter(mockTaskResults);
115+
116+
expect(filtered.every((task) => task.contest_id.startsWith('abc'))).toBeTruthy();
117+
expect(
118+
filtered.every((task) => {
119+
const round = getContestRound(task.contest_id);
120+
return round >= 319 && round <= 999;
121+
}),
122+
).toBeTruthy();
123+
});
124+
125+
test('expects to get correct metadata', () => {
126+
const provider = contestTableProviders.abc319Onwards;
127+
const metadata = provider.getMetadata();
128+
129+
expect(metadata.title).toBe('AtCoder Beginner Contest 319 〜 ');
130+
expect(metadata.buttonLabel).toBe('ABC 319 〜 ');
131+
expect(metadata.ariaLabel).toBe('Filter contests from ABC 319 onwards');
132+
});
133+
134+
test('expects to format contest round label correctly', () => {
135+
const provider = contestTableProviders.abc319Onwards;
136+
const label = provider.getContestRoundLabel('abc397');
137+
138+
expect(label).toBe('397');
139+
});
140+
});
141+
142+
describe('ABC212 to ABC318 provider', () => {
143+
test('expects to filter tasks to include only ABC between 212 and 318', () => {
144+
const provider = contestTableProviders.fromAbc212ToAbc318;
145+
const filtered = provider.filter(mockTaskResults);
146+
147+
expect(filtered.every((task) => task.contest_id.startsWith('abc'))).toBeTruthy();
148+
expect(
149+
filtered.every((task) => {
150+
const round = getContestRound(task.contest_id);
151+
return round >= 212 && round <= 318;
152+
}),
153+
).toBeTruthy();
154+
});
155+
156+
test('expects to get correct metadata', () => {
157+
const provider = contestTableProviders.fromAbc212ToAbc318;
158+
const metadata = provider.getMetadata();
159+
160+
expect(metadata.title).toBe('AtCoder Beginner Contest 212 〜 318');
161+
expect(metadata.buttonLabel).toBe('ABC 212 〜 318');
162+
expect(metadata.ariaLabel).toBe('Filter contests from ABC 212 to ABC 318');
163+
});
164+
165+
test('expects to format contest round label correctly', () => {
166+
const provider = contestTableProviders.fromAbc212ToAbc318;
167+
const label = provider.getContestRoundLabel('abc318');
168+
169+
expect(label).toBe('318');
170+
});
171+
});
172+
173+
describe('Common provider functionality', () => {
174+
test('expects to get contest round IDs correctly', () => {
175+
const provider = contestTableProviders.abcLatest20Rounds;
176+
// Use a subset of the mock data that covers the relevant contest IDs
177+
const filtered = mockTaskResults.filter((task) =>
178+
['abc397', 'abc319', 'abc318'].includes(task.contest_id),
179+
);
180+
181+
const roundIds = provider.getContestRoundIds(filtered);
182+
183+
expect(roundIds).toEqual(['abc397', 'abc319', 'abc318']);
184+
});
185+
186+
test('expects to get header IDs for tasks correctly', () => {
187+
const provider = contestTableProviders.abcLatest20Rounds;
188+
const filtered = mockTaskResults.filter((task) => task.contest_id === 'abc319');
189+
const headerIds = provider.getHeaderIdsForTask(filtered);
190+
191+
expect(headerIds).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
192+
});
193+
});
194+
});

0 commit comments

Comments
 (0)