@@ -436,4 +436,112 @@ test.describe('Host mode', () => {
436436 await page . goto ( routedURL ) ;
437437 await expect ( page . locator ( '[data-test-white-paper]' ) ) . toBeVisible ( ) ;
438438 } ) ;
439+
440+ test ( 'routing rule for `/` resolves when realm root is visited without a trailing slash' , async ( {
441+ page,
442+ } ) => {
443+ // The realm publishes at e.g. `https://published.localhost:4205/<user>/<realm>/`
444+ // (with trailing slash). When a visitor types the URL without the
445+ // trailing slash, the server-rendered HTML is correct (the SSR
446+ // path-in-realm computation handles the missing slash), but the
447+ // Ember SPA's catch-all `/*path` strips the trailing slash from
448+ // the URL on the client side. Without canonicalization the
449+ // injected map key `/<user>/<realm>/` for the `/` rule wouldn't
450+ // match the client's `params.path === '<user>/<realm>'`, and
451+ // hydration would replace the SSR'd card with the bare-shell
452+ // fallback. This test pins the canonicalized comparator.
453+ await login ( page , username , password ) ;
454+ await page . goto ( realmURL ) ;
455+ await page . locator ( '[data-test-stack-item-content]' ) . first ( ) . waitFor ( ) ;
456+
457+ await postCardSource (
458+ page ,
459+ realmURL ,
460+ 'realm.json' ,
461+ JSON . stringify ( {
462+ data : {
463+ type : 'card' ,
464+ attributes : {
465+ cardInfo : { name : `Routed Realm ${ randomUUID ( ) } ` } ,
466+ hostRoutingRules : [ { path : '/' } ] ,
467+ } ,
468+ relationships : {
469+ 'hostRoutingRules.0.instance' : {
470+ links : { self : './white-paper' } ,
471+ } ,
472+ } ,
473+ meta : {
474+ adoptsFrom : {
475+ module : 'https://cardstack.com/base/realm-config' ,
476+ name : 'RealmConfig' ,
477+ } ,
478+ } ,
479+ } ,
480+ } ) ,
481+ ) ;
482+
483+ await page . evaluate (
484+ async ( { realmURL, publishedRealmURL } ) => {
485+ let sessions = JSON . parse (
486+ window . localStorage . getItem ( 'boxel-session' ) ?? '{}' ,
487+ ) ;
488+ let token = sessions [ realmURL ] ;
489+ if ( ! token ) {
490+ throw new Error ( `No session token found for ${ realmURL } ` ) ;
491+ }
492+ let response = await fetch ( 'https://localhost:4205/_publish-realm' , {
493+ method : 'POST' ,
494+ headers : {
495+ Accept : 'application/json' ,
496+ 'Content-Type' : 'application/json' ,
497+ Authorization : token ,
498+ } ,
499+ body : JSON . stringify ( {
500+ sourceRealmURL : realmURL ,
501+ publishedRealmURL,
502+ } ) ,
503+ } ) ;
504+ if ( ! response . ok ) {
505+ throw new Error ( await response . text ( ) ) ;
506+ }
507+ } ,
508+ { realmURL, publishedRealmURL } ,
509+ ) ;
510+
511+ await logout ( page ) ;
512+
513+ // Wait until the SSR HTML at the canonical (trailing-slash) URL
514+ // contains the routed card's marker, then navigate to the
515+ // NO-TRAILING-SLASH variant and assert the marker stays visible
516+ // through hydration. The no-slash navigation is what the
517+ // canonicalization fix targets.
518+ await waitUntil ( async ( ) => {
519+ let response = await page . request . get ( publishedRealmURL , {
520+ headers : { Accept : 'text/html' } ,
521+ } ) ;
522+ if ( ! response . ok ( ) ) {
523+ return false ;
524+ }
525+ let text = await response . text ( ) ;
526+ return text . includes ( 'data-test-white-paper' ) ;
527+ } ) ;
528+
529+ let noSlashURL = publishedRealmURL . replace ( / \/ $ / , '' ) ;
530+ await page . goto ( noSlashURL ) ;
531+ // `[data-test-host-mode-card="<id>"]` is set by the host SPA's
532+ // CardRenderer — that attribute exists ONLY post-hydration (it's
533+ // not in the SSR'd isolated_html). Pinning it to the rule's target
534+ // id means:
535+ // (a) `toBeVisible` implicitly waits for hydration to finish,
536+ // so it can't pass on the brief SSR flash before the SPA
537+ // replaces it; and
538+ // (b) if the resolveRoutedPath miss makes the SPA fall back to
539+ // the realm index card, the attribute value is `…/index`
540+ // (or similar) and this assertion fails with a clear diff
541+ // instead of silently catching the SSR'd marker.
542+ let expectedRoutedCardId = `${ publishedRealmURL } white-paper` ;
543+ await expect (
544+ page . locator ( `[data-test-host-mode-card="${ expectedRoutedCardId } "]` ) ,
545+ ) . toBeVisible ( ) ;
546+ } ) ;
439547} ) ;
0 commit comments