diff --git a/.changeset/warm-windows-occur.md b/.changeset/warm-windows-occur.md new file mode 100644 index 000000000..47c797f2f --- /dev/null +++ b/.changeset/warm-windows-occur.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +let view transitions handle same origin redirects diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 6ae8f3592..77d56e652 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -65,9 +65,19 @@ const { fallback = 'animate' } = Astro.props as Props; }; async function getHTML(href: string) { - const res = await fetch(href); + let res; + try { + res = await fetch(href); + } catch (err) { + return { ok: false }; + } const html = await res.text(); - return { ok: res.ok, html }; + return { + ok: res.ok, + html, + redirected: res.redirected ? res.url : undefined, + contentType: res.headers.get('content-type'), + }; } function getFallback(): Fallback { @@ -280,13 +290,16 @@ const { fallback = 'animate' } = Astro.props as Props; async function navigate(dir: Direction, loc: URL, state?: State) { let finished: Promise; const href = loc.href; - const { html, ok } = await getHTML(href); + const { html, ok, contentType, redirected } = await getHTML(href); + // if there was a redirection, show the final URL in the browser's address bar + redirected && (loc = new URL(redirected)); // If there is a problem fetching the new page, just do an MPA navigation to it. - if (!ok) { + if (!ok || contentType !== 'text/html') { location.href = href; return; } - const doc = parser.parseFromString(html, 'text/html'); + + const doc = parser.parseFromString(html, contentType); if (!doc.querySelector('[name="astro-view-transitions-enabled"]')) { location.href = href; return; diff --git a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs index 02dc2043d..68fdc8e2e 100644 --- a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs +++ b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs @@ -7,6 +7,10 @@ export default defineConfig({ output: 'server', adapter: nodejs({ mode: 'standalone' }), integrations: [react()], + redirects: { + '/redirect-two': '/two', + '/redirect-external': 'http://example.com/', + }, vite: { build: { assetsInlineLimit: 0, diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/one.astro index 3f9666c1d..cc57e76d8 100644 --- a/packages/astro/e2e/fixtures/view-transitions/src/pages/one.astro +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/one.astro @@ -8,6 +8,8 @@ import Layout from '../components/Layout.astro'; go to 3 go to long page go to top + go to redirect 2 + go to a redirect external
test content
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 6421b5fc3..f1c1c4ef8 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -566,7 +566,55 @@ test.describe('View Transitions', () => { await expect(p, 'should have content').toHaveText('Page 1'); }); - test('client:only styles are retained on transition', async ({ page, astro }) => { + test('Moving to a page which redirects to another', async ({ page, astro }) => { + const loads = []; + page.addListener('load', (p) => { + loads.push(p.title()); + }); + + // Go to page 1 + await page.goto(astro.resolveUrl('/one')); + let p = page.locator('#one'); + await expect(p, 'should have content').toHaveText('Page 1'); + + // go to page 2 + await page.click('#click-redirect-two'); + p = page.locator('#two'); + await expect(p, 'should have content').toHaveText('Page 2'); + + // go back + await page.goBack(); + p = page.locator('#one'); + await expect(p, 'should have content').toHaveText('Page 1'); + + expect( + loads.length, + 'There should only be the initial page load and two normal transitions' + ).toEqual(1); + }); + + test('Redirect to external site causes page load', async ({ page, astro }) => { + const loads = []; + page.addListener('load', (p) => { + loads.push(p.title()); + }); + + // Go to page 1 + await page.goto(astro.resolveUrl('/one')); + let p = page.locator('#one'); + await expect(p, 'should have content').toHaveText('Page 1'); + + // go to external page + await page.click('#click-redirect-external'); + // doesn't work for playwright when we are too fast + await page.waitForTimeout(1000); + p = page.locator('h1'); + await expect(p, 'should have content').toBeVisible(); + + expect(loads.length, 'There should be 2 page loads').toEqual(2); + }); + + test('client:only styles are retained on transition', async ({ page, astro }) => { const totalExpectedStyles = 8; // Go to page 1