@@ -22,24 +22,196 @@ jest.mock('react-tiny-popover', () => ({
2222} ) ) ;
2323
2424jest . mock ( 'react-i18next' , ( ) => ( {
25- Trans : ( { i18nKey } : { i18nKey : string } ) => i18nKey ,
25+ Trans : ( { i18nKey, values } : { i18nKey : string ; values ?: Record < string , unknown > } ) => {
26+ if ( i18nKey === 'common.wca.advancement.ranking' ) {
27+ return `Top ${ values ?. level } to next round` ;
28+ }
29+
30+ if ( i18nKey === 'common.wca.advancement.percent' ) {
31+ return `Top ${ values ?. level } % to next round` ;
32+ }
33+
34+ if ( i18nKey === 'common.wca.advancement.linkedRanking' ) {
35+ return `Top ${ values ?. level } combined across ${ values ?. rounds } advance to next round` ;
36+ }
37+
38+ if ( i18nKey === 'common.wca.advancement.linkedPercent' ) {
39+ return `Top ${ values ?. level } % combined across ${ values ?. rounds } advance to next round` ;
40+ }
41+
42+ if ( i18nKey === 'common.wca.cumulativeTimelimit' ) {
43+ return `Time Limit: ${ values ?. time } Cumulative` ;
44+ }
45+
46+ if ( i18nKey === 'common.wca.cumulativeTimelimitWithrounds' ) {
47+ return `Time Limit: ${ values ?. time } Total with:` ;
48+ }
49+
50+ return i18nKey ;
51+ } ,
2652 useTranslation : ( ) => ( {
27- t : ( key : string ) => key ,
53+ t : ( key : string , options ?: Record < string , unknown > ) => {
54+ if ( key === 'common.help' ) {
55+ return 'help' ;
56+ }
57+
58+ if ( key === 'common.wca.cutoff' ) {
59+ return 'Cutoff' ;
60+ }
61+
62+ if ( key === 'common.wca.timeLimit' ) {
63+ return 'Time Limit' ;
64+ }
65+
66+ if ( key === 'common.activityCodeToName.round' ) {
67+ return `Round ${ options ?. roundNumber } ` ;
68+ }
69+
70+ if ( key === 'common.wca.advancement.ranking' ) {
71+ return `Top ${ options ?. level } advance to ${ options ?. what } ` ;
72+ }
73+
74+ if ( key === 'common.wca.advancement.percent' ) {
75+ return `Top ${ options ?. level } % advance to ${ options ?. what } ` ;
76+ }
77+
78+ if ( key === 'common.wca.advancement.linkedRanking' ) {
79+ return `Top ${ options ?. level } in dual rounds ${ options ?. rounds } advance to ${ options ?. what } ` ;
80+ }
81+
82+ if ( key === 'common.wca.advancement.linkedPercent' ) {
83+ return `Top ${ options ?. level } % in dual rounds ${ options ?. rounds } advance to ${ options ?. what } ` ;
84+ }
85+
86+ if ( key === 'common.wca.advancement.nextRound' ) {
87+ return 'next round' ;
88+ }
89+
90+ if ( key === 'common.wca.advancement.final' ) {
91+ return 'final' ;
92+ }
93+
94+ if ( key === 'common.wca.advancement.resultThresholdUnknown' ) {
95+ return 'an unknown result' ;
96+ }
97+
98+ if ( options ?. defaultValue ) {
99+ return String ( options . defaultValue )
100+ . replace ( '{{level}}' , String ( options . level ?? '' ) )
101+ . replace ( '{{rounds}}' , String ( options . rounds ?? '' ) )
102+ . replace ( '{{round}}' , String ( options . round ?? '' ) )
103+ . replace ( '{{what}}' , String ( options . what ?? '' ) )
104+ . replace ( '{{scope}}' , String ( options . scope ?? '' ) )
105+ . replace ( '{{result}}' , String ( options . result ?? '' ) ) ;
106+ }
107+
108+ return key ;
109+ } ,
28110 } ) ,
29111} ) ) ;
30112
113+ const wcifMock = {
114+ id : 'TestComp2026' ,
115+ schedule : { venues : [ ] } ,
116+ events : [
117+ {
118+ id : '333' ,
119+ rounds : [
120+ {
121+ id : '333-r1' ,
122+ format : 'a' ,
123+ cutoff : null ,
124+ timeLimit : null ,
125+ advancementCondition : {
126+ type : 'ranking' ,
127+ level : 16 ,
128+ } ,
129+ results : [ ] ,
130+ } ,
131+ {
132+ id : '333-r2' ,
133+ format : 'a' ,
134+ cutoff : null ,
135+ timeLimit : null ,
136+ participationRuleset : {
137+ participationSource : {
138+ type : 'round' ,
139+ roundId : '333-r1' ,
140+ resultCondition : {
141+ type : 'percent' ,
142+ value : 75 ,
143+ } ,
144+ } ,
145+ } ,
146+ results : [ ] ,
147+ } ,
148+ {
149+ id : '333-r3' ,
150+ format : 'a' ,
151+ cutoff : null ,
152+ timeLimit : null ,
153+ participationRuleset : {
154+ participationSource : {
155+ type : 'linkedRounds' ,
156+ roundIds : [ '333-r1' , '333-r2' ] ,
157+ resultCondition : {
158+ type : 'ranking' ,
159+ value : 12 ,
160+ } ,
161+ } ,
162+ } ,
163+ results : [ ] ,
164+ } ,
165+ ] ,
166+ } ,
167+ {
168+ id : '222' ,
169+ rounds : [
170+ {
171+ id : '222-r1' ,
172+ format : 'a' ,
173+ cutoff : null ,
174+ timeLimit : null ,
175+ results : [ ] ,
176+ } ,
177+ {
178+ id : '222-r2' ,
179+ format : 'a' ,
180+ cutoff : null ,
181+ timeLimit : null ,
182+ results : [ ] ,
183+ } ,
184+ {
185+ id : '222-r3' ,
186+ format : 'a' ,
187+ cutoff : null ,
188+ timeLimit : null ,
189+ participationRuleset : {
190+ participationSource : {
191+ type : 'linkedRounds' ,
192+ roundIds : [ '222-r1' , '222-r2' ] ,
193+ resultCondition : {
194+ type : 'ranking' ,
195+ value : 8 ,
196+ } ,
197+ } ,
198+ } ,
199+ results : [ ] ,
200+ } ,
201+ ] ,
202+ } ,
203+ ] ,
204+ } ;
205+
31206jest . mock ( '@/providers/WCIFProvider' , ( ) => ( {
32207 useWCIF : ( ) => ( {
33208 competitionId : 'TestComp2026' ,
34- wcif : {
35- id : 'TestComp2026' ,
36- schedule : { venues : [ ] } ,
37- } ,
209+ wcif : wcifMock ,
38210 setTitle : ( ) => { } ,
39211 } ) ,
40212} ) ) ;
41213
42- const round = {
214+ const cutoffOnlyRound = {
43215 id : '333-r1' ,
44216 cutoff : {
45217 numberOfAttempts : 2 ,
@@ -49,7 +221,7 @@ const round = {
49221 advancementCondition : null ,
50222} as unknown as Round ;
51223
52- function renderPanel ( ) {
224+ function renderPanel ( round : Round ) {
53225 return render (
54226 < MemoryRouter >
55227 < CutoffTimeLimitPanel round = { round } />
@@ -59,7 +231,7 @@ function renderPanel() {
59231
60232describe ( 'CutoffTimeLimitPanel' , ( ) => {
61233 it ( 'uses theme-aware popover classes for help content' , ( ) => {
62- renderPanel ( ) ;
234+ renderPanel ( cutoffOnlyRound ) ;
63235
64236 fireEvent . click ( screen . getByRole ( 'button' , { name : / h e l p / i } ) ) ;
65237
@@ -71,4 +243,31 @@ describe('CutoffTimeLimitPanel', () => {
71243 expect ( popoverContent ) . toHaveClass ( 'text-default' ) ;
72244 expect ( popoverContent ) . not . toHaveClass ( 'bg-white' ) ;
73245 } ) ;
246+
247+ it ( 'shows the legacy advancement text for stable wcif rounds' , ( ) => {
248+ renderPanel ( wcifMock . events [ 0 ] . rounds [ 0 ] as unknown as Round ) ;
249+
250+ expect ( screen . getByText ( 'Top 16 advance to next round' ) ) . toBeInTheDocument ( ) ;
251+ } ) ;
252+
253+ it ( 'shows advancement text derived from the next round participation ruleset' , ( ) => {
254+ renderPanel ( {
255+ ...( wcifMock . events [ 0 ] . rounds [ 0 ] as object ) ,
256+ advancementCondition : null ,
257+ } as Round ) ;
258+
259+ expect ( screen . getByText ( 'Top 75% advance to next round' ) ) . toBeInTheDocument ( ) ;
260+ } ) ;
261+
262+ it ( 'shows linked-round advancement text when a later round depends on combined results' , ( ) => {
263+ renderPanel ( wcifMock . events [ 0 ] . rounds [ 1 ] as unknown as Round ) ;
264+
265+ expect ( screen . getByText ( 'Top 12 in dual rounds 1 & 2 advance to final' ) ) . toBeInTheDocument ( ) ;
266+ } ) ;
267+
268+ it ( 'shows the same dual-round advancement text for the first round in a linked-round set' , ( ) => {
269+ renderPanel ( wcifMock . events [ 1 ] . rounds [ 0 ] as unknown as Round ) ;
270+
271+ expect ( screen . getByText ( 'Top 8 in dual rounds 1 & 2 advance to final' ) ) . toBeInTheDocument ( ) ;
272+ } ) ;
74273} ) ;
0 commit comments