From 98ad32ee46118bc5caccef870cd48c85aeb12e84 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Apr 2022 13:47:19 +0200 Subject: [PATCH 1/4] test: organize react 18 tests add env to options callbacks add stdout/stderr --- .../import-assertion/test/index.test.js | 35 +----- .../react-18-invalid-config/index.test.js | 73 +++++++++++-- test/integration/react-18/test/index.test.js | 53 ++------- .../test/functions.js | 9 +- .../test/index.test.js | 101 ++++-------------- .../test/rsc.js | 3 +- .../test/runtime.js | 4 +- .../test/switchable-runtime.test.js | 11 +- .../test/utils.js | 36 +------ test/lib/next-test-utils.js | 64 +++++++++++ 10 files changed, 179 insertions(+), 210 deletions(-) diff --git a/test/integration/import-assertion/test/index.test.js b/test/integration/import-assertion/test/index.test.js index 9bcfe028754ceb4..9a4992fc5928118 100644 --- a/test/integration/import-assertion/test/index.test.js +++ b/test/integration/import-assertion/test/index.test.js @@ -1,37 +1,8 @@ import { join } from 'path' -import { - nextBuild, - nextStart, - launchApp, - killApp, - findPort, - renderViaHTTP, -} from 'next-test-utils' +import { renderViaHTTP } from 'next-test-utils' const appDir = join(__dirname, '../') -function runSuite(suiteName, env, runTests) { - const context = { appDir } - describe(`${suiteName} ${env}`, () => { - if (env === 'prod') { - beforeAll(async () => { - context.appPort = await findPort() - await nextBuild(context.appDir) - context.server = await nextStart(context.appDir, context.appPort) - }) - } - if (env === 'dev') { - beforeAll(async () => { - context.appPort = await findPort() - context.server = await launchApp(context.appDir, context.appPort) - }) - } - afterAll(async () => await killApp(context.server)) - - runTests(context, env) - }) -} - function basic(context) { it('should handle json assertions', async () => { const esHtml = await renderViaHTTP(context.appPort, '/es') @@ -41,5 +12,5 @@ function basic(context) { }) } -runSuite('import-assertion', 'dev', basic) -runSuite('import-assertion', 'prod', basic) +runDevSuite('import-assertion', appDir, { runTests: basic }) +runProdSuite('import-assertion', appDir, { runTests: basic }) diff --git a/test/integration/react-18-invalid-config/index.test.js b/test/integration/react-18-invalid-config/index.test.js index 9cde36de3148d1b..dc2f1e2c930cc47 100644 --- a/test/integration/react-18-invalid-config/index.test.js +++ b/test/integration/react-18-invalid-config/index.test.js @@ -2,19 +2,47 @@ import fs from 'fs-extra' import { join } from 'path' -import { File, nextBuild } from 'next-test-utils' +import { + File, + nextBuild, + runDevSuite, + runProdSuite, + fetchViaHTTP, +} from 'next-test-utils' const appDir = __dirname const nodeArgs = ['-r', join(appDir, '../../lib/react-17-require-hook.js')] -const nextConfig = new File(join(appDir, 'next.config.js')) const reactDomPackagePah = join(appDir, 'node_modules/react-dom') +const nextConfig = new File(join(appDir, 'next.config.js')) +const documentPage = new File(join(appDir, 'pages/_document.js')) +const indexPage = join(appDir, 'pages/index.js') +const indexServerPage = join(appDir, 'pages/index.server.js') + +const documentWithGip = ` +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) +} + +Document.getInitialProps = (ctx) => { + return ctx.defaultGetInitialProps(ctx) +} +` -function writeNextConfig(config) { +function writeNextConfig(config, reactVersion = 17) { const content = ` const path = require('path') - module.exports = require(path.join(__dirname, '../../lib/with-react-17.js'))({ experimental: ${JSON.stringify( - config - )} }) + const withReact = ${reactVersion} === 18 ? v => v : require(path.join(__dirname, '../../lib/with-react-17.js')) + module.exports = withReact({ experimental: ${JSON.stringify(config)} }) ` nextConfig.write(content) } @@ -65,3 +93,36 @@ describe('React 17 with React 18 config', () => { expect(code).toBe(1) }) }) + +const documentSuite = { + runTests: (context, env) => { + if (env === 'dev') { + it('should error when custom _document has getInitialProps method', async () => { + const res = await fetchViaHTTP(context.appPort, '/') + expect(res.status).toBe(500) + }) + } else { + it('should failed building', async () => { + expect(context.code).toBe(1) + }) + } + }, + beforeAll: async () => { + writeNextConfig( + { + serverComponents: true, + }, + 18 + ) + documentPage.write(documentWithGip) + await fs.rename(indexPage, indexServerPage) + }, + afterAll: async () => { + documentPage.delete() + nextConfig.restore() + await fs.rename(indexServerPage, indexPage) + }, +} + +runDevSuite('Invalid custom document with gip', appDir, documentSuite) +runProdSuite('Invalid custom document with gip', appDir, documentSuite) diff --git a/test/integration/react-18/test/index.test.js b/test/integration/react-18/test/index.test.js index 7155004b6d19142..43e2287ac11b2fd 100644 --- a/test/integration/react-18/test/index.test.js +++ b/test/integration/react-18/test/index.test.js @@ -7,27 +7,23 @@ import { findPort, killApp, launchApp, - nextBuild, - nextStart, renderViaHTTP, hasRedbox, getRedboxHeader, + runDevSuite, + runProdSuite, } from 'next-test-utils' import concurrent from './concurrent' import basics from './basics' import strictMode from './strict-mode' import webdriver from 'next-webdriver' -// overrides react and react-dom to v18 -const nodeArgs = [] const appDir = join(__dirname, '../app') const nextConfig = new File(join(appDir, 'next.config.js')) const invalidPage = new File(join(appDir, 'pages/invalid.js')) describe('Basics', () => { - runTests('default setting with react 18', (context, env) => - basics(context, env) - ) + runTests('default setting with react 18', basics) }) // React 18 with Strict Mode enabled might cause double invocation of lifecycle methods. @@ -37,9 +33,7 @@ describe('Strict mode - dev', () => { beforeAll(async () => { nextConfig.replace('// reactStrictMode: true,', 'reactStrictMode: true,') context.appPort = await findPort() - context.server = await launchApp(context.appDir, context.appPort, { - nodeArgs, - }) + context.server = await launchApp(context.appDir, context.appPort) }) afterAll(() => { @@ -84,42 +78,11 @@ function runTestsAgainstRuntime(runtime) { ) } -function runTest(env, name, fn, options) { - const context = { appDir } - describe(`${name} (${env})`, () => { - beforeAll(async () => { - context.appPort = await findPort() - context.stderr = '' - options?.beforeAll(env) - if (env === 'dev') { - context.server = await launchApp(context.appDir, context.appPort, { - nodeArgs, - onStderr(msg) { - context.stderr += msg - }, - }) - } else { - await nextBuild(context.appDir, [], { nodeArgs }) - context.server = await nextStart(context.appDir, context.appPort, { - nodeArgs, - onStderr(msg) { - context.stderr += msg - }, - }) - } - }) - afterAll(async () => { - options?.afterAll(env) - await killApp(context.server) - }) - fn(context, env) - }) -} - runTestsAgainstRuntime('edge') runTestsAgainstRuntime('nodejs') -function runTests(name, fn, options) { - runTest('dev', name, fn, options) - runTest('prod', name, fn, options) +function runTests(name, fn, opts) { + const suiteOptions = { ...opts, runTests: fn } + runDevSuite(name, appDir, suiteOptions) + runProdSuite(name, appDir, suiteOptions) } diff --git a/test/integration/react-streaming-and-server-components/test/functions.js b/test/integration/react-streaming-and-server-components/test/functions.js index 965190486c2faef..31f98fcca124f10 100644 --- a/test/integration/react-streaming-and-server-components/test/functions.js +++ b/test/integration/react-streaming-and-server-components/test/functions.js @@ -10,20 +10,23 @@ import { nextBuild } from './utils' export default function (context) { it('should not generate functions manifest when filesystem API is not enabled', async () => { // Make sure there is no existing functions manifest (caused by failed tests etc). - await fs.remove(join(context.appDir, '.next')) + const distDir = join(context.appDir, '.next') + await fs.remove(distDir) await nextBuild(context.appDir) const functionsManifestPath = join( - context.distDir, + distDir, 'server', 'functions-manifest.json' ) expect(fs.existsSync(functionsManifestPath)).toBe(false) await fs.remove(join(context.appDir, '.next')) }) + it('should contain rsc paths in functions manifest', async () => { + const distDir = join(context.appDir, '.next') await nextBuild(context.appDir, { env: { ENABLE_FILE_SYSTEM_API: '1' } }) const functionsManifestPath = join( - context.distDir, + distDir, 'server', 'functions-manifest.json' ) diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index 6ac2a84e5cb42b7..658d90e9b5ff8e0 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -3,16 +3,17 @@ import { join } from 'path' import fs from 'fs-extra' -import { fetchViaHTTP, findPort, killApp, renderViaHTTP } from 'next-test-utils' - import { + fetchViaHTTP, + renderViaHTTP, nextBuild, - nextStart, - nextDev, + runDevSuite, + runProdSuite, +} from 'next-test-utils' + +import { appDir, nativeModuleTestAppDir, - distDir, - documentPage, appPage, appServerPage, error500Page, @@ -25,26 +26,6 @@ import streaming from './streaming' import basic from './basic' import runtime from './runtime' -const documentWithGip = ` -import { Html, Head, Main, NextScript } from 'next/document' - -export default function Document() { - return ( - - - -
- - - - ) -} - -Document.getInitialProps = (ctx) => { - return ctx.defaultGetInitialProps(ctx) -} -` - const rscAppPage = ` import Container from '../components/container.server' export default function App({children}) { @@ -72,7 +53,7 @@ describe('Edge runtime - errors', () => { it('should warn user that native node APIs are not supported', async () => { const fsImportedErrorMessage = 'Native Node.js APIs are not supported in the Edge Runtime. Found `dns` imported.' - const { stderr } = await nextBuild(nativeModuleTestAppDir) + const { stderr } = await nextBuild(nativeModuleTestAppDir, { stderr: true }) expect(stderr).toContain(fsImportedErrorMessage) }) }) @@ -80,6 +61,7 @@ describe('Edge runtime - errors', () => { const edgeRuntimeBasicSuite = { runTests: (context, env) => { const options = { runtime: 'edge', env } + const distDir = join(appDir, '.next') basic(context, options) streaming(context, options) rsc(context, options) @@ -158,6 +140,7 @@ const edgeRuntimeBasicSuite = { const nodejsRuntimeBasicSuite = { runTests: (context, env) => { const options = { runtime: 'nodejs', env } + const distDir = join(appDir, '.next') basic(context, options) streaming(context, options) rsc(context, options) @@ -217,61 +200,13 @@ const cssSuite = { afterAll: () => appPage.delete(), } -const documentSuite = { - runTests: (context, env) => { - if (env === 'dev') { - it('should error when custom _document has getInitialProps method', async () => { - const res = await fetchViaHTTP(context.appPort, '/') - expect(res.status).toBe(500) - }) - } else { - it('should failed building', async () => { - expect(context.code).toBe(1) - }) - } - }, - beforeAll: () => documentPage.write(documentWithGip), - afterAll: () => documentPage.delete(), -} - -runSuite('Node.js runtime', 'dev', nodejsRuntimeBasicSuite) -runSuite('Node.js runtime', 'prod', nodejsRuntimeBasicSuite) -runSuite('Edge runtime', 'dev', edgeRuntimeBasicSuite) -runSuite('Edge runtime', 'prod', edgeRuntimeBasicSuite) +runDevSuite('Node.js runtime', appDir, nodejsRuntimeBasicSuite) +runProdSuite('Node.js runtime', appDir, nodejsRuntimeBasicSuite) +runDevSuite('Edge runtime', appDir, edgeRuntimeBasicSuite) +runProdSuite('Edge runtime', appDir, edgeRuntimeBasicSuite) -runSuite('Custom App', 'dev', customAppPageSuite) -runSuite('Custom App', 'prod', customAppPageSuite) +runDevSuite('Custom App', appDir, customAppPageSuite) +runProdSuite('Custom App', appDir, customAppPageSuite) -runSuite('CSS', 'dev', cssSuite) -runSuite('CSS', 'prod', cssSuite) - -runSuite('Custom Document', 'dev', documentSuite) -runSuite('Custom Document', 'prod', documentSuite) - -function runSuite(suiteName, env, options) { - const context = { appDir, distDir } - describe(`${suiteName} ${env}`, () => { - beforeAll(async () => { - options.beforeAll?.() - if (env === 'prod') { - context.appPort = await findPort() - const { stdout, stderr, code } = await nextBuild(context.appDir) - context.stdout = stdout - context.stderr = stderr - context.code = code - context.server = await nextStart(context.appDir, context.appPort) - } - if (env === 'dev') { - context.appPort = await findPort() - context.server = await nextDev(context.appDir, context.appPort) - } - }) - afterAll(async () => { - options.afterAll?.() - if (context.server) { - await killApp(context.server) - } - }) - options.runTests(context, env) - }) -} +runDevSuite('CSS', appDir, cssSuite) +runProdSuite('CSS', appDir, cssSuite) 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 a7cc18df86b7f54..cb5e0537d15104a 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -3,9 +3,10 @@ import webdriver from 'next-webdriver' import { renderViaHTTP, check } from 'next-test-utils' import { join } from 'path' import fs from 'fs-extra' -import { distDir, getNodeBySelector } from './utils' +import { getNodeBySelector } from './utils' export default function (context, { runtime, env }) { + const distDir = join(context.appDir, '.next') it('should render server components correctly', async () => { const homeHTML = await renderViaHTTP(context.appPort, '/', null, { headers: { diff --git a/test/integration/react-streaming-and-server-components/test/runtime.js b/test/integration/react-streaming-and-server-components/test/runtime.js index b166fdb4ded351b..236510214b6251e 100644 --- a/test/integration/react-streaming-and-server-components/test/runtime.js +++ b/test/integration/react-streaming-and-server-components/test/runtime.js @@ -2,9 +2,9 @@ import { renderViaHTTP } from 'next-test-utils' import { join } from 'path' import fs from 'fs-extra' -import { distDir } from './utils' - export default async function runtime(context, { runtime, env }) { + const distDir = join(context.appDir, '.next') + if (runtime === 'edge') { it('should support per-page runtime configuration', async () => { const html1 = await renderViaHTTP(context.appPort, '/runtime') diff --git a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js index d2611080404923f..ef4d0e9a4df1421 100644 --- a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js +++ b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js @@ -5,10 +5,12 @@ import { check, findPort, killApp, + launchApp, + nextBuild, + nextStart, renderViaHTTP, waitFor, } from 'next-test-utils' -import { nextBuild, nextDev, nextStart } from './utils' const appDir = join(__dirname, '../switchable-runtime') @@ -48,7 +50,10 @@ describe('Switchable runtime (prod)', () => { beforeAll(async () => { context.appPort = await findPort() - const { stdout, stderr } = await nextBuild(context.appDir) + const { stdout, stderr } = await nextBuild(context.appDir, { + stderr: true, + stdout: true, + }) context.stdout = stdout context.stderr = stderr context.server = await nextStart(context.appDir, context.appPort) @@ -252,7 +257,7 @@ describe('Switchable runtime (dev)', () => { beforeAll(async () => { context.appPort = await findPort() - context.server = await nextDev(context.appDir, context.appPort) + context.server = await launchApp(context.appDir, context.appPort) }) afterAll(async () => { await killApp(context.server) diff --git a/test/integration/react-streaming-and-server-components/test/utils.js b/test/integration/react-streaming-and-server-components/test/utils.js index 65bae002ce679f2..d1f19ed8e80fd9d 100644 --- a/test/integration/react-streaming-and-server-components/test/utils.js +++ b/test/integration/react-streaming-and-server-components/test/utils.js @@ -1,51 +1,17 @@ import { join } from 'path' -import { - File, - launchApp, - nextBuild as _nextBuild, - nextStart as _nextStart, -} from 'next-test-utils' +import { File } from 'next-test-utils' import cheerio from 'cheerio' -const nodeArgs = [] - export const appDir = join(__dirname, '../app') export const nativeModuleTestAppDir = join( __dirname, '../unsupported-native-module' ) -export const distDir = join(__dirname, '../app/.next') -export const documentPage = new File(join(appDir, 'pages/_document.jsx')) export const appPage = new File(join(appDir, 'pages/_app.js')) export const appServerPage = new File(join(appDir, 'pages/_app.server.js')) export const error500Page = new File(join(appDir, 'pages/500.js')) export const nextConfig = new File(join(appDir, 'next.config.js')) -export async function nextBuild(dir, options) { - return await _nextBuild(dir, [], { - ...options, - stdout: true, - stderr: true, - nodeArgs, - }) -} - -export async function nextStart(dir, port) { - return await _nextStart(dir, port, { - stdout: true, - stderr: true, - nodeArgs, - }) -} - -export async function nextDev(dir, port) { - return await launchApp(dir, port, { - stdout: true, - stderr: true, - nodeArgs, - }) -} - export function getNodeBySelector(html, selector) { const $ = cheerio.load(html) return $(selector) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index c90aaa433e25d6e..a33c5970a128b75 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -722,3 +722,67 @@ export function readNextBuildServerPageFile(appDir, page) { const pageFile = getPageFileFromPagesManifest(appDir, page) return readFileSync(path.join(appDir, '.next', 'server', pageFile), 'utf8') } + +/** + * + * @param {string} suiteName + * @param {{env: 'prod' | 'dev', appDir: string}} context + * @param {{beforeAll?: Function; afterAll?: Function; runTests: Function}} options + */ +function runSuite(suiteName, context, options) { + const { appDir, env } = context + describe(`${suiteName} ${env}`, () => { + beforeAll(async () => { + options.beforeAll?.(env) + context.stderr = '' + const onStderr = (msg) => { + context.stderr += msg + } + if (env === 'prod') { + context.appPort = await findPort() + const { stdout, stderr, code } = await nextBuild(appDir, [], { + stderr: true, + stdout: true, + }) + context.stdout = stdout + context.stderr = stderr + context.code = code + context.server = await nextStart(context.appDir, context.appPort, { + onStderr, + }) + } else if (env === 'dev') { + context.appPort = await findPort() + context.server = await launchApp(context.appDir, context.appPort, { + onStderr, + }) + } + }) + afterAll(async () => { + options.afterAll?.(env) + if (context.server) { + await killApp(context.server) + } + }) + options.runTests(context, env) + }) +} + +/** + * + * @param {string} suiteName + * @param {string} appDir + * @param {{beforeAll?: Function; afterAll?: Function; runTests: Function}} options + */ +export function runDevSuite(suiteName, appDir, options) { + return runSuite(suiteName, { appDir, env: 'dev' }, options) +} + +/** + * + * @param {string} suiteName + * @param {string} appDir + * @param {{beforeAll?: Function; afterAll?: Function; runTests: Function}} options + */ +export function runProdSuite(suiteName, appDir, options) { + return runSuite(suiteName, { appDir, env: 'prod' }, options) +} From 3467e7ae03f4b2e7c48687bde61f41d2d7daa9b2 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Apr 2022 14:33:45 +0200 Subject: [PATCH 2/4] fix missing import --- test/integration/import-assertion/test/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/import-assertion/test/index.test.js b/test/integration/import-assertion/test/index.test.js index 9a4992fc5928118..b080b485ddb5b1c 100644 --- a/test/integration/import-assertion/test/index.test.js +++ b/test/integration/import-assertion/test/index.test.js @@ -1,5 +1,5 @@ import { join } from 'path' -import { renderViaHTTP } from 'next-test-utils' +import { renderViaHTTP, runDevSuite, runProdSuite } from 'next-test-utils' const appDir = join(__dirname, '../') From fd6e496781357064ee4bb2806f0a5d6328b25188 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Apr 2022 15:09:30 +0200 Subject: [PATCH 3/4] fix next build arg --- .../test/switchable-runtime.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js index ef4d0e9a4df1421..c3a2c276769df6b 100644 --- a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js +++ b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js @@ -50,7 +50,7 @@ describe('Switchable runtime (prod)', () => { beforeAll(async () => { context.appPort = await findPort() - const { stdout, stderr } = await nextBuild(context.appDir, { + const { stdout, stderr } = await nextBuild(context.appDir, [], { stderr: true, stdout: true, }) From bfdc6029dffa70b1f31ce61004fad47501509369 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Apr 2022 15:38:30 +0200 Subject: [PATCH 4/4] fix next build arg --- .../react-streaming-and-server-components/test/index.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index 658d90e9b5ff8e0..4e526e8ede82dc4 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -53,7 +53,9 @@ describe('Edge runtime - errors', () => { it('should warn user that native node APIs are not supported', async () => { const fsImportedErrorMessage = 'Native Node.js APIs are not supported in the Edge Runtime. Found `dns` imported.' - const { stderr } = await nextBuild(nativeModuleTestAppDir, { stderr: true }) + const { stderr } = await nextBuild(nativeModuleTestAppDir, [], { + stderr: true, + }) expect(stderr).toContain(fsImportedErrorMessage) }) })