diff --git a/test/integration/gssp-redirect-base-path/test/index.test.js b/test/integration/gssp-redirect-base-path/test/index.test.js index 71c4c0fe0c757..8fb4dbb7c944f 100644 --- a/test/integration/gssp-redirect-base-path/test/index.test.js +++ b/test/integration/gssp-redirect-base-path/test/index.test.js @@ -132,8 +132,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-_gsp-blog_first`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -154,8 +155,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-_gsp-blog_first`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -178,8 +180,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-_`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#index') @@ -194,8 +197,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-_`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#index') @@ -210,8 +214,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-_missing`, - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -231,8 +236,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gsp-blog/redirect-dest-external`, - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -248,8 +254,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gssp-blog/redirect-dest-external`, - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -279,8 +286,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/gssp-blog/redirect-dest-_gssp-blog_first`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gssp') @@ -294,7 +302,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSSP page is navigated to client-side (internal normal)', async () => { - const browser = await webdriver(appPort, `${basePath}`, true, true) + const browser = await webdriver(appPort, `${basePath}`, { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gssp-blog/redirect-dest-_another') @@ -307,7 +317,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSSP page is navigated to client-side (external)', async () => { - const browser = await webdriver(appPort, `${basePath}`, true, true) + const browser = await webdriver(appPort, `${basePath}`, { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gssp-blog/redirect-dest-_gssp-blog_first') @@ -324,7 +336,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSP page is navigated to client-side (internal)', async () => { - const browser = await webdriver(appPort, `${basePath}`, true, true) + const browser = await webdriver(appPort, `${basePath}`, { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gsp-blog/redirect-dest-_another') @@ -337,7 +351,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSP page is navigated to client-side (external)', async () => { - const browser = await webdriver(appPort, `${basePath}`, true, true) + const browser = await webdriver(appPort, `${basePath}`, { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gsp-blog/redirect-dest-_gsp-blog_first') @@ -357,8 +373,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/another?mark_as=root`, - true, - true + { + retryWaitHydration: true, + } ) await browser.eval(`(function () { @@ -384,8 +401,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/another?mark_as=root`, - true, - true + { + retryWaitHydration: true, + } ) await browser.eval(`(function () { @@ -411,8 +429,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/another?mark_as=root`, - true, - true + { + retryWaitHydration: true, + } ) await browser.eval(`(function () { @@ -438,8 +457,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, `${basePath}/another?mark_as=root`, - true, - true + { + retryWaitHydration: true, + } ) await browser.eval(`(function () { diff --git a/test/integration/gssp-redirect/test/index.test.js b/test/integration/gssp-redirect/test/index.test.js index f475740be81cd..009463b63bd81 100644 --- a/test/integration/gssp-redirect/test/index.test.js +++ b/test/integration/gssp-redirect/test/index.test.js @@ -92,8 +92,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog/redirect-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -113,8 +114,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog-blocking/redirect-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -134,8 +136,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog-blocking/redirect-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -155,8 +158,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog-blocking/redirect-revalidate-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -176,8 +180,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog-blocking/redirect-revalidate-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -198,8 +203,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog/redirect-dest-_gsp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gsp') @@ -219,12 +225,9 @@ const runTests = (isDev) => { } it('should apply redirect when fallback GSP page is visited directly (internal normal)', async () => { - const browser = await webdriver( - appPort, - '/gsp-blog/redirect-dest-_', - true, - true - ) + const browser = await webdriver(appPort, '/gsp-blog/redirect-dest-_', { + retryWaitHydration: true, + }) await browser.waitForElementByCss('#index') @@ -235,12 +238,9 @@ const runTests = (isDev) => { if (!isDev) { it('should apply redirect when fallback GSP page is visited directly (internal normal) 2nd visit', async () => { - const browser = await webdriver( - appPort, - '/gsp-blog/redirect-dest-_', - true, - true - ) + const browser = await webdriver(appPort, '/gsp-blog/redirect-dest-_', { + retryWaitHydration: true, + }) await browser.waitForElementByCss('#index') @@ -254,8 +254,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog/redirect-dest-_missing', - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -275,8 +276,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gsp-blog/redirect-dest-external', - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -292,8 +294,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gssp-blog/redirect-dest-external', - true, - true + { + retryWaitHydration: true, + } ) await check( @@ -323,8 +326,9 @@ const runTests = (isDev) => { const browser = await webdriver( appPort, '/gssp-blog/redirect-dest-_gssp-blog_first', - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('#gssp') @@ -338,7 +342,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSSP page is navigated to client-side (internal normal)', async () => { - const browser = await webdriver(appPort, '/', true, true) + const browser = await webdriver(appPort, '/', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gssp-blog/redirect-dest-_another') @@ -351,7 +357,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSSP page is navigated to client-side (external)', async () => { - const browser = await webdriver(appPort, '/', true, true) + const browser = await webdriver(appPort, '/', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gssp-blog/redirect-dest-_gssp-blog_first') @@ -368,7 +376,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSP page is navigated to client-side (internal)', async () => { - const browser = await webdriver(appPort, '/', true, true) + const browser = await webdriver(appPort, '/', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gsp-blog/redirect-dest-_another') @@ -381,7 +391,9 @@ const runTests = (isDev) => { }) it('should apply redirect when GSP page is navigated to client-side (external)', async () => { - const browser = await webdriver(appPort, '/', true, true) + const browser = await webdriver(appPort, '/', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/gsp-blog/redirect-dest-_gsp-blog_first') @@ -398,12 +410,9 @@ const runTests = (isDev) => { }) it('should not replace history of the origin page when GSSP page is navigated to client-side (internal normal)', async () => { - const browser = await webdriver( - appPort, - '/another?mark_as=root', - true, - true - ) + const browser = await webdriver(appPort, '/another?mark_as=root', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/') @@ -425,12 +434,9 @@ const runTests = (isDev) => { }) it('should not replace history of the origin page when GSSP page is navigated to client-side (external)', async () => { - const browser = await webdriver( - appPort, - '/another?mark_as=root', - true, - true - ) + const browser = await webdriver(appPort, '/another?mark_as=root', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/') @@ -452,12 +458,9 @@ const runTests = (isDev) => { }) it('should not replace history of the origin page when GSP page is navigated to client-side (internal)', async () => { - const browser = await webdriver( - appPort, - '/another?mark_as=root', - true, - true - ) + const browser = await webdriver(appPort, '/another?mark_as=root', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/') @@ -479,12 +482,9 @@ const runTests = (isDev) => { }) it('should not replace history of the origin page when GSP page is navigated to client-side (external)', async () => { - const browser = await webdriver( - appPort, - '/another?mark_as=root', - true, - true - ) + const browser = await webdriver(appPort, '/another?mark_as=root', { + retryWaitHydration: true, + }) await browser.eval(`(function () { window.next.router.push('/') diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index 2235946b180fc..623217a118c32 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -2354,12 +2354,9 @@ export function runTests(ctx) { }) it('should render 404 for fallback page that returned 404 on client transition', async () => { - const browser = await webdriver( - ctx.appPort, - `${ctx.basePath}/en`, - true, - true - ) + const browser = await webdriver(ctx.appPort, `${ctx.basePath}/en`, { + retryWaitHydration: true, + }) await browser.eval(`(function() { next.router.push('/not-found/fallback/first') })()`) @@ -2395,8 +2392,9 @@ export function runTests(ctx) { const browser = await webdriver( ctx.appPort, `${ctx.basePath}/en/not-found/fallback/first`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('h1') await browser.eval('window.beforeNav = 1') @@ -2427,12 +2425,9 @@ export function runTests(ctx) { }) it('should render 404 for blocking fallback page that returned 404 on client transition', async () => { - const browser = await webdriver( - ctx.appPort, - `${ctx.basePath}/en`, - true, - true - ) + const browser = await webdriver(ctx.appPort, `${ctx.basePath}/en`, { + retryWaitHydration: true, + }) await browser.eval(`(function() { next.router.push('/not-found/blocking-fallback/first') })()`) @@ -2468,8 +2463,9 @@ export function runTests(ctx) { const browser = await webdriver( ctx.appPort, `${ctx.basePath}/en/not-found/blocking-fallback/first`, - true, - true + { + retryWaitHydration: true, + } ) await browser.waitForElementByCss('h1') await browser.eval('window.beforeNav = 1') diff --git a/test/integration/image-component/react-virtualized/test/index.test.js b/test/integration/image-component/react-virtualized/test/index.test.js index d1913108afb25..6e98101e95e57 100644 --- a/test/integration/image-component/react-virtualized/test/index.test.js +++ b/test/integration/image-component/react-virtualized/test/index.test.js @@ -62,7 +62,9 @@ describe('react-virtualized wrapping next/image', () => { it('should not cancel requests for images', async () => { // TODO: this test doesnt work unless we can set `disableCache: true` - let browser = await webdriver(appPort, '/', undefined, undefined, true) + let browser = await webdriver(appPort, '/', { + disableCache: true, + }) expect(cancelCount).toBe(0) await browser.eval('window.scrollTo({ top: 100, behavior: "smooth" })') await waitFor(100) diff --git a/test/integration/pnpm-support/test/index.test.js b/test/integration/pnpm-support/test/index.test.js index a42d0bd0d74ee..fb50ef49fe7a0 100644 --- a/test/integration/pnpm-support/test/index.test.js +++ b/test/integration/pnpm-support/test/index.test.js @@ -156,11 +156,15 @@ describe('pnpm support', () => { await renderViaHTTP(appPort, '/') - browser = await webdriver(appPort, '/', false) + browser = await webdriver(appPort, '/', { + waitHydration: false, + }) expect(await browser.waitForElementByCss('#world').text()).toBe('World') await browser.close() - browser = await webdriver(appPort, '/about', false) + browser = await webdriver(appPort, '/about', { + waitHydration: false, + }) expect(await browser.waitForElementByCss('#world').text()).toBe('World') await browser.close() } finally { diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index 988c25f5e7679..2cccad780122b 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -30,6 +30,25 @@ export default function (context, { runtime, env }) { expect(homeHTML).toContain('foo.client') }) + it('should reuse the inline flight response without sending extra requests', async () => { + let hasFlightRequest = false + let requestsCount = 0 + await webdriver(context.appPort, '/', { + beforePageLoad(page) { + page.on('request', (request) => { + requestsCount++ + const url = request.url() + if (/__flight__=1/.test(url)) { + hasFlightRequest = true + } + }) + }, + }) + + expect(requestsCount).toBeGreaterThan(0) + expect(hasFlightRequest).toBe(false) + }) + it('should support multi-level server component imports', async () => { const html = await renderViaHTTP(context.appPort, '/multi') expect(html).toContain('bar.server.js:') @@ -127,14 +146,13 @@ export default function (context, { runtime, env }) { const content = 'custom-404-page' const page404HTML = await renderViaHTTP(context.appPort, '/404') const pageUnknownHTML = await renderViaHTTP(context.appPort, '/no.where') + let browser = await webdriver(context.appPort, '/404') + const hydrated404Content = await browser.waitForElementByCss(id).text() + browser = await webdriver(context.appPort, '/no.where') + const hydratedUnknownContent = await browser.waitForElementByCss(id).text() - const page404Browser = await webdriver(context.appPort, '/404') - const pageUnknownBrowser = await webdriver(context.appPort, '/no.where') - - expect(await page404Browser.waitForElementByCss(id).text()).toBe(content) - expect(await pageUnknownBrowser.waitForElementByCss(id).text()).toBe( - content - ) + expect(hydrated404Content).toBe(content) + expect(hydratedUnknownContent).toBe(content) expect(getNodeBySelector(page404HTML, id).text()).toBe(content) expect(getNodeBySelector(pageUnknownHTML, id).text()).toBe(content) diff --git a/test/lib/browsers/base.ts b/test/lib/browsers/base.ts index bc1d9193719fc..0f0065f6039ad 100644 --- a/test/lib/browsers/base.ts +++ b/test/lib/browsers/base.ts @@ -72,7 +72,10 @@ export class BrowserInterface { } on(event: Event, cb: (...args: any[]) => void) {} off(event: Event, cb: (...args: any[]) => void) {} - async loadPage(url: string, { disableCache: boolean }): Promise {} + async loadPage( + url: string, + { disableCache: boolean, beforePageLoad: Function } + ): Promise {} async get(url: string): Promise {} async getValue(): Promise {} diff --git a/test/lib/browsers/playwright.ts b/test/lib/browsers/playwright.ts index 3ca06ce25018a..d9ffafb4ff81d 100644 --- a/test/lib/browsers/playwright.ts +++ b/test/lib/browsers/playwright.ts @@ -64,7 +64,10 @@ class Playwright extends BrowserInterface { return page.goto(url) as any } - async loadPage(url: string, opts?: { disableCache: boolean }) { + async loadPage( + url: string, + opts?: { disableCache: boolean; beforePageLoad?: (...args: any[]) => void } + ) { if (this.activeTrace) { const traceDir = path.join(__dirname, '../../traces') const traceOutputPath = path.join( @@ -136,6 +139,8 @@ class Playwright extends BrowserInterface { }) }) + opts?.beforePageLoad?.(page) + if (tracePlaywright) { await context.tracing.start({ screenshots: true, diff --git a/test/lib/next-webdriver.ts b/test/lib/next-webdriver.ts index a7321fcaf49e7..2627ed17bdc1c 100644 --- a/test/lib/next-webdriver.ts +++ b/test/lib/next-webdriver.ts @@ -40,19 +40,33 @@ if (typeof afterAll === 'function') { * * @param appPort can either be the port or the full URL * @param url the path/query to append when using appPort - * @param waitHydration whether to wait for react hydration to finish - * @param retryWaitHydration allow retrying hydration wait if reload occurs + * @param options.waitHydration whether to wait for react hydration to finish + * @param options.retryWaitHydration allow retrying hydration wait if reload occurs + * @param options.disableCache disable cache for page load + * @param options.beforePageLoad the callback receiving page instance before loading page * @returns thenable browser instance */ export default async function webdriver( appPortOrUrl: string | number, url: string, - waitHydration = true, - retryWaitHydration = false, - disableCache = false + options?: { + waitHydration?: boolean + retryWaitHydration?: boolean + disableCache?: boolean + beforePageLoad?: (page: any) => void + } ): Promise { let CurrentInterface: typeof BrowserInterface + const defaultOptions = { + waitHydration: true, + retryWaitHydration: false, + disableCache: false, + } + options = Object.assign(defaultOptions, options) + const { waitHydration, retryWaitHydration, disableCache, beforePageLoad } = + options + // we import only the needed interface if ( process.env.LEGACY_SAFARI || @@ -81,7 +95,7 @@ export default async function webdriver( console.log(`\n> Loading browser with ${fullUrl}\n`) - await browser.loadPage(fullUrl, { disableCache }) + await browser.loadPage(fullUrl, { disableCache, beforePageLoad }) console.log(`\n> Loaded browser with ${fullUrl}\n`) // Wait for application to hydrate