diff --git a/.changeset/young-hornets-post.md b/.changeset/young-hornets-post.md new file mode 100644 index 000000000000..514db808af45 --- /dev/null +++ b/.changeset/young-hornets-post.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes a bug where the "tap" prefetch strategy worked only on the first clicked link with view transitions enabled diff --git a/packages/astro/e2e/fixtures/prefetch/src/components/ViewTransitionsLayout.astro b/packages/astro/e2e/fixtures/prefetch/src/components/ViewTransitionsLayout.astro new file mode 100644 index 000000000000..ceb6f8cb0023 --- /dev/null +++ b/packages/astro/e2e/fixtures/prefetch/src/components/ViewTransitionsLayout.astro @@ -0,0 +1,14 @@ +--- +import { ClientRouter } from 'astro:transitions'; +--- + + + + Testing + + + + + + + diff --git a/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/1.astro b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/1.astro new file mode 100644 index 000000000000..aed64ab9c1d4 --- /dev/null +++ b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/1.astro @@ -0,0 +1,10 @@ +--- +import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro"; +--- + + +

View Transitions 1

+
+
+2 +
diff --git a/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/2.astro b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/2.astro new file mode 100644 index 000000000000..c9ffcc410cbf --- /dev/null +++ b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/2.astro @@ -0,0 +1,7 @@ +--- +import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro"; +--- + + +

View Transitions 2

+
diff --git a/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/index.astro b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/index.astro new file mode 100644 index 000000000000..8ea5fd567ac1 --- /dev/null +++ b/packages/astro/e2e/fixtures/prefetch/src/pages/view-transitions/index.astro @@ -0,0 +1,8 @@ +--- +import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro"; +--- + + +

View Transitions index

+1 +
diff --git a/packages/astro/e2e/prefetch.test.js b/packages/astro/e2e/prefetch.test.js index 2da44189a305..eb45aa5b8f97 100644 --- a/packages/astro/e2e/prefetch.test.js +++ b/packages/astro/e2e/prefetch.test.js @@ -56,6 +56,24 @@ async function expectUrlNotPrefetched(url, page) { expect(reqUrls).not.toContainEqual(url); } +/** + * @param {import('@playwright/test').Page} page + * @param {string} selector + */ +async function mouseDown(page, selector) { + const box = await page.locator(selector).boundingBox(); + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function waitForPageLoad(page) { + await page.waitForEvent('response'); + await new Promise(res => setTimeout(res, 500)); // wait for transition to finish +} + test.describe('Prefetch (default)', () => { let devServer; @@ -393,3 +411,75 @@ test.describe('Prefetch (default), Experimental ({ clientPrerender: true })', () expect(await scriptIsInHead(page, 'prefetch-load')).toBeTruthy(); }); }); + +test.describe('Prefetch View Transitions', () => { + let devServer; + + test.afterEach(async () => { + await devServer.stop(); + }); + + test('"load" strategy', async ({ page, astro }) => { + devServer = await astro.startDevServer({ + prefetch: { + defaultStrategy: 'load', + }, + }); + await page.goto(astro.resolveUrl('/view-transitions')); + await expectUrlPrefetched('/view-transitions/1', page); + + await Promise.all([waitForPageLoad(page), page.click('a')]); + await expectUrlPrefetched('/view-transitions/2', page); + }); + + test('"viewport" strategy', async ({ page, astro }) => { + devServer = await astro.startDevServer({ + prefetch: { + defaultStrategy: 'viewport', + }, + }); + await page.goto(astro.resolveUrl('/view-transitions')); + await expectUrlPrefetched('/view-transitions/1', page); + + await Promise.all([waitForPageLoad(page), page.click('a')]); + await expectUrlPrefetched('/view-transitions/2', page); + }); + + test('"tap" strategy', async ({ page, astro }) => { + devServer = await astro.startDevServer({ + prefetch: { + defaultStrategy: 'tap', + }, + }); + await page.goto(astro.resolveUrl('/view-transitions')); + + await expectUrlNotPrefetched('/view-transitions/1', page); + await mouseDown(page, 'a'); + await expectUrlPrefetched('/view-transitions/1', page); + + await Promise.all([waitForPageLoad(page), page.mouse.up()]); + + await expectUrlNotPrefetched('/view-transitions/2', page); + await mouseDown(page, 'a') + await expectUrlPrefetched('/view-transitions/2', page); + }); + + test('"hover" strategy', async ({ page, astro }) => { + devServer = await astro.startDevServer({ + prefetch: { + defaultStrategy: 'hover', + }, + }); + await page.goto(astro.resolveUrl('/view-transitions')); + + await expectUrlNotPrefetched('/view-transitions/1', page); + await page.locator('a').hover(); + await expectUrlPrefetched('/view-transitions/1', page); + + await Promise.all([waitForPageLoad(page), page.click('a')]); + + await expectUrlNotPrefetched('/view-transitions/2', page); + await page.locator('a').hover(); + await expectUrlPrefetched('/view-transitions/2', page); + }); +}); diff --git a/packages/astro/src/prefetch/index.ts b/packages/astro/src/prefetch/index.ts index 34d3b5363998..2cfd84940385 100644 --- a/packages/astro/src/prefetch/index.ts +++ b/packages/astro/src/prefetch/index.ts @@ -54,7 +54,7 @@ export function init(defaultOpts?: InitOptions) { */ function initTapStrategy() { for (const event of ['touchstart', 'mousedown']) { - document.body.addEventListener( + document.addEventListener( event, (e) => { if (elMatchesStrategy(e.target, 'tap')) {