@@ -2,6 +2,8 @@ import { describe, expect, it } from 'vitest'
22import {
33 buildTaggedVersionRows ,
44 buildVersionToTagsMap ,
5+ compareTagRows ,
6+ compareVersionGroupKeys ,
57 filterExcludedTags ,
68 filterVersions ,
79 getPrereleaseChannel ,
@@ -423,6 +425,105 @@ describe('isSameVersionGroup', () => {
423425 } )
424426} )
425427
428+ describe ( 'compareTagRows' , ( ) => {
429+ function row ( version : string , tags : string [ ] ) {
430+ return { id : `version:${ version } ` , primaryTag : tags [ 0 ] ! , tags, version }
431+ }
432+
433+ it ( 'sorts by tag priority ascending (rc before beta)' , ( ) => {
434+ const rc = row ( '2.0.0-rc.1' , [ 'rc' ] )
435+ const beta = row ( '2.0.0-beta.1' , [ 'beta' ] )
436+ expect ( compareTagRows ( rc , beta , { } ) ) . toBeLessThan ( 0 )
437+ expect ( compareTagRows ( beta , rc , { } ) ) . toBeGreaterThan ( 0 )
438+ } )
439+
440+ it ( 'sorts by tag priority ascending (beta before alpha)' , ( ) => {
441+ const beta = row ( '2.0.0-beta.1' , [ 'beta' ] )
442+ const alpha = row ( '2.0.0-alpha.1' , [ 'alpha' ] )
443+ expect ( compareTagRows ( beta , alpha , { } ) ) . toBeLessThan ( 0 )
444+ } )
445+
446+ it ( 'falls back to publish date descending when priorities are equal' , ( ) => {
447+ const newer = row ( '1.1.0' , [ 'legacy' ] )
448+ const older = row ( '1.0.0' , [ 'legacy' ] )
449+ const times = { '1.1.0' : '2024-06-01T00:00:00.000Z' , '1.0.0' : '2024-01-01T00:00:00.000Z' }
450+ expect ( compareTagRows ( newer , older , times ) ) . toBeLessThan ( 0 )
451+ expect ( compareTagRows ( older , newer , times ) ) . toBeGreaterThan ( 0 )
452+ } )
453+
454+ it ( 'returns 0 for equal priority and equal publish time' , ( ) => {
455+ const a = row ( '1.0.0' , [ 'legacy' ] )
456+ const b = row ( '1.0.1' , [ 'legacy' ] )
457+ const times = { '1.0.0' : '2024-01-01T00:00:00.000Z' , '1.0.1' : '2024-01-01T00:00:00.000Z' }
458+ expect ( compareTagRows ( a , b , times ) ) . toBe ( 0 )
459+ } )
460+
461+ it ( 'uses minimum tag priority for multi-tag rows' , ( ) => {
462+ // Row with ['rc', 'next'] has min priority of rc (2)
463+ // Row with ['beta'] has priority 3 — so rc-row should sort first
464+ const rcAndNext = row ( '3.0.0-rc.1' , [ 'rc' , 'next' ] )
465+ const beta = row ( '3.0.0-beta.1' , [ 'beta' ] )
466+ expect ( compareTagRows ( rcAndNext , beta , { } ) ) . toBeLessThan ( 0 )
467+ } )
468+
469+ it ( 'sorts unknown tags after known priority tags' , ( ) => {
470+ const known = row ( '2.0.0-alpha.1' , [ 'alpha' ] )
471+ const unknown = row ( '2.0.0-custom.1' , [ 'custom-tag' ] )
472+ expect ( compareTagRows ( known , unknown , { } ) ) . toBeLessThan ( 0 )
473+ } )
474+
475+ it ( 'sorts unknown tags by publish date descending' , ( ) => {
476+ const newer = row ( '2.0.0' , [ 'v2-custom' ] )
477+ const older = row ( '1.0.0' , [ 'v1-custom' ] )
478+ const times = { '2.0.0' : '2025-01-01T00:00:00.000Z' , '1.0.0' : '2024-01-01T00:00:00.000Z' }
479+ expect ( compareTagRows ( newer , older , times ) ) . toBeLessThan ( 0 )
480+ } )
481+
482+ it ( 'treats missing publish time as empty string (sorts last among same-priority rows)' , ( ) => {
483+ const withTime = row ( '1.1.0' , [ 'legacy' ] )
484+ const withoutTime = row ( '1.0.0' , [ 'legacy' ] )
485+ const times = { '1.1.0' : '2024-06-01T00:00:00.000Z' }
486+ expect ( compareTagRows ( withTime , withoutTime , times ) ) . toBeLessThan ( 0 )
487+ } )
488+ } )
489+
490+ describe ( 'compareVersionGroupKeys' , ( ) => {
491+ it ( 'sorts higher major before lower major' , ( ) => {
492+ expect ( compareVersionGroupKeys ( '2' , '1' ) ) . toBeLessThan ( 0 )
493+ expect ( compareVersionGroupKeys ( '1' , '2' ) ) . toBeGreaterThan ( 0 )
494+ } )
495+
496+ it ( 'returns 0 for equal keys' , ( ) => {
497+ expect ( compareVersionGroupKeys ( '3' , '3' ) ) . toBe ( 0 )
498+ expect ( compareVersionGroupKeys ( '0.9' , '0.9' ) ) . toBe ( 0 )
499+ } )
500+
501+ it ( 'sorts higher minor before lower minor for 0.x groups' , ( ) => {
502+ expect ( compareVersionGroupKeys ( '0.10' , '0.9' ) ) . toBeLessThan ( 0 )
503+ expect ( compareVersionGroupKeys ( '0.9' , '0.10' ) ) . toBeGreaterThan ( 0 )
504+ } )
505+
506+ it ( 'sorts non-0.x keys (no minor) before 0.x keys with same major' , ( ) => {
507+ // major-only key "0" has no minor (undefined → -1), so "0.1" sorts before "0"
508+ expect ( compareVersionGroupKeys ( '0.1' , '0' ) ) . toBeLessThan ( 0 )
509+ } )
510+
511+ it ( 'sorts major-version groups in descending order when used with Array.sort' , ( ) => {
512+ const keys = [ '1' , '3' , '2' , '10' ]
513+ expect ( keys . sort ( compareVersionGroupKeys ) ) . toEqual ( [ '10' , '3' , '2' , '1' ] )
514+ } )
515+
516+ it ( 'sorts 0.x groups in descending minor order when used with Array.sort' , ( ) => {
517+ const keys = [ '0.1' , '0.10' , '0.9' , '0.2' ]
518+ expect ( keys . sort ( compareVersionGroupKeys ) ) . toEqual ( [ '0.10' , '0.9' , '0.2' , '0.1' ] )
519+ } )
520+
521+ it ( 'interleaves major and 0.x groups correctly' , ( ) => {
522+ const keys = [ '0.9' , '1' , '0.10' , '2' ]
523+ expect ( keys . sort ( compareVersionGroupKeys ) ) . toEqual ( [ '2' , '1' , '0.10' , '0.9' ] )
524+ } )
525+ } )
526+
426527describe ( 'filterVersions' , ( ) => {
427528 const versions = [ '1.0.0' , '1.1.0' , '1.5.3' , '2.0.0' , '2.1.0' , '3.0.0-beta.1' ]
428529
0 commit comments