99 ABC212ToABC318Provider ,
1010 EDPCProvider ,
1111 TDPCProvider ,
12+ JOIFirstQualRoundProvider ,
1213 ContestTableProviderGroup ,
1314 prepareContestProviderPresets ,
1415} from '$lib/utils/contest_table_provider' ;
@@ -23,6 +24,8 @@ vi.mock('$lib/utils/contest', () => ({
2324 return ContestType . EDPC ;
2425 } else if ( contestId === 'tdpc' ) {
2526 return ContestType . TDPC ;
27+ } else if ( contestId . startsWith ( 'joi' ) ) {
28+ return ContestType . JOI ;
2629 }
2730
2831 return ContestType . OTHERS ;
@@ -33,6 +36,16 @@ vi.mock('$lib/utils/contest', () => ({
3336 return `ABC ${ contestId . replace ( 'abc' , '' ) } ` ;
3437 } else if ( contestId === 'dp' || contestId === 'tdpc' ) {
3538 return '' ;
39+ } else if ( contestId . startsWith ( 'joi' ) ) {
40+ // First qual round
41+ const matched = contestId . match ( / j o i ( \d { 4 } ) y o 1 ( [ a b c ] ) / ) ;
42+
43+ if ( matched ) {
44+ const [ , year , round ] = matched ;
45+ const roundMap : Record < string , string > = { a : '1' , b : '2' , c : '3' } ;
46+
47+ return `${ year } 第 ${ roundMap [ round ] } 回` ;
48+ }
3649 }
3750
3851 return contestId ;
@@ -126,6 +139,7 @@ describe('ContestTableProviderBase and implementations', () => {
126139
127140 expect ( displayConfig . isShownHeader ) . toBe ( true ) ;
128141 expect ( displayConfig . isShownRoundLabel ) . toBe ( true ) ;
142+ expect ( displayConfig . roundLabelWidth ) . toBe ( 'xl:w-16' ) ;
129143 expect ( displayConfig . isShownTaskIndex ) . toBe ( false ) ;
130144 } ) ;
131145 } ) ;
@@ -165,6 +179,7 @@ describe('ContestTableProviderBase and implementations', () => {
165179
166180 expect ( displayConfig . isShownHeader ) . toBe ( true ) ;
167181 expect ( displayConfig . isShownRoundLabel ) . toBe ( true ) ;
182+ expect ( displayConfig . roundLabelWidth ) . toBe ( 'xl:w-16' ) ;
168183 expect ( displayConfig . isShownTaskIndex ) . toBe ( false ) ;
169184 } ) ;
170185 } ) ;
@@ -204,6 +219,7 @@ describe('ContestTableProviderBase and implementations', () => {
204219
205220 expect ( displayConfig . isShownHeader ) . toBe ( true ) ;
206221 expect ( displayConfig . isShownRoundLabel ) . toBe ( true ) ;
222+ expect ( displayConfig . roundLabelWidth ) . toBe ( 'xl:w-16' ) ;
207223 expect ( displayConfig . isShownTaskIndex ) . toBe ( false ) ;
208224 } ) ;
209225 } ) ;
@@ -223,6 +239,7 @@ describe('ContestTableProviderBase and implementations', () => {
223239
224240 expect ( displayConfig . isShownHeader ) . toBe ( false ) ;
225241 expect ( displayConfig . isShownRoundLabel ) . toBe ( false ) ;
242+ expect ( displayConfig . roundLabelWidth ) . toBe ( '' ) ;
226243 expect ( displayConfig . isShownTaskIndex ) . toBe ( true ) ;
227244 } ) ;
228245
@@ -249,6 +266,7 @@ describe('ContestTableProviderBase and implementations', () => {
249266
250267 expect ( displayConfig . isShownHeader ) . toBe ( false ) ;
251268 expect ( displayConfig . isShownRoundLabel ) . toBe ( false ) ;
269+ expect ( displayConfig . roundLabelWidth ) . toBe ( '' ) ;
252270 expect ( displayConfig . isShownTaskIndex ) . toBe ( true ) ;
253271 } ) ;
254272
@@ -260,6 +278,123 @@ describe('ContestTableProviderBase and implementations', () => {
260278 } ) ;
261279 } ) ;
262280
281+ describe ( 'JOI First Qual Round provider' , ( ) => {
282+ test ( 'expects to filter tasks to include only JOI contests' , ( ) => {
283+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
284+ const mockJOITasks = [
285+ { contest_id : 'joi2024yo1a' , task_id : 'joi2024yo1a_a' } ,
286+ { contest_id : 'joi2024yo1b' , task_id : 'joi2024yo1b_a' } ,
287+ { contest_id : 'joi2024yo1c' , task_id : 'joi2024yo1c_a' } ,
288+ { contest_id : 'joi2023yo1a' , task_id : 'joi2023yo1a_a' } ,
289+ { contest_id : 'joi2024yo2' , task_id : 'joi2024yo2_a' } ,
290+ { contest_id : 'abc123' , task_id : 'abc123_a' } ,
291+ ] ;
292+
293+ const filtered = provider . filter ( mockJOITasks as any ) ;
294+
295+ expect ( filtered ?. every ( ( task ) => task . contest_id . startsWith ( 'joi' ) ) ) . toBe ( true ) ;
296+ expect ( filtered ?. length ) . toBe ( 4 ) ;
297+ expect ( filtered ?. every ( ( task ) => task . contest_id . match ( / j o i \d { 4 } y o 1 [ a b c ] / ) ) ) . toBe ( true ) ;
298+ } ) ;
299+
300+ test ( 'expects to filter contests by year correctly' , ( ) => {
301+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
302+ const mockJOITasks = [
303+ { contest_id : 'joi2024yo1a' , task_id : 'joi2024yo1a_a' } ,
304+ { contest_id : 'joi2024yo1b' , task_id : 'joi2024yo1b_a' } ,
305+ { contest_id : 'joi2024yo1c' , task_id : 'joi2024yo1c_a' } ,
306+ { contest_id : 'joi2023yo1a' , task_id : 'joi2023yo1a_a' } ,
307+ { contest_id : 'joi2023yo1b' , task_id : 'joi2023yo1b_b' } ,
308+ { contest_id : 'joi2022yo1a' , task_id : 'joi2022yo1a_c' } ,
309+ ] ;
310+
311+ const filtered = provider . filter ( mockJOITasks as any ) ;
312+
313+ expect ( filtered ?. length ) . toBe ( 6 ) ;
314+ expect ( filtered ?. filter ( ( task ) => task . contest_id . includes ( '2024' ) ) . length ) . toBe ( 3 ) ;
315+ expect ( filtered ?. filter ( ( task ) => task . contest_id . includes ( '2023' ) ) . length ) . toBe ( 2 ) ;
316+ expect ( filtered ?. filter ( ( task ) => task . contest_id . includes ( '2022' ) ) . length ) . toBe ( 1 ) ;
317+ } ) ;
318+
319+ test ( 'expects to get correct metadata' , ( ) => {
320+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
321+ const metadata = provider . getMetadata ( ) ;
322+
323+ expect ( metadata . title ) . toBe ( 'JOI 一次予選' ) ;
324+ expect ( metadata . abbreviationName ) . toBe ( 'joiFirstQualRound' ) ;
325+ } ) ;
326+
327+ test ( 'expects to get correct display configuration' , ( ) => {
328+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
329+ const displayConfig = provider . getDisplayConfig ( ) ;
330+
331+ expect ( displayConfig . isShownHeader ) . toBe ( true ) ;
332+ expect ( displayConfig . isShownRoundLabel ) . toBe ( true ) ;
333+ expect ( displayConfig . roundLabelWidth ) . toBe ( 'xl:w-28' ) ;
334+ expect ( displayConfig . isShownTaskIndex ) . toBe ( false ) ;
335+ } ) ;
336+
337+ test ( 'expects to format contest round label correctly' , ( ) => {
338+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
339+
340+ expect ( provider . getContestRoundLabel ( 'joi2024yo1a' ) ) . toBe ( '2024 第 1 回' ) ;
341+ expect ( provider . getContestRoundLabel ( 'joi2024yo1b' ) ) . toBe ( '2024 第 2 回' ) ;
342+ expect ( provider . getContestRoundLabel ( 'joi2024yo1c' ) ) . toBe ( '2024 第 3 回' ) ;
343+ expect ( provider . getContestRoundLabel ( 'joi2023yo1a' ) ) . toBe ( '2023 第 1 回' ) ;
344+ expect ( provider . getContestRoundLabel ( 'joi2023yo1b' ) ) . toBe ( '2023 第 2 回' ) ;
345+ expect ( provider . getContestRoundLabel ( 'joi2023yo1c' ) ) . toBe ( '2023 第 3 回' ) ;
346+ } ) ;
347+
348+ test ( 'expects to handle invalid contest IDs gracefully' , ( ) => {
349+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
350+
351+ expect ( provider . getContestRoundLabel ( 'invalid-id' ) ) . toBe ( 'invalid-id' ) ;
352+ expect ( provider . getContestRoundLabel ( 'joi2024yo1d' ) ) . toBe ( 'joi2024yo1d' ) ; // Invalid round
353+ expect ( provider . getContestRoundLabel ( 'joi2024yo2' ) ) . toBe ( 'joi2024yo2' ) ; // Not first qual round
354+ } ) ;
355+
356+ test ( 'expects to generate correct table structure' , ( ) => {
357+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
358+ const mockJOITasks = [
359+ { contest_id : 'joi2024yo1a' , task_id : 'joi2024yo1a_a' , task_table_index : 'A' } ,
360+ { contest_id : 'joi2024yo1a' , task_id : 'joi2024yo1a_b' , task_table_index : 'B' } ,
361+ { contest_id : 'joi2024yo1b' , task_id : 'joi2024yo1b_a' , task_table_index : 'A' } ,
362+ { contest_id : 'joi2024yo1c' , task_id : 'joi2024yo1c_c' , task_table_index : 'C' } ,
363+ ] ;
364+
365+ const table = provider . generateTable ( mockJOITasks as any ) ;
366+
367+ expect ( table ) . toHaveProperty ( 'joi2024yo1a' ) ;
368+ expect ( table ) . toHaveProperty ( 'joi2024yo1b' ) ;
369+ expect ( table ) . toHaveProperty ( 'joi2024yo1c' ) ;
370+ expect ( table . joi2024yo1a ) . toHaveProperty ( 'A' ) ;
371+ expect ( table . joi2024yo1a ) . toHaveProperty ( 'B' ) ;
372+ expect ( table . joi2024yo1b ) . toHaveProperty ( 'A' ) ;
373+ expect ( table . joi2024yo1c ) . toHaveProperty ( 'C' ) ;
374+ } ) ;
375+
376+ test ( 'expects to get contest round IDs correctly' , ( ) => {
377+ const provider = new JOIFirstQualRoundProvider ( ContestType . JOI ) ;
378+ const mockJOITasks = [
379+ { contest_id : 'joi2024yo1a' , task_id : 'joi2024yo1a_a' } ,
380+ { contest_id : 'joi2024yo1b' , task_id : 'joi2024yo1b_a' } ,
381+ { contest_id : 'joi2024yo1c' , task_id : 'joi2024yo1c_a' } ,
382+ { contest_id : 'joi2023yo1a' , task_id : 'joi2023yo1a_a' } ,
383+ { contest_id : 'joi2023yo1b' , task_id : 'joi2023yo1b_a' } ,
384+ { contest_id : 'joi2023yo1c' , task_id : 'joi2023yo1c_a' } ,
385+ ] ;
386+
387+ const roundIds = provider . getContestRoundIds ( mockJOITasks as any ) ;
388+
389+ expect ( roundIds ) . toContain ( 'joi2024yo1a' ) ;
390+ expect ( roundIds ) . toContain ( 'joi2024yo1b' ) ;
391+ expect ( roundIds ) . toContain ( 'joi2024yo1c' ) ;
392+ expect ( roundIds ) . toContain ( 'joi2023yo1a' ) ;
393+ expect ( roundIds ) . toContain ( 'joi2023yo1b' ) ;
394+ expect ( roundIds ) . toContain ( 'joi2023yo1c' ) ;
395+ } ) ;
396+ } ) ;
397+
263398 describe ( 'Common provider functionality' , ( ) => {
264399 test ( 'expects to get contest round IDs correctly' , ( ) => {
265400 const provider = new ABCLatest20RoundsProvider ( ContestType . ABC ) ;
0 commit comments