From 4ee6b29cb3df616cfa4c4c8fb9a9c10329af4b48 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Thu, 5 Feb 2026 11:52:14 +0000 Subject: [PATCH 1/2] chore: Update button label in contest table (#3135) --- .../how-to-add-contest-table-provider.md | 55 ++++++++++++++++++- src/lib/utils/contest_table_provider.ts | 6 +- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/docs/guides/how-to-add-contest-table-provider.md b/docs/guides/how-to-add-contest-table-provider.md index 591e86522..9e2f2a338 100644 --- a/docs/guides/how-to-add-contest-table-provider.md +++ b/docs/guides/how-to-add-contest-table-provider.md @@ -324,6 +324,59 @@ describe('MyNewProvider', () => { }); ``` +### prepareContestProviderPresets() のテスト + +Provider クラスのテスト完了後、`prepareContestProviderPresets()` から返されるグループ(`ContestTableProviderGroup`)の動作を検証します。 + +**テスト項目**: + +1. **グループ名の確認** + + ```typescript + const group = prepareContestProviderPresets().MyNewProvider(); + expect(group.getGroupName()).toBe('Expected Group Name'); + ``` + +2. **グループメタデータの確認**(`buttonLabel`、`ariaLabel`) + + ```typescript + expect(group.getMetadata()).toEqual({ + buttonLabel: 'Button Label', + ariaLabel: 'ARIA Label', + }); + ``` + +3. **プロバイダー数の確認** + - **単一プロバイダーグループ**: `getSize()` が 1 を返す + - **複数プロバイダーグループ**: `getSize()` が複数を返す(例:ACL は 3) + +4. **プロバイダーインスタンス型の確認** + - **単一プロバイダー**: + ```typescript + expect(group.getProvider(ContestType.MY_NEW)).toBeInstanceOf(MyNewProvider); + ``` + - **複数プロバイダー**(セクション指定あり): + ```typescript + expect(group.getProvider(ContestType.TESSOKU_BOOK, 'examples')).toBeInstanceOf( + TessokuBookForExamplesProvider, + ); + expect(group.getProvider(ContestType.TESSOKU_BOOK, 'practicals')).toBeInstanceOf( + TessokuBookForPracticalsProvider, + ); + ``` + +**セクション識別子の指定**: + +複数プロバイダーを含むグループでは、`getProvider()` の第2引数にセクション識別子を渡します。セクション定数は `src/lib/types/contest_table_provider.ts` で定義されています: + +- `TESSOKU_SECTIONS`: `examples`、`practicals`、`challenges` +- `JOI_SECOND_QUAL_ROUND_SECTIONS`: `'2020Onwards'`、`from2006To2019` +- `JOI_FINAL_ROUND_SECTIONS`: `semiFinal` + +詳細な実装例は [単体テストファイル](../src/test/lib/utils/contest_table_provider.test.ts) を参照してください。 + +--- + ### モックデータ準備 **ステップ1: データソース確認** @@ -479,4 +532,4 @@ describe('CustomProvider with unique config', () => { --- -**最終更新**: 2026-02-01 +**最終更新**: 2026-02-05 diff --git a/src/lib/utils/contest_table_provider.ts b/src/lib/utils/contest_table_provider.ts index a7f974d6e..31a32781f 100644 --- a/src/lib/utils/contest_table_provider.ts +++ b/src/lib/utils/contest_table_provider.ts @@ -1278,7 +1278,7 @@ export const prepareContestProviderPresets = () => { */ Acl: () => new ContestTableProviderGroup(`AtCoder Library Contests`, { - buttonLabel: 'ACL', + buttonLabel: 'AtCoder Library (ACL)', ariaLabel: 'Filter ACL Contests', }).addProviders( new ACLPracticeProvider(ContestType.ACL_PRACTICE), @@ -1293,8 +1293,8 @@ export const prepareContestProviderPresets = () => { }).addProvider(new JOIFirstQualRoundProvider(ContestType.JOI)), JOISecondQualAndSemiFinalRound: () => - new ContestTableProviderGroup(`JOI 二次予選・予選(旧形式)・本選`, { - buttonLabel: 'JOI 二次予選・予選(旧形式)・本選', + new ContestTableProviderGroup(`JOI 二次予選・本選`, { + buttonLabel: 'JOI 二次予選・本選', ariaLabel: 'Filter JOI Second Qual Round', }).addProviders( new JOISecondQualRound2020OnwardsProvider(ContestType.JOI), From c1231996b70e64905af8f2402eda1c127ccf1561 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Thu, 5 Feb 2026 11:52:30 +0000 Subject: [PATCH 2/2] chore: Add tests (#3135) --- .../lib/utils/contest_table_provider.test.ts | 87 ++++++++++++++++--- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/src/test/lib/utils/contest_table_provider.test.ts b/src/test/lib/utils/contest_table_provider.test.ts index b84d0778a..9eb7565a6 100644 --- a/src/test/lib/utils/contest_table_provider.test.ts +++ b/src/test/lib/utils/contest_table_provider.test.ts @@ -2937,20 +2937,6 @@ describe('prepareContestProviderPresets', () => { expect(group.getProvider(ContestType.ABC)).toBeInstanceOf(ABC212ToABC318Provider); }); - test('expects to create DPs preset correctly', () => { - const group = prepareContestProviderPresets().dps(); - - expect(group.getGroupName()).toBe('EDPC・TDPC・FPS 24'); - expect(group.getMetadata()).toEqual({ - buttonLabel: 'EDPC・TDPC・FPS 24', - ariaLabel: 'EDPC and TDPC and FPS 24 contests', - }); - expect(group.getSize()).toBe(3); - expect(group.getProvider(ContestType.EDPC)).toBeInstanceOf(EDPCProvider); - expect(group.getProvider(ContestType.TDPC)).toBeInstanceOf(TDPCProvider); - expect(group.getProvider(ContestType.FPS_24)).toBeInstanceOf(FPS24Provider); - }); - test('expects to create Typical90 preset correctly', () => { const group = prepareContestProviderPresets().Typical90(); @@ -2983,14 +2969,87 @@ describe('prepareContestProviderPresets', () => { ); }); + test('expects to create DPs preset correctly', () => { + const group = prepareContestProviderPresets().dps(); + + expect(group.getGroupName()).toBe('EDPC・TDPC・FPS 24'); + expect(group.getMetadata()).toEqual({ + buttonLabel: 'EDPC・TDPC・FPS 24', + ariaLabel: 'EDPC and TDPC and FPS 24 contests', + }); + expect(group.getSize()).toBe(3); + expect(group.getProvider(ContestType.EDPC)).toBeInstanceOf(EDPCProvider); + expect(group.getProvider(ContestType.TDPC)).toBeInstanceOf(TDPCProvider); + expect(group.getProvider(ContestType.FPS_24)).toBeInstanceOf(FPS24Provider); + }); + + test('expects to create ACL preset correctly with 3 providers', () => { + const group = prepareContestProviderPresets().Acl(); + + expect(group.getGroupName()).toBe('AtCoder Library Contests'); + expect(group.getMetadata()).toEqual({ + buttonLabel: 'AtCoder Library (ACL)', + ariaLabel: 'Filter ACL Contests', + }); + expect(group.getSize()).toBe(3); + expect(group.getProvider(ContestType.ACL_PRACTICE)).toBeInstanceOf(ACLPracticeProvider); + expect(group.getProvider(ContestType.ABC_LIKE)).toBeInstanceOf(ACLBeginnerProvider); + expect(group.getProvider(ContestType.ARC_LIKE)).toBeInstanceOf(ACLProvider); + }); + + test('expects to create JOI First Qual Round preset correctly with 1 provider', () => { + const group = prepareContestProviderPresets().JOIFirstQualRound(); + + expect(group.getGroupName()).toBe('JOI 一次予選'); + expect(group.getMetadata()).toEqual({ + buttonLabel: 'JOI 一次予選', + ariaLabel: 'Filter JOI First Qualifying Round', + }); + expect(group.getSize()).toBe(1); + expect(group.getProvider(ContestType.JOI)).toBeInstanceOf(JOIFirstQualRoundProvider); + }); + + test('expects to create JOI Second Qual and Semi-Final Round preset correctly with 3 providers', () => { + const group = prepareContestProviderPresets().JOISecondQualAndSemiFinalRound(); + + expect(group.getGroupName()).toBe('JOI 二次予選・本選'); + expect(group.getMetadata()).toEqual({ + buttonLabel: 'JOI 二次予選・本選', + ariaLabel: 'Filter JOI Second Qual Round', + }); + expect(group.getSize()).toBe(3); + expect(group.getProvider(ContestType.JOI, '2020Onwards')).toBeInstanceOf( + JOISecondQualRound2020OnwardsProvider, + ); + expect(group.getProvider(ContestType.JOI, 'from2006To2019')).toBeInstanceOf( + JOIQualRoundFrom2006To2019Provider, + ); + expect(group.getProvider(ContestType.JOI, 'semiFinal')).toBeInstanceOf( + JOISemiFinalRoundProvider, + ); + }); + test('expects to verify all presets are functions', () => { const presets = prepareContestProviderPresets(); expect(typeof presets.ABS).toBe('function'); expect(typeof presets.ABC319Onwards).toBe('function'); expect(typeof presets.ABC212ToABC318).toBe('function'); + expect(typeof presets.ABC126ToABC211).toBe('function'); + expect(typeof presets.ABC042ToABC125).toBe('function'); + expect(typeof presets.ARC104Onwards).toBe('function'); + expect(typeof presets.ARC058ToARC103).toBe('function'); + expect(typeof presets.AGC001Onwards).toBe('function'); + expect(typeof presets.ABCLike).toBe('function'); + expect(typeof presets.ABC001ToABC041).toBe('function'); + expect(typeof presets.ARC001ToARC057).toBe('function'); expect(typeof presets.Typical90).toBe('function'); + expect(typeof presets.TessokuBook).toBe('function'); + expect(typeof presets.MathAndAlgorithm).toBe('function'); expect(typeof presets.dps).toBe('function'); + expect(typeof presets.Acl).toBe('function'); + expect(typeof presets.JOIFirstQualRound).toBe('function'); + expect(typeof presets.JOISecondQualAndSemiFinalRound).toBe('function'); }); test('expects each preset to create independent instances', () => {