@@ -101,8 +101,8 @@ describe('infographic layout plan', () => {
101101 const ids = plan . primitives . map ( primitive => primitive . id )
102102 expect ( ids . filter ( id => id . startsWith ( 'buff-' ) && id . endsWith ( '-start' ) ) ) . toHaveLength ( 1 )
103103 expect ( ids . filter ( id => id . endsWith ( '-continue-before' ) ) ) . toHaveLength ( 2 )
104- expect ( ids . filter ( id => id . startsWith ( 'buff-' ) && id . endsWith ( '-arrow' ) ) ) . toHaveLength ( 2 )
105- expect ( ids . filter ( id => id . startsWith ( 'buff-' ) && id . endsWith ( '-end' ) ) ) . toHaveLength ( 1 )
104+ expect ( ids . filter ( id => id . startsWith ( 'buff-' ) && id . endsWith ( '-arrow' ) ) ) . toHaveLength ( 3 )
105+ expect ( ids . filter ( id => id . startsWith ( 'buff-' ) && id . endsWith ( '-end' ) ) ) . toHaveLength ( 0 )
106106 expect ( plan . textBlocks . filter ( block => block . role === 'buff' ) ) . toHaveLength ( 3 )
107107 expect ( auditRenderPlan ( plan ) ) . toEqual ( [ ] )
108108 } )
@@ -219,6 +219,60 @@ describe('infographic layout plan', () => {
219219 expect ( count ! . bounds . y ) . toBeGreaterThan ( name ! . bounds . y + name ! . bounds . height )
220220 } )
221221
222+ it . each ( [ 1 , 2 ] ) ( 'anchors counts below upward-growing name lanes (%s rows)' , rowCount => {
223+ const plan = layoutInfographic ( { ...baseInput , rowCount, rotation : [
224+ action ( 'g1' , 'gcd' , { name : 'Rising Raptor' , recastTime : 0 } ) ,
225+ action ( 'g2' , 'gcd' , { name : 'Shadow of the Destroyer' , recastTime : 0 } ) ,
226+ action ( 'g3' , 'gcd' , { name : 'Six-sided Star' , recastTime : 0 } ) ,
227+ action ( 'g4' , 'gcd' , { name : '' , recastTime : 0 } ) ,
228+ ] } , measurer )
229+ const icons = actionIcons ( plan )
230+ const counts = plan . textBlocks . filter ( block => block . role === 'count' )
231+ expect ( counts . map ( block => block . lines [ 0 ] . text ) ) . toEqual ( [ '1' , '2' , '3' , '4' ] )
232+ for ( const y of Array . from ( new Set ( icons . map ( icon => icon . bounds . y ) ) ) ) {
233+ const owners = icons . filter ( icon => icon . bounds . y === y ) . map ( icon => icon . ownerId )
234+ const rowCounts = counts . filter ( count => owners . includes ( count . ownerId ) )
235+ expect ( new Set ( rowCounts . map ( count => count . lines [ 0 ] . y ) ) . size ) . toBe ( 1 )
236+ const names = plan . textBlocks . filter ( block => block . role === 'action' && owners . includes ( block . ownerId ) )
237+ expect ( names . every ( name => name . bounds . y >= y + icons [ 0 ] . bounds . height ) ) . toBe ( true )
238+ expect ( names . every ( name => name . bounds . y + name . bounds . height < rowCounts [ 0 ] . bounds . y ) ) . toBe ( true )
239+ }
240+ expect ( auditRenderPlan ( plan ) ) . toEqual ( [ ] )
241+ } )
242+
243+ it . each ( [ 1 , 2 ] ) ( 'distinguishes expiry from continuation and includes short tails in bounds (%s rows)' , rowCount => {
244+ const make = ( duration : number , applicationDelay = 0 ) => layoutInfographic ( { ...baseInput , rowCount,
245+ rotation : [ action ( 'g1' ) , action ( 'g2' , 'gcd' , { statusesApplied : [ {
246+ id : 'buff' , name : 'A continuing status with a long label' , imageSrc : '/favicon.ico' ,
247+ color : '#abcdef' , duration, applicationDelay,
248+ } ] } ) ] ,
249+ } , measurer )
250+ for ( const duration of [ 1 , 2.5 ] ) {
251+ const plan = make ( duration )
252+ expect ( plan . primitives . some ( item => item . id . endsWith ( '-end' ) ) ) . toBe ( true )
253+ expect ( plan . primitives . some ( item => item . id . endsWith ( '-arrow' ) ) ) . toBe ( false )
254+ }
255+ for ( const delay of [ 0 , 2.5 ] ) {
256+ const plan = make ( 20 , delay )
257+ expect ( plan . primitives . some ( item => item . id . endsWith ( '-arrow' ) ) ) . toBe ( true )
258+ expect ( plan . primitives . some ( item => item . id . endsWith ( '-end' ) ) ) . toBe ( false )
259+ expect ( plan . textBlocks . filter ( block => block . role === 'buff' ) ) . toHaveLength ( 1 )
260+ expect ( auditRenderPlan ( plan ) ) . toEqual ( [ ] )
261+ }
262+ expect ( make ( 20 , 3 ) . primitives . some ( item => item . ownerId ?. startsWith ( 'buff-' ) ) ) . toBe ( false )
263+ expect ( make ( 0 ) . primitives . some ( item => item . ownerId ?. startsWith ( 'buff-' ) ) ) . toBe ( false )
264+ } )
265+
266+ it ( 'grows the canvas for a late continuation without stretching the timeline' , ( ) => {
267+ const rotation = Array . from ( { length : 10 } , ( _ , i ) => action ( String ( i ) ) )
268+ const original = layoutInfographic ( { ...baseInput , rotation } , measurer )
269+ rotation [ 9 ] . statusesApplied = [ { id : 'tail' , name : 'A long continuing buff at the end of the rotation' , imageSrc : '/favicon.ico' , color : '#abcdef' , duration : 20 , applicationDelay : 2 } ]
270+ const plan = layoutInfographic ( { ...baseInput , rotation } , measurer )
271+ expect ( plan . width ) . toBeGreaterThan ( original . width )
272+ expect ( actionIcons ( plan ) . map ( icon => icon . bounds . x ) ) . toEqual ( actionIcons ( original ) . map ( icon => icon . bounds . x ) )
273+ expect ( auditRenderPlan ( plan ) ) . toEqual ( [ ] )
274+ } )
275+
222276 it ( 'adds outward lanes and connectors for consecutive oGCD labels' , ( ) => {
223277 const plan = layoutInfographic ( {
224278 ...baseInput ,
0 commit comments