Skip to content
5 changes: 5 additions & 0 deletions .changeset/young-hornets-post.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
import { ClientRouter } from 'astro:transitions';
---

<html>
<head>
<title>Testing</title>
<ClientRouter />
</head>
<body>
<slot />
</body>
</html>

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro";
---

<ViewTransitionsLayout>
<h1>View Transitions 1</h1>
<br>
<br>
<a href="/view-transitions/2">2</a>
</ViewTransitionsLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro";
---

<ViewTransitionsLayout>
<h1>View Transitions 2</h1>
</ViewTransitionsLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
import ViewTransitionsLayout from "../../components/ViewTransitionsLayout.astro";
---

<ViewTransitionsLayout>
<h1>View Transitions index</h1>
<a href="/view-transitions/1">1</a>
</ViewTransitionsLayout>
90 changes: 90 additions & 0 deletions packages/astro/e2e/prefetch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
});
});
2 changes: 1 addition & 1 deletion packages/astro/src/prefetch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down
Loading