@@ -42,6 +42,18 @@ function collectPreloads(page) {
4242 } ) ;
4343}
4444
45+ function collectHashChanges ( page ) {
46+ return page . evaluate ( ( ) => {
47+ window . hashchanges = [ ] ;
48+ window . addEventListener ( 'hashchange' , ( e ) => {
49+ window . hashchanges . push ( {
50+ oldURL : e . oldURL ,
51+ newURL : e . newURL ,
52+ } ) ;
53+ } ) ;
54+ } ) ;
55+ }
56+
4557async function nativeViewTransition ( page ) {
4658 return page . evaluate ( ( ) => document . startViewTransition !== undefined ) ;
4759}
@@ -1714,4 +1726,52 @@ test.describe('View Transitions', () => {
17141726 await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
17151727 expect ( lines . join ( '' ) ) . toBe ( '' ) ;
17161728 } ) ;
1729+
1730+ test ( 'hashchange event fires for same-page hash navigation' , async ( { page, astro } ) => {
1731+ await page . goto ( astro . resolveUrl ( '/long-page' ) ) ;
1732+ await collectHashChanges ( page ) ;
1733+
1734+ // navigate to hash on same page
1735+ await page . click ( '#click-scroll-down' ) ;
1736+ await page . waitForTimeout ( 100 ) ;
1737+
1738+ // check hashchange event was fired
1739+ const events = await page . evaluate ( ( ) => window . hashchanges ) ;
1740+ expect ( events . length ) . toBe ( 1 ) ;
1741+ expect ( events [ 0 ] . oldURL ) . toContain ( '/long-page' ) ;
1742+ expect ( events [ 0 ] . newURL ) . toContain ( '/long-page#click-one-again' ) ;
1743+ expect ( events [ 0 ] . oldURL ) . not . toContain ( '#' ) ;
1744+ } ) ;
1745+
1746+ test ( 'hashchange event fires for back/forward with hash' , async ( { page, astro } ) => {
1747+ await page . goto ( astro . resolveUrl ( '/long-page' ) ) ;
1748+ await collectHashChanges ( page ) ;
1749+
1750+ // navigate to hash
1751+ await page . click ( '#click-scroll-down' ) ;
1752+ await page . waitForTimeout ( 100 ) ;
1753+
1754+ await page . goBack ( ) ;
1755+ await page . waitForTimeout ( 100 ) ;
1756+
1757+ // should have two hashchange events
1758+ const events = await page . evaluate ( ( ) => window . hashchanges ) ;
1759+ expect ( events . length ) . toBe ( 2 ) ;
1760+ expect ( events [ 1 ] . oldURL ) . toContain ( '#click-one-again' ) ;
1761+ expect ( events [ 1 ] . newURL ) . toContain ( '/long-page' ) ;
1762+ expect ( events [ 1 ] . newURL ) . not . toContain ( '#' ) ;
1763+ } ) ;
1764+
1765+ test ( 'hashchange event does NOT fire for cross-page navigation' , async ( { page, astro } ) => {
1766+ await page . goto ( astro . resolveUrl ( '/one' ) ) ;
1767+ await collectHashChanges ( page ) ;
1768+
1769+ // navigate to different page
1770+ await page . click ( '#click-two' ) ;
1771+ await page . waitForTimeout ( 100 ) ;
1772+
1773+ // should have zero hashchange events
1774+ const events = await page . evaluate ( ( ) => window . hashchanges ) ;
1775+ expect ( events . length ) . toBe ( 0 ) ;
1776+ } ) ;
17171777} ) ;
0 commit comments