0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-17 22:44:24 -05:00

Makes view transition e2e tests compatible with Firefox (#11093)

This commit is contained in:
Martin Trapp 2024-05-21 12:50:32 +02:00 committed by GitHub
parent bfe9c73536
commit a514bbe992
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 120 deletions

View file

@ -6,7 +6,7 @@ import { ViewTransitions, fade } from 'astro:transitions';
<ViewTransitions /> <ViewTransitions />
</head> </head>
<body> <body>
<h1 transition:name="h1" transition:animate={fade({duration:10000})}>Abort</h1> <h1 transition:name="h1" transition:animate={fade({duration:500})}>Abort</h1>
</body> </body>
</html> </html>
@ -15,11 +15,10 @@ import { ViewTransitions, fade } from 'astro:transitions';
import {navigate } from 'astro:transitions/client'; import {navigate } from 'astro:transitions/client';
setTimeout(()=>{ setTimeout(()=>{
[...document.getAnimations()].forEach((a) => a.addEventListener('cancel', [...document.getAnimations()].forEach((a) => a.addEventListener('cancel', (e) => console.log("[test]", e.type, a.animationName)));
(e) => console.log("[test]",e.type, a.animationName))); console.log("[test] navigate to /one");
console.log("[test] navigate to /one")
navigate("/one"); navigate("/one");
}, 1000); }, 200);
console.log('[test] navigate to "."') console.log('[test] navigate to "."')
navigate("/abort2"); navigate("/abort2");
</script> </script>

View file

@ -13,6 +13,14 @@ test.afterAll(async () => {
await devServer.stop(); await devServer.stop();
}); });
function collectLoads(page) {
const loads = [];
page.on('load', async () => {
const url = page.url();
if (url !== 'about:blank') loads.push(await page.title());
});
return loads;
}
function scrollToBottom(page) { function scrollToBottom(page) {
return page.evaluate(() => { return page.evaluate(() => {
window.scrollY = document.documentElement.scrollHeight; window.scrollY = document.documentElement.scrollHeight;
@ -34,12 +42,13 @@ function collectPreloads(page) {
}); });
} }
async function nativeViewTransition(page) {
return page.evaluate(() => document.startViewTransition !== undefined)
}
test.describe('View Transitions', () => { test.describe('View Transitions', () => {
test('Moving from page 1 to page 2', async ({ page, astro }) => { test('Moving from page 1 to page 2', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -55,10 +64,7 @@ test.describe('View Transitions', () => {
}); });
test('Back button is captured', async ({ page, astro }) => { test('Back button is captured', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -79,11 +85,7 @@ test.describe('View Transitions', () => {
}); });
test('Clicking on a link with nested content', async ({ page, astro }) => { test('Clicking on a link with nested content', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 4 // Go to page 4
await page.goto(astro.resolveUrl('/four')); await page.goto(astro.resolveUrl('/four'));
let p = page.locator('#four'); let p = page.locator('#four');
@ -98,11 +100,7 @@ test.describe('View Transitions', () => {
}); });
test('Clicking on a link to a page with non-recommended headers', async ({ page, astro }) => { test('Clicking on a link to a page with non-recommended headers', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 4 // Go to page 4
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
let p = page.locator('#one'); let p = page.locator('#one');
@ -120,10 +118,7 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -145,10 +140,7 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
let p = page.locator('#one'); let p = page.locator('#one');
@ -176,10 +168,7 @@ test.describe('View Transitions', () => {
}); });
test('Moving from a page without ViewTransitions w/ back button', async ({ page, astro }) => { test('Moving from a page without ViewTransitions w/ back button', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -250,7 +239,7 @@ test.describe('View Transitions', () => {
}); });
test('No page rendering during swap()', async ({ page, astro }) => { test('No page rendering during swap()', async ({ page, astro }) => {
// This has been a problem with theme switchers (e.g. for drakmode) // This has been a problem with theme switchers (e.g. for darkmode)
// Swap() should not trigger any page renders and give users the chance to // Swap() should not trigger any page renders and give users the chance to
// correct attributes in the astro:after-swap handler before they become visible // correct attributes in the astro:after-swap handler before they become visible
@ -258,6 +247,10 @@ test.describe('View Transitions', () => {
// The test succeeds if no additional animation beside those of the // The test succeeds if no additional animation beside those of the
// view transition is triggered during swap() // view transition is triggered during swap()
// Only works for browsers with native view transitions
if (!await nativeViewTransition(page)) return;
await page.goto(astro.resolveUrl('/listener-one')); await page.goto(astro.resolveUrl('/listener-one'));
let p = page.locator('#totwo'); let p = page.locator('#totwo');
await expect(p, 'should have content').toHaveText('Go to listener two'); await expect(p, 'should have content').toHaveText('Go to listener two');
@ -295,10 +288,8 @@ test.describe('View Transitions', () => {
}); });
test('click self link (w/o hash) does not do navigation', async ({ page, astro }) => { test('click self link (w/o hash) does not do navigation', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
const p = page.locator('#one'); const p = page.locator('#one');
@ -596,10 +587,8 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/query')); await page.goto(astro.resolveUrl('/query'));
@ -618,10 +607,8 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
// Go to the half bakeed page // Go to the half bakeed page
await page.goto(astro.resolveUrl('/half-baked')); await page.goto(astro.resolveUrl('/half-baked'));
let p = page.locator('#half-baked'); let p = page.locator('#half-baked');
@ -669,10 +656,8 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 4 // Go to page 4
await page.goto(astro.resolveUrl('/four')); await page.goto(astro.resolveUrl('/four'));
@ -684,6 +669,9 @@ test.describe('View Transitions', () => {
p = page.locator('#two'); p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2'); await expect(p, 'should have content').toHaveText('Page 2');
// go to next page
await page.click('#click-longpage');
expect(loads.length, 'There should be 2 page load').toEqual(2); expect(loads.length, 'There should be 2 page load').toEqual(2);
}); });
@ -703,10 +691,8 @@ test.describe('View Transitions', () => {
}); });
test('data-astro-reload not required for non-html content', async ({ page, astro }) => { test('data-astro-reload not required for non-html content', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 4 // Go to page 4
await page.goto(astro.resolveUrl('/four')); await page.goto(astro.resolveUrl('/four'));
let p = page.locator('#four'); let p = page.locator('#four');
@ -765,10 +751,8 @@ test.describe('View Transitions', () => {
}); });
test('Moving to a page which redirects to another', async ({ page, astro }) => { test('Moving to a page which redirects to another', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -792,10 +776,8 @@ test.describe('View Transitions', () => {
}); });
test('Redirect to external site causes page load', async ({ page, astro }) => { test('Redirect to external site causes page load', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -805,11 +787,9 @@ test.describe('View Transitions', () => {
// go to external page // go to external page
await page.click('#click-redirect-external'); await page.click('#click-redirect-external');
// doesn't work for playwright when we are too fast // doesn't work for playwright when we are too fast
p = page.locator('h1');
await expect(p, 'should have content').toBeVisible();
await page.waitForURL('http://example.com'); await page.waitForURL('http://example.com');
await page.waitForFunction((arr) => arr.length === 2, loads); await expect(page.locator("h1"), 'should have content').toHaveText('Example Domain');
expect(loads.length, 'There should be 2 page loads').toEqual(2); expect(loads.length, 'There should be 2 page loads').toEqual(2);
}); });
@ -1019,10 +999,8 @@ test.describe('View Transitions', () => {
}); });
test('form POST that redirects to another page is handled', async ({ page, astro }) => { test('form POST that redirects to another page is handled', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
await page.goto(astro.resolveUrl('/form-one')); await page.goto(astro.resolveUrl('/form-one'));
@ -1048,10 +1026,8 @@ test.describe('View Transitions', () => {
}); });
test('form GET that redirects to another page is handled', async ({ page, astro }) => { test('form GET that redirects to another page is handled', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
await page.goto(astro.resolveUrl('/form-one?method=get')); await page.goto(astro.resolveUrl('/form-one?method=get'));
@ -1070,10 +1046,8 @@ test.describe('View Transitions', () => {
}); });
test('form POST when there is an error shows the error', async ({ page, astro }) => { test('form POST when there is an error shows the error', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
await page.goto(astro.resolveUrl('/form-one?throw')); await page.goto(astro.resolveUrl('/form-one?throw'));
@ -1095,11 +1069,8 @@ test.describe('View Transitions', () => {
page, page,
astro, astro,
}) => { }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
const postedEncodings = []; const postedEncodings = [];
@ -1130,11 +1101,8 @@ test.describe('View Transitions', () => {
}); });
test('form POST respects enctype attribute', async ({ page, astro }) => { test('form POST respects enctype attribute', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
const postedEncodings = []; const postedEncodings = [];
@ -1222,10 +1190,8 @@ test.describe('View Transitions', () => {
}); });
test('form POST with no action handler', async ({ page, astro }) => { test('form POST with no action handler', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', async (p) => {
loads.push(p);
});
await page.goto(astro.resolveUrl('/form-two')); await page.goto(astro.resolveUrl('/form-two'));
@ -1256,10 +1222,8 @@ test.describe('View Transitions', () => {
}); });
test('click on an svg anchor should trigger navigation', async ({ page, astro }) => { test('click on an svg anchor should trigger navigation', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor')); await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#insidesvga'); let locator = page.locator('#insidesvga');
@ -1271,10 +1235,8 @@ test.describe('View Transitions', () => {
}); });
test('click inside an svg anchor should trigger navigation', async ({ page, astro }) => { test('click inside an svg anchor should trigger navigation', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor')); await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#insidesvga'); let locator = page.locator('#insidesvga');
await expect(locator, 'should have content').toHaveText('text within a svga'); await expect(locator, 'should have content').toHaveText('text within a svga');
@ -1285,10 +1247,8 @@ test.describe('View Transitions', () => {
}); });
test('click on an area in an image map should trigger navigation', async ({ page, astro }) => { test('click on an area in an image map should trigger navigation', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor')); await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#area'); let locator = page.locator('#area');
await expect(locator, 'should have attribute').toHaveAttribute('shape', 'default'); await expect(locator, 'should have attribute').toHaveAttribute('shape', 'default');
@ -1328,10 +1288,8 @@ test.describe('View Transitions', () => {
}); });
test('view transition should also work with 404 page', async ({ page, astro }) => { test('view transition should also work with 404 page', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
// Go to page 1 // Go to page 1
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
@ -1347,10 +1305,8 @@ test.describe('View Transitions', () => {
}); });
test('custom elements can trigger a view transition', async ({ page, astro }) => { test('custom elements can trigger a view transition', async ({ page, astro }) => {
const loads = []; const loads = collectLoads(page);
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/one')); await page.goto(astro.resolveUrl('/one'));
await expect(page.locator('#one'), 'should have content').toHaveText('Page 1'); await expect(page.locator('#one'), 'should have content').toHaveText('Page 1');
// go to page 2 // go to page 2
@ -1361,6 +1317,9 @@ test.describe('View Transitions', () => {
}); });
test('transition:name should be escaped correctly', async ({ page, astro }) => { test('transition:name should be escaped correctly', async ({ page, astro }) => {
// view-transition-name errors on browser w/o native support
if (!await nativeViewTransition(page)) return;
const expectedAnimations = new Set(); const expectedAnimations = new Set();
const checkName = async (selector, name) => { const checkName = async (selector, name) => {
expectedAnimations.add(name); expectedAnimations.add(name);
@ -1518,7 +1477,6 @@ test.describe('View Transitions', () => {
// clicks on slow loading page two // clicks on slow loading page two
// after short delay clicks on fast loading page one // after short delay clicks on fast loading page one
// even after some delay /two should not show up // even after some delay /two should not show up
await new Promise((resolve) => setTimeout(resolve, 2000)); // wait is part of the test
let p = page.locator('#one'); let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1'); await expect(p, 'should have content').toHaveText('Page 1');
}); });
@ -1533,16 +1491,19 @@ test.describe('View Transitions', () => {
// Navigate to self with a 10 second animation // Navigate to self with a 10 second animation
// shortly after starting that, change your mind an navigate to /one // shortly after starting that, change your mind an navigate to /one
// check that animations got canceled // check that animations got canceled
await new Promise((resolve) => setTimeout(resolve, 1000)); // wait is part of the test
let p = page.locator('#one'); let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1'); await expect(p, 'should have content').toHaveText('Page 1');
// This test would be more important for a browser without native view transitions // This test would be more important for a browser without native view transitions
// as those do not have automatic cancelation of transitions. // as those do not have automatic cancelation of transitions.
// For simulated view transitions, the last line would be missing as enter and exit animations // For simulated view transitions, the last line would be missing
// don't run in parallel. // as enter and exit animations don't run in parallel.
expect(lines.join('\n')).toBe(
'[test] navigate to "."\n[test] navigate to /one\n[test] cancel astroFadeOut\n[test] cancel astroFadeIn' let expected = '[test] navigate to "."\n[test] navigate to /one\n[test] cancel astroFadeOut';
); const native = await nativeViewTransition(page);
if (native) {
expected += '\n[test] cancel astroFadeIn';
}
await page.click('#click-two');
expect(lines.join('\n')).toBe(expected);
}); });
}); });

View file

@ -5,7 +5,7 @@ process.stdout.isTTY = false;
const config = { const config = {
// TODO: add more tests like view transitions and audits, and fix them. Some of them are failing. // TODO: add more tests like view transitions and audits, and fix them. Some of them are failing.
testMatch: ['e2e/css.test.js', 'e2e/prefetch.test.js'], testMatch: ['e2e/css.test.js', 'e2e/prefetch.test.js', 'e2e/view-transitions.test.js'],
/* Maximum time one test can run for. */ /* Maximum time one test can run for. */
timeout: 40 * 1000, timeout: 40 * 1000,
expect: { expect: {