@@ -624,6 +624,136 @@ describe('PackageVersions', () => {
624624 expect ( text . includes ( '1.1.0' ) || component . findAll ( 'button' ) . length > 2 ) . toBe ( true )
625625 } )
626626 } )
627+
628+ it ( 'shows DateTime for major group versions' , async ( ) => {
629+ mockFetchAllPackageVersions . mockResolvedValue ( [
630+ { version : '2.0.0' , time : '2024-01-15T12:00:00.000Z' , hasProvenance : false } ,
631+ { version : '1.1.0' , time : '2024-01-10T12:00:00.000Z' , hasProvenance : false } ,
632+ ] )
633+
634+ const component = await mountSuspended ( PackageVersions , {
635+ props : {
636+ packageName : 'test-package' ,
637+ versions : {
638+ '2.0.0' : createVersion ( '2.0.0' ) ,
639+ } ,
640+ distTags : { latest : '2.0.0' } ,
641+ time : { '2.0.0' : '2024-01-15T12:00:00.000Z' } ,
642+ } ,
643+ } )
644+
645+ // Expand "Other versions"
646+ const otherVersionsButton = component
647+ . findAll ( 'button' )
648+ . find ( btn => btn . text ( ) . includes ( 'Other versions' ) )
649+
650+ await otherVersionsButton ! . trigger ( 'click' )
651+
652+ await vi . waitFor ( ( ) => {
653+ // Should have DateTime components for both the main version and other versions
654+ const dateTimeComponents = component . findAllComponents ( { name : 'DateTime' } )
655+ expect ( dateTimeComponents . length ) . toBeGreaterThan ( 1 )
656+ } )
657+ } )
658+
659+ it ( 'shows ProvenanceBadge for major group versions with provenance' , async ( ) => {
660+ mockFetchAllPackageVersions . mockResolvedValue ( [
661+ { version : '2.0.0' , time : '2024-01-15T12:00:00.000Z' , hasProvenance : true } ,
662+ { version : '1.1.0' , time : '2024-01-10T12:00:00.000Z' , hasProvenance : true } ,
663+ ] )
664+
665+ const component = await mountSuspended ( PackageVersions , {
666+ props : {
667+ packageName : 'test-package' ,
668+ versions : {
669+ '2.0.0' : createVersion ( '2.0.0' , { hasProvenance : true } ) ,
670+ } ,
671+ distTags : { latest : '2.0.0' } ,
672+ time : { '2.0.0' : '2024-01-15T12:00:00.000Z' } ,
673+ } ,
674+ } )
675+
676+ // Expand "Other versions"
677+ const otherVersionsButton = component
678+ . findAll ( 'button' )
679+ . find ( btn => btn . text ( ) . includes ( 'Other versions' ) )
680+
681+ await otherVersionsButton ! . trigger ( 'click' )
682+
683+ await vi . waitFor ( ( ) => {
684+ // Should have ProvenanceBadge components for versions with provenance
685+ const provenanceBadges = component . findAllComponents ( { name : 'ProvenanceBadge' } )
686+ expect ( provenanceBadges . length ) . toBeGreaterThan ( 1 )
687+ } )
688+ } )
689+
690+ it ( 'renders major group header as clickable link' , async ( ) => {
691+ mockFetchAllPackageVersions . mockResolvedValue ( [
692+ { version : '2.0.0' , time : '2024-01-15T12:00:00.000Z' , hasProvenance : false } ,
693+ { version : '1.1.0' , time : '2024-01-10T12:00:00.000Z' , hasProvenance : false } ,
694+ { version : '1.0.0' , time : '2024-01-05T12:00:00.000Z' , hasProvenance : false } ,
695+ ] )
696+
697+ const component = await mountSuspended ( PackageVersions , {
698+ props : {
699+ packageName : 'test-package' ,
700+ versions : {
701+ '2.0.0' : createVersion ( '2.0.0' ) ,
702+ } ,
703+ distTags : { latest : '2.0.0' } ,
704+ time : { '2.0.0' : '2024-01-15T12:00:00.000Z' } ,
705+ } ,
706+ } )
707+
708+ // Expand "Other versions"
709+ const otherVersionsButton = component
710+ . findAll ( 'button' )
711+ . find ( btn => btn . text ( ) . includes ( 'Other versions' ) )
712+
713+ await otherVersionsButton ! . trigger ( 'click' )
714+
715+ await vi . waitFor ( ( ) => {
716+ // Find the major group header - should be a link (NuxtLink renders as <a>)
717+ const links = component . findAll ( 'a' )
718+ const majorGroupLink = links . find ( l => l . text ( ) === '1.1.0' )
719+ expect ( majorGroupLink ?. exists ( ) ) . toBe ( true )
720+ } )
721+ } )
722+
723+ it ( 'shows DateTime and ProvenanceBadge for single version in major group' , async ( ) => {
724+ mockFetchAllPackageVersions . mockResolvedValue ( [
725+ { version : '2.0.0' , time : '2024-01-15T12:00:00.000Z' , hasProvenance : false } ,
726+ { version : '1.0.0' , time : '2024-01-05T12:00:00.000Z' , hasProvenance : true } ,
727+ ] )
728+
729+ const component = await mountSuspended ( PackageVersions , {
730+ props : {
731+ packageName : 'test-package' ,
732+ versions : {
733+ '2.0.0' : createVersion ( '2.0.0' ) ,
734+ } ,
735+ distTags : { latest : '2.0.0' } ,
736+ time : { '2.0.0' : '2024-01-15T12:00:00.000Z' } ,
737+ } ,
738+ } )
739+
740+ // Expand "Other versions"
741+ const otherVersionsButton = component
742+ . findAll ( 'button' )
743+ . find ( btn => btn . text ( ) . includes ( 'Other versions' ) )
744+
745+ await otherVersionsButton ! . trigger ( 'click' )
746+
747+ await vi . waitFor ( ( ) => {
748+ // Single version group (1.0.0) should still have DateTime
749+ const dateTimeComponents = component . findAllComponents ( { name : 'DateTime' } )
750+ expect ( dateTimeComponents . length ) . toBeGreaterThan ( 1 )
751+
752+ // And ProvenanceBadge for the version with provenance
753+ const provenanceBadges = component . findAllComponents ( { name : 'ProvenanceBadge' } )
754+ expect ( provenanceBadges . length ) . toBeGreaterThan ( 0 )
755+ } )
756+ } )
627757 } )
628758
629759 describe ( 'loading states' , ( ) => {
@@ -746,6 +876,61 @@ describe('PackageVersions', () => {
746876 expect ( expandButton . exists ( ) ) . toBe ( true )
747877 expect ( expandButton . attributes ( 'aria-label' ) ) . toMatch ( / E x p a n d | C o l l a p s e / )
748878 } )
879+
880+ it ( 'other versions button has aria-label' , async ( ) => {
881+ const component = await mountSuspended ( PackageVersions , {
882+ props : {
883+ packageName : 'test-package' ,
884+ versions : {
885+ '1.0.0' : createVersion ( '1.0.0' ) ,
886+ } ,
887+ distTags : { latest : '1.0.0' } ,
888+ time : { '1.0.0' : '2024-01-15T12:00:00.000Z' } ,
889+ } ,
890+ } )
891+
892+ const otherVersionsButton = component
893+ . findAll ( 'button' )
894+ . find ( btn => btn . text ( ) . includes ( 'Other versions' ) )
895+
896+ expect ( otherVersionsButton ?. attributes ( 'aria-label' ) ) . toMatch ( / E x p a n d o t h e r v e r s i o n s / )
897+ } )
898+
899+ it ( 'expand buttons have visible focus states' , async ( ) => {
900+ const component = await mountSuspended ( PackageVersions , {
901+ props : {
902+ packageName : 'test-package' ,
903+ versions : {
904+ '1.0.0' : createVersion ( '1.0.0' ) ,
905+ } ,
906+ distTags : { latest : '1.0.0' } ,
907+ time : { '1.0.0' : '2024-01-15T12:00:00.000Z' } ,
908+ } ,
909+ } )
910+
911+ const expandButton = component . find ( 'button[aria-expanded]' )
912+ expect ( expandButton . classes ( ) . some ( c => c . includes ( 'focus-visible' ) ) ) . toBe ( true )
913+ } )
914+
915+ it ( 'icons have aria-hidden attribute' , async ( ) => {
916+ const component = await mountSuspended ( PackageVersions , {
917+ props : {
918+ packageName : 'test-package' ,
919+ versions : {
920+ '1.0.0' : createVersion ( '1.0.0' ) ,
921+ } ,
922+ distTags : { latest : '1.0.0' } ,
923+ time : { '1.0.0' : '2024-01-15T12:00:00.000Z' } ,
924+ } ,
925+ } )
926+
927+ // Find chevron icons inside buttons
928+ const chevronIcons = component . findAll ( 'button span.i-carbon-chevron-right' )
929+ expect ( chevronIcons . length ) . toBeGreaterThan ( 0 )
930+ for ( const icon of chevronIcons ) {
931+ expect ( icon . attributes ( 'aria-hidden' ) ) . toBe ( 'true' )
932+ }
933+ } )
749934 } )
750935
751936 describe ( 'error handling' , ( ) => {
0 commit comments