diff --git a/test/e2e/app-dir/logging/fetch-logging.test.ts b/test/e2e/app-dir/logging/fetch-logging.test.ts index 6f7d93383e64a2..05b8a6d54faf4d 100644 --- a/test/e2e/app-dir/logging/fetch-logging.test.ts +++ b/test/e2e/app-dir/logging/fetch-logging.test.ts @@ -220,12 +220,13 @@ describe('app-dir - logging', () => { await next.patchFile( 'app/default-cache/page.js', (content) => content.replace('Default Cache', 'Hello!'), - async () => { - headline = await browser.waitForElementByCss('h1').text() - expect(headline).toBe('Hello!') - const logs = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(logs).not.toInclude(` │ GET `) - } + async () => + retry(async () => { + headline = await browser.waitForElementByCss('h1').text() + expect(headline).toBe('Hello!') + const logs = stripAnsi(next.cliOutput.slice(outputIndex)) + expect(logs).not.toInclude(` │ GET `) + }) ) }) } diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index 16d45940f8bfdf..f8bcb81787474c 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -1008,9 +1008,8 @@ describe('Prerender', () => { if ((global as any).isNextDev) { it('should not show warning from url prop being returned', async () => { - const urlPropPage = 'pages/url-prop.js' await next.patchFile( - urlPropPage, + 'pages/url-prop.js', ` export async function getStaticProps() { return { @@ -1021,15 +1020,16 @@ describe('Prerender', () => { } export default ({ url }) =>

url: {url}

- ` - ) - - const html = await renderViaHTTP(next.url, '/url-prop') - await next.deleteFile(urlPropPage) - expect(next.cliOutput).not.toMatch( - /The prop `url` is a reserved prop in Next.js for legacy reasons and will be overridden on page \/url-prop/ + `, + async () => + retry(async () => { + const html = await renderViaHTTP(next.url, '/url-prop') + expect(next.cliOutput).not.toMatch( + /The prop `url` is a reserved prop in Next.js for legacy reasons and will be overridden on page \/url-prop/ + ) + expect(html).toMatch(/url:.*?something/) + }) ) - expect(html).toMatch(/url:.*?something/) }) it('should always show fallback for page not in getStaticPaths', async () => { @@ -1083,33 +1083,30 @@ describe('Prerender', () => { }) it('should log error in console and browser in development mode', async () => { - const indexPage = 'pages/index.js' - const origContent = await next.readFile(indexPage) - const browser = await webdriver(next.url, '/') expect(await browser.elementByCss('p').text()).toMatch(/hello.*?world/) await next.patchFile( - indexPage, - origContent - .replace('// throw new', 'throw new') - .replace('{/* */}', '') + 'pages/index.js', + (content) => + content + .replace('// throw new', 'throw new') + .replace('{/* */}', ''), + async () => { + await browser.waitForElementByCss('#after-change') + // we need to reload the page to trigger getStaticProps + await browser.refresh() + + return retry(async () => { + await assertHasRedbox(browser) + const errOverlayContent = await getRedboxHeader(browser) + const errorMsg = /oops from getStaticProps/ + expect(next.cliOutput).toMatch(errorMsg) + expect(errOverlayContent).toMatch(errorMsg) + }) + } ) - - try { - await browser.waitForElementByCss('#after-change') - // we need to reload the page to trigger getStaticProps - await browser.refresh() - - await assertHasRedbox(browser) - const errOverlayContent = await getRedboxHeader(browser) - const errorMsg = /oops from getStaticProps/ - expect(next.cliOutput).toMatch(errorMsg) - expect(errOverlayContent).toMatch(errorMsg) - } finally { - await next.patchFile(indexPage, origContent) - } }) it('should always call getStaticProps without caching in dev', async () => { @@ -1136,25 +1133,20 @@ describe('Prerender', () => { }) it('should error on bad object from getStaticProps', async () => { - const indexPage = 'pages/index.js' - const origContent = await next.readFile(indexPage) await next.patchFile( - indexPage, - origContent.replace(/\/\/ bad-prop/, 'another: true,') + 'pages/index.js', + (content) => content.replace(/\/\/ bad-prop/, 'another: true,'), + async () => + retry(async () => { + const html = await renderViaHTTP(next.url, '/') + expect(html).toMatch(/Additional keys were returned/) + }) ) - await waitFor(1000) - try { - const html = await renderViaHTTP(next.url, '/') - expect(html).toMatch(/Additional keys were returned/) - } finally { - await next.patchFile(indexPage, origContent) - } }) it('should error on dynamic page without getStaticPaths', async () => { - const curPage = 'pages/temp/[slug].js' await next.patchFile( - curPage, + 'pages/temp/[slug].js', ` export async function getStaticProps() { return { @@ -1164,23 +1156,20 @@ describe('Prerender', () => { } } export default () => 'oops' - ` + `, + async () => + retry(async () => { + const html = await renderViaHTTP(next.url, '/temp/hello') + expect(html).toMatch( + /getStaticPaths is required for dynamic SSG pages and is missing for/ + ) + }) ) - await waitFor(1000) - try { - const html = await renderViaHTTP(next.url, '/temp/hello') - expect(html).toMatch( - /getStaticPaths is required for dynamic SSG pages and is missing for/ - ) - } finally { - await next.deleteFile(curPage) - } }) it('should error on dynamic page without getStaticPaths returning fallback property', async () => { - const curPage = 'pages/temp2/[slug].js' await next.patchFile( - curPage, + 'pages/temp2/[slug].js', ` export async function getStaticPaths() { return { @@ -1195,15 +1184,13 @@ describe('Prerender', () => { } } export default () => 'oops' - ` + `, + async () => + retry(async () => { + const html = await renderViaHTTP(next.url, '/temp2/hello') + expect(html).toMatch(/`fallback` key must be returned from/) + }) ) - await waitFor(1000) - try { - const html = await renderViaHTTP(next.url, '/temp2/hello') - expect(html).toMatch(/`fallback` key must be returned from/) - } finally { - await next.deleteFile(curPage) - } }) it('should not re-call getStaticProps when updating query', async () => { @@ -2199,26 +2186,23 @@ describe('Prerender', () => { if (!isDev && !isDeploy) { it('should automatically reset cache TTL when an error occurs and build cache was available', async () => { - await next.patchFile('error.txt', 'yes') - await waitFor(2000) + await next.patchFile('error.txt', 'yes', async () => { + await waitFor(2000) - for (let i = 0; i < 5; i++) { - const res = await fetchViaHTTP( - next.url, - '/blocking-fallback/test-errors-1' - ) - expect(res.status).toBe(200) - } - await next.deleteFile('error.txt') - await check( - () => - next.cliOutput.match( + for (let i = 0; i < 5; i++) { + const res = await fetchViaHTTP( + next.url, + '/blocking-fallback/test-errors-1' + ) + expect(res.status).toBe(200) + } + + return retry(async () => { + expect(next.cliOutput).toMatch( /throwing error for \/blocking-fallback\/test-errors-1/ - ).length === 1 - ? 'success' - : next.cliOutput, - 'success' - ) + ) + }) + }) }) it('should automatically reset cache TTL when an error occurs and runtime cache was available', async () => { @@ -2229,26 +2213,22 @@ describe('Prerender', () => { expect(res.status).toBe(200) await waitFor(2000) - await next.patchFile('error.txt', 'yes') - for (let i = 0; i < 5; i++) { - const res = await fetchViaHTTP( - next.url, - '/blocking-fallback/test-errors-2' - ) - expect(res.status).toBe(200) - } - await next.deleteFile('error.txt') + await next.patchFile('error.txt', 'yes', async () => { + for (let i = 0; i < 5; i++) { + const res = await fetchViaHTTP( + next.url, + '/blocking-fallback/test-errors-2' + ) + expect(res.status).toBe(200) + } - await check( - () => - next.cliOutput.match( + return retry(async () => { + expect(next.cliOutput).toMatch( /throwing error for \/blocking-fallback\/test-errors-2/ - ).length === 1 - ? 'success' - : next.cliOutput, - 'success' - ) + ) + }) + }) }) it('should not on-demand revalidate for fallback: blocking with onlyGenerated if not generated', async () => { @@ -2294,15 +2274,17 @@ describe('Prerender', () => { expect($('p').text()).toMatch(/Post:.*?test-if-generated-2/) expect(res.headers.get('x-nextjs-cache')).toMatch(/MISS/) - const res2 = await fetchViaHTTP( - next.url, - '/blocking-fallback/test-if-generated-2' - ) - const html2 = await res2.text() - const $2 = cheerio.load(html2) + await retry(async () => { + const res2 = await fetchViaHTTP( + next.url, + '/blocking-fallback/test-if-generated-2' + ) + const html2 = await res2.text() + const $2 = cheerio.load(html2) - expect(initialTime).toBe($2('#time').text()) - expect(res2.headers.get('x-nextjs-cache')).toMatch(/(HIT|STALE)/) + expect(initialTime).toBe($2('#time').text()) + expect(res2.headers.get('x-nextjs-cache')).toMatch(/(HIT|STALE)/) + }) const res3 = await fetchViaHTTP( next.url, @@ -2318,14 +2300,16 @@ describe('Prerender', () => { const revalidateData = await res3.json() expect(revalidateData.revalidated).toBe(true) - const res4 = await fetchViaHTTP( - next.url, - '/blocking-fallback/test-if-generated-2' - ) - const html4 = await res4.text() - const $4 = cheerio.load(html4) - expect($4('#time').text()).not.toBe(initialTime) - expect(res4.headers.get('x-nextjs-cache')).toMatch(/(HIT|STALE)/) + await retry(async () => { + const res4 = await fetchViaHTTP( + next.url, + '/blocking-fallback/test-if-generated-2' + ) + const html4 = await res4.text() + const $4 = cheerio.load(html4) + expect($4('#time').text()).not.toBe(initialTime) + expect(res4.headers.get('x-nextjs-cache')).toMatch(/(HIT|STALE)/) + }) }) it('should on-demand revalidate for revalidate: false', async () => { diff --git a/test/lib/next-modes/base.ts b/test/lib/next-modes/base.ts index ea92f26de5b6b2..ca64ecec5d5b7f 100644 --- a/test/lib/next-modes/base.ts +++ b/test/lib/next-modes/base.ts @@ -8,7 +8,7 @@ import { ChildProcess } from 'child_process' import { createNextInstall } from '../create-next-install' import { Span } from 'next/dist/trace' import webdriver from '../next-webdriver' -import { renderViaHTTP, fetchViaHTTP, findPort, retry } from 'next-test-utils' +import { renderViaHTTP, fetchViaHTTP, findPort } from 'next-test-utils' import cheerio from 'cheerio' import { once } from 'events' import { BrowserInterface } from '../browsers/base' @@ -491,7 +491,7 @@ export class NextInstance { public async patchFile( filename: string, content: string | ((content: string) => string), - retryWithTempContent?: (context: { newFile: boolean }) => Promise + runWithTempContent?: (context: { newFile: boolean }) => Promise ): Promise<{ newFile: boolean }> { const outputPath = path.join(this.testDir, filename) const newFile = !existsSync(outputPath) @@ -503,9 +503,9 @@ export class NextInstance { typeof content === 'function' ? content(previousContent) : content ) - if (retryWithTempContent) { + if (runWithTempContent) { try { - await retry(() => retryWithTempContent({ newFile })) + await runWithTempContent({ newFile }) } finally { if (previousContent === undefined) { await fs.rm(outputPath) diff --git a/test/lib/next-modes/next-dev.ts b/test/lib/next-modes/next-dev.ts index facd5165747906..5f4575ef91d23f 100644 --- a/test/lib/next-modes/next-dev.ts +++ b/test/lib/next-modes/next-dev.ts @@ -153,7 +153,7 @@ export class NextDevInstance extends NextInstance { public override async patchFile( filename: string, content: string | ((contents: string) => string), - retryWithTempContent?: (context: { newFile: boolean }) => Promise + runWithTempContent?: (context: { newFile: boolean }) => Promise ) { const isServerRunning = this.childProcess && !this.isStopping const cliOutputLength = this.cliOutput.length @@ -174,10 +174,10 @@ export class NextDevInstance extends NextInstance { } } - if (retryWithTempContent) { + if (runWithTempContent) { return super.patchFile(filename, content, async ({ newFile }) => { await waitForChanges({ newFile }) - await retryWithTempContent({ newFile }) + await runWithTempContent({ newFile }) }) }