@@ -317,6 +317,62 @@ describe('fixed resolver', () => {
317317 path : '/users/posva/admin' ,
318318 } )
319319 } )
320+
321+ it ( 'preserves currentLocation.hash in relative-by-name navigation without to.hash' , ( ) => {
322+ const resolver = createFixedResolver ( [
323+ {
324+ name : 'home' ,
325+ path : EMPTY_PATH_PATTERN_MATCHER ,
326+ hash : ANY_HASH_PATTERN_MATCHER ,
327+ } ,
328+ ] )
329+
330+ const currentLocation = resolver . resolve ( '/#current-hash' )
331+
332+ expect ( resolver . resolve ( { } , currentLocation ) ) . toMatchObject ( {
333+ name : 'home' ,
334+ path : '/' ,
335+ hash : '#current-hash' ,
336+ } )
337+ } )
338+
339+ it ( 'uses currentLocation values when matcher and to values are nullish' , ( ) => {
340+ const resolver = createFixedResolver ( [
341+ {
342+ name : 'page' ,
343+ path : EMPTY_PATH_PATTERN_MATCHER ,
344+ query : [ PAGE_QUERY_PATTERN_MATCHER ] ,
345+ hash : ANY_HASH_PATTERN_MATCHER ,
346+ } ,
347+ ] )
348+
349+ // Create currentLocation using the resolver to ensure it's properly formed
350+ const currentLocation = resolver . resolve ( {
351+ name : 'page' ,
352+ params : { page : 10 , hash : 'current' } ,
353+ query : { existing : 'value' } ,
354+ } )
355+
356+ // Verify currentLocation was created correctly
357+ expect ( currentLocation ) . toMatchObject ( {
358+ name : 'page' ,
359+ path : '/' ,
360+ params : { page : 10 , hash : 'current' } ,
361+ query : { existing : 'value' , page : '10' } , // matcher adds page to query
362+ hash : '#current' , // matcher builds hash from params
363+ fullPath : '/?existing=value&page=10#current' ,
364+ } )
365+
366+ // Now test that relative navigation preserves currentLocation values
367+ expect ( resolver . resolve ( { } , currentLocation ) ) . toMatchObject ( {
368+ name : 'page' ,
369+ path : '/' ,
370+ params : { page : 10 , hash : 'current' } , // from currentLocation
371+ query : { existing : 'value' , page : '10' } , // matcher builds with currentLocation params
372+ hash : '#current' , // matcher builds with currentLocation params
373+ fullPath : '/?existing=value&page=10#current' ,
374+ } )
375+ } )
320376 } )
321377
322378 describe ( 'absolute locations' , ( ) => {
@@ -402,6 +458,200 @@ describe('fixed resolver', () => {
402458 resolver . resolve ( { name : 'nonexistent' , params : { } } )
403459 ) . toThrowError ( 'Record "nonexistent" not found' )
404460 } )
461+
462+ it ( 'resolves named locations with explicit query' , ( ) => {
463+ const resolver = createFixedResolver ( [
464+ {
465+ name : 'home' ,
466+ path : EMPTY_PATH_PATTERN_MATCHER ,
467+ } ,
468+ ] )
469+
470+ expect (
471+ resolver . resolve ( {
472+ name : 'home' ,
473+ params : { } ,
474+ query : { foo : 'bar' , baz : 'qux' } ,
475+ } )
476+ ) . toMatchObject ( {
477+ name : 'home' ,
478+ path : '/' ,
479+ params : { } ,
480+ query : { foo : 'bar' , baz : 'qux' } ,
481+ hash : '' ,
482+ fullPath : '/?foo=bar&baz=qux' ,
483+ } )
484+ } )
485+
486+ it ( 'resolves named locations with explicit hash' , ( ) => {
487+ const resolver = createFixedResolver ( [
488+ {
489+ name : 'home' ,
490+ path : EMPTY_PATH_PATTERN_MATCHER ,
491+ } ,
492+ ] )
493+
494+ expect (
495+ resolver . resolve ( {
496+ name : 'home' ,
497+ params : { } ,
498+ hash : '#section' ,
499+ } )
500+ ) . toMatchObject ( {
501+ name : 'home' ,
502+ path : '/' ,
503+ params : { } ,
504+ query : { } ,
505+ hash : '#section' ,
506+ fullPath : '/#section' ,
507+ } )
508+ } )
509+
510+ it ( 'resolves named locations with both query and hash' , ( ) => {
511+ const resolver = createFixedResolver ( [
512+ {
513+ name : 'home' ,
514+ path : EMPTY_PATH_PATTERN_MATCHER ,
515+ } ,
516+ ] )
517+
518+ expect (
519+ resolver . resolve ( {
520+ name : 'home' ,
521+ params : { } ,
522+ query : { page : '1' } ,
523+ hash : '#top' ,
524+ } )
525+ ) . toMatchObject ( {
526+ name : 'home' ,
527+ path : '/' ,
528+ params : { } ,
529+ query : { page : '1' } ,
530+ hash : '#top' ,
531+ fullPath : '/?page=1#top' ,
532+ } )
533+ } )
534+
535+ it ( 'resolves named locations with params, query, and hash' , ( ) => {
536+ const resolver = createFixedResolver ( [
537+ { name : 'user-edit' , path : USERS_ID_OTHER_PATH_MATCHER } ,
538+ ] )
539+
540+ expect (
541+ resolver . resolve ( {
542+ name : 'user-edit' ,
543+ params : { id : 'posva' , other : 'profile' } ,
544+ query : { tab : 'settings' } ,
545+ hash : '#bio' ,
546+ } )
547+ ) . toMatchObject ( {
548+ name : 'user-edit' ,
549+ path : '/users/posva/profile' ,
550+ params : { id : 'posva' , other : 'profile' } ,
551+ query : { tab : 'settings' } ,
552+ hash : '#bio' ,
553+ fullPath : '/users/posva/profile?tab=settings#bio' ,
554+ } )
555+ } )
556+
557+ it ( 'query matcher params take precedence over to.query' , ( ) => {
558+ const resolver = createFixedResolver ( [
559+ {
560+ name : 'search' ,
561+ path : EMPTY_PATH_PATTERN_MATCHER ,
562+ query : [ PAGE_QUERY_PATTERN_MATCHER ] ,
563+ } ,
564+ ] )
565+
566+ expect (
567+ resolver . resolve ( {
568+ name : 'search' ,
569+ params : { page : 42 } ,
570+ query : { page : '1' , other : 'value' } ,
571+ } )
572+ ) . toMatchObject ( {
573+ name : 'search' ,
574+ path : '/' ,
575+ params : { page : 42 } ,
576+ query : { page : '42' , other : 'value' } , // matcher param overrides to.query
577+ fullPath : '/?page=42&other=value' ,
578+ } )
579+ } )
580+
581+ it ( 'hash matcher params take precedence over to.hash' , ( ) => {
582+ const resolver = createFixedResolver ( [
583+ {
584+ name : 'document' ,
585+ path : EMPTY_PATH_PATTERN_MATCHER ,
586+ hash : ANY_HASH_PATTERN_MATCHER ,
587+ } ,
588+ ] )
589+
590+ expect (
591+ resolver . resolve ( {
592+ name : 'document' ,
593+ params : { hash : 'section1' } ,
594+ hash : '#section2' ,
595+ } )
596+ ) . toMatchObject ( {
597+ name : 'document' ,
598+ path : '/' ,
599+ params : { hash : 'section1' } ,
600+ hash : '#section1' , // matcher param overrides to.hash
601+ fullPath : '/#section1' ,
602+ } )
603+ } )
604+
605+ it ( 'preserves empty string hash from matcher over to.hash' , ( ) => {
606+ const resolver = createFixedResolver ( [
607+ {
608+ name : 'document' ,
609+ path : EMPTY_PATH_PATTERN_MATCHER ,
610+ hash : ANY_HASH_PATTERN_MATCHER ,
611+ } ,
612+ ] )
613+
614+ expect (
615+ resolver . resolve ( {
616+ name : 'document' ,
617+ params : { hash : '' } ,
618+ hash : '#fallback' ,
619+ } )
620+ ) . toMatchObject ( {
621+ name : 'document' ,
622+ path : '/' ,
623+ params : { hash : '' } ,
624+ hash : '' , // empty string from matcher is preserved
625+ fullPath : '/' ,
626+ } )
627+ } )
628+
629+ it ( 'combines query and hash matchers correctly' , ( ) => {
630+ const resolver = createFixedResolver ( [
631+ {
632+ name : 'page' ,
633+ path : EMPTY_PATH_PATTERN_MATCHER ,
634+ query : [ PAGE_QUERY_PATTERN_MATCHER ] ,
635+ hash : ANY_HASH_PATTERN_MATCHER ,
636+ } ,
637+ ] )
638+
639+ expect (
640+ resolver . resolve ( {
641+ name : 'page' ,
642+ params : { page : 5 , hash : 'top' } ,
643+ query : { page : '1' , sort : 'name' } ,
644+ hash : '#bottom' ,
645+ } )
646+ ) . toMatchObject ( {
647+ name : 'page' ,
648+ path : '/' ,
649+ params : { page : 5 , hash : 'top' } ,
650+ query : { page : '5' , sort : 'name' } , // matcher overrides, regular query preserved
651+ hash : '#top' , // matcher overrides to.hash
652+ fullPath : '/?page=5&sort=name#top' ,
653+ } )
654+ } )
405655 } )
406656
407657 describe ( 'encoding' , ( ) => {
0 commit comments