Update flakey tests and add Node.js setup retrying (#47871)
x-ref: https://github.com/vercel/next.js/actions/runs/4599615812/jobs/8125278036 x-ref: https://github.com/vercel/next.js/actions/runs/4598323624/jobs/8124618075?pr=47365 x-ref: https://github.com/vercel/next.js/actions/runs/4598323624/jobs/8124612692?pr=47365
This commit is contained in:
parent
036f540bb4
commit
320ebe2d34
5 changed files with 347 additions and 255 deletions
16
.github/workflows/build_test_deploy.yml
vendored
16
.github/workflows/build_test_deploy.yml
vendored
|
@ -655,7 +655,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_CNA=1 xvfb-run node run-tests.js test/integration/create-next-app/index.test.ts test/integration/create-next-app/templates.test.ts >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_CNA=1 xvfb-run node run-tests.js test/integration/create-next-app/index.test.ts test/integration/create-next-app/templates.test.ts >> /proc/1/fd/1"
|
||||
if: ${{ needs.build.outputs.docsChange == 'nope' }}
|
||||
|
||||
- name: Upload test trace
|
||||
|
@ -693,7 +693,7 @@ jobs:
|
|||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Run tests
|
||||
run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && cd ./packages/next-codemod && pnpm build && pnpm test >> /proc/1/fd/1"
|
||||
run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && cd ./packages/next-codemod && pnpm build && pnpm test >> /proc/1/fd/1"
|
||||
|
||||
testIntegration:
|
||||
name: Test Integration
|
||||
|
@ -758,7 +758,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/28 >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/28 >> /proc/1/fd/1"
|
||||
if: ${{needs.build.outputs.docsChange == 'nope'}}
|
||||
|
||||
- name: Upload test trace
|
||||
|
@ -855,7 +855,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && __INTERNAL_NEXT_DEV_TEST_TURBO_DEV=TRUE __INTERNAL_CUSTOM_TURBOPACK_BINDINGS=${NEXT_BINDINGS_BIN} __INTERNAL_NEXT_DEV_TEST_TURBO_GLOB_MATCH=${NEXT_DEV_TEST_GLOB} NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type development --timings -c 1 $TEST_FILES_LIST >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && __INTERNAL_NEXT_DEV_TEST_TURBO_DEV=TRUE __INTERNAL_CUSTOM_TURBOPACK_BINDINGS=${NEXT_BINDINGS_BIN} __INTERNAL_NEXT_DEV_TEST_TURBO_GLOB_MATCH=${NEXT_DEV_TEST_GLOB} NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type development --timings -c 1 $TEST_FILES_LIST >> /proc/1/fd/1"
|
||||
name: Run test/development
|
||||
if: ${{needs.build.outputs.docsChange == 'nope'}}
|
||||
|
||||
|
@ -901,7 +901,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_MAINTENANCE_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSERNAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_MAINTENANCE_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSERNAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1"
|
||||
if: ${{needs.build.outputs.docsChange == 'nope'}}
|
||||
|
||||
testSafari:
|
||||
|
@ -955,7 +955,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSER_NAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSER_NAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1"
|
||||
if: ${{needs.build.outputs.docsChange == 'nope'}}
|
||||
|
||||
publishRelease:
|
||||
|
@ -1052,7 +1052,7 @@ jobs:
|
|||
- run: RESET_VC_PROJECT=true node scripts/reset-vercel-project.mjs
|
||||
name: Reset test project
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && VERCEL_TEST_TOKEN=${{ secrets.VERCEL_TEST_TOKEN }} VERCEL_TEST_TEAM=vtest314-next-e2e-tests NEXT_TEST_JOB=1 NEXT_TEST_MODE=deploy TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && VERCEL_TEST_TOKEN=${{ secrets.VERCEL_TEST_TOKEN }} VERCEL_TEST_TEAM=vtest314-next-e2e-tests NEXT_TEST_JOB=1 NEXT_TEST_MODE=deploy TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e >> /proc/1/fd/1"
|
||||
name: Run test/e2e (deploy)
|
||||
|
||||
- name: Upload test trace
|
||||
|
@ -1274,7 +1274,7 @@ jobs:
|
|||
name: next-swc-test-binary
|
||||
path: packages/next-swc/native
|
||||
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ env.NODE_LTS_VERSION }} | FORCE=1 bash && node -v && node ./scripts/setup-wasm.mjs && npm i -g pnpm@${PNPM_VERSION} > /dev/null && TEST_WASM=true xvfb-run node run-tests.js test/integration/production/test/index.test.js test/e2e/streaming-ssr/index.test.ts >> /proc/1/fd/1"
|
||||
- run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && node ./scripts/setup-wasm.mjs && npm i -g pnpm@${PNPM_VERSION} > /dev/null && TEST_WASM=true xvfb-run node run-tests.js test/integration/production/test/index.test.js test/e2e/streaming-ssr/index.test.ts >> /proc/1/fd/1"
|
||||
if: ${{needs.build.outputs.docsChange == 'nope'}}
|
||||
|
||||
# Build binaries for publishing
|
||||
|
|
|
@ -1247,13 +1247,16 @@ export default async function build(
|
|||
// each worker will consume ~1GB of memory in a production build.
|
||||
// For example, if the system has 10 CPU cores and 8GB of remaining memory
|
||||
// we will use 8 workers.
|
||||
const numWorkers =
|
||||
const numWorkers = Math.max(
|
||||
config.experimental.cpus !== defaultConfig.experimental!.cpus
|
||||
? config.experimental.cpus
|
||||
? (config.experimental.cpus as number)
|
||||
: Math.min(
|
||||
config.experimental.cpus || 1,
|
||||
Math.floor(os.freemem() / 1e9)
|
||||
)
|
||||
),
|
||||
// enforce a minimum of 4 workers
|
||||
4
|
||||
)
|
||||
|
||||
const staticWorkers = new Worker(staticWorker, {
|
||||
timeout: timeout * 1000,
|
||||
|
|
6
scripts/setup-node.sh
Executable file
6
scripts/setup-node.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
# retry setting up Node.js version 5 times waiting 15 seconds between
|
||||
for i in 1 2 3 4 5;
|
||||
do curl -s https://install-node.vercel.app/v${NODE_VERSION} | FORCE=1 bash && break || sleep 15;
|
||||
done
|
|
@ -9,6 +9,7 @@ import {
|
|||
runNextCommand,
|
||||
runNextCommandDev,
|
||||
} from 'next-test-utils'
|
||||
import fs from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import pkg from 'next/package'
|
||||
import http from 'http'
|
||||
|
@ -17,7 +18,191 @@ import stripAnsi from 'strip-ansi'
|
|||
const dir = join(__dirname, '..')
|
||||
const dirDuplicateSass = join(__dirname, '../duplicate-sass')
|
||||
|
||||
const testExitSignal = async (
|
||||
killSignal = '',
|
||||
args = [],
|
||||
readyRegex = /Creating an optimized production/
|
||||
) => {
|
||||
let instance
|
||||
const killSigint = (inst) => {
|
||||
instance = inst
|
||||
}
|
||||
let output = ''
|
||||
|
||||
let cmdPromise = runNextCommand(args, {
|
||||
ignoreFail: true,
|
||||
instance: killSigint,
|
||||
onStdout: (msg) => {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
}).catch((err) => expect.fail(err.message))
|
||||
|
||||
await check(() => output, readyRegex)
|
||||
instance.kill(killSignal)
|
||||
|
||||
const { code, signal } = await cmdPromise
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitSignal = process.platform === `win32` ? killSignal : null
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
expect(code).toBe(0)
|
||||
}
|
||||
|
||||
describe('CLI Usage', () => {
|
||||
describe('start', () => {
|
||||
test('should exit when SIGINT is signalled', async () => {
|
||||
require('console').log('before build')
|
||||
await fs.remove(join(dir, '.next'))
|
||||
await nextBuild(dir, undefined, {
|
||||
onStdout(msg) {
|
||||
console.log(msg)
|
||||
},
|
||||
onStderr(msg) {
|
||||
console.log(msg)
|
||||
},
|
||||
})
|
||||
require('console').log('build finished')
|
||||
|
||||
const port = await findPort()
|
||||
await testExitSignal(
|
||||
'SIGINT',
|
||||
['start', dir, '-p', port],
|
||||
/started server on/
|
||||
)
|
||||
})
|
||||
test('should exit when SIGTERM is signalled', async () => {
|
||||
await fs.remove(join(dir, '.next'))
|
||||
await nextBuild(dir, undefined, {
|
||||
onStdout(msg) {
|
||||
console.log(msg)
|
||||
},
|
||||
onStderr(msg) {
|
||||
console.log(msg)
|
||||
},
|
||||
})
|
||||
const port = await findPort()
|
||||
await testExitSignal(
|
||||
'SIGTERM',
|
||||
['start', dir, '-p', port],
|
||||
/started server on/
|
||||
)
|
||||
})
|
||||
|
||||
test('--help', async () => {
|
||||
const help = await runNextCommand(['start', '--help'], {
|
||||
stdout: true,
|
||||
})
|
||||
expect(help.stdout).toMatch(/Starts the application in production mode/)
|
||||
})
|
||||
|
||||
test('-h', async () => {
|
||||
const help = await runNextCommand(['start', '-h'], {
|
||||
stdout: true,
|
||||
})
|
||||
expect(help.stdout).toMatch(/Starts the application in production mode/)
|
||||
})
|
||||
|
||||
test('should format IPv6 addresses correctly', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommand(
|
||||
['start', '--hostname', '::', '--port', port],
|
||||
{
|
||||
stdout: true,
|
||||
}
|
||||
)
|
||||
expect(output.stdout).toMatch(new RegExp(`on \\[::\\]:${port}`))
|
||||
expect(output.stdout).toMatch(new RegExp(`http://\\[::1\\]:${port}`))
|
||||
})
|
||||
|
||||
test('should warn when unknown argument provided', async () => {
|
||||
const { stderr } = await runNextCommand(['start', '--random'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(stderr).toEqual('Unknown or unexpected option: --random\n')
|
||||
})
|
||||
test('should not throw UnhandledPromiseRejectionWarning', async () => {
|
||||
const { stderr } = await runNextCommand(['start', '--random'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
|
||||
})
|
||||
|
||||
test('duplicate sass deps', async () => {
|
||||
const port = await findPort()
|
||||
|
||||
let stderr = ''
|
||||
let instance = await launchApp(dirDuplicateSass, port, {
|
||||
stderr: true,
|
||||
onStderr(msg) {
|
||||
stderr += msg
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
await check(() => stderr, /both `sass` and `node-sass` installed/)
|
||||
} finally {
|
||||
await killApp(instance)
|
||||
}
|
||||
})
|
||||
|
||||
test('invalid directory', async () => {
|
||||
const output = await runNextCommand(['start', 'non-existent'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(output.stderr).toContain(
|
||||
'Invalid project directory provided, no such directory'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout string arg', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', 'string'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "NaN"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout negative number', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout=-100'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "-100"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout Infinity', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', 'Infinity'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "Infinity"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout happy path', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', '100'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).not.toContain(
|
||||
'Invalid keep alive timeout provided, expected a non negative number'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('no command', () => {
|
||||
test('--help', async () => {
|
||||
const help = await runNextCommand(['--help'], {
|
||||
|
@ -114,32 +299,11 @@ describe('CLI Usage', () => {
|
|||
})
|
||||
|
||||
test('should exit when SIGINT is signalled', async () => {
|
||||
const killSigint = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGINT'), 1000)
|
||||
const { code, signal } = await runNextCommand(['build', dir], {
|
||||
ignoreFail: true,
|
||||
instance: killSigint,
|
||||
})
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGINT' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
await testExitSignal('SIGINT', ['build', dir])
|
||||
})
|
||||
|
||||
test('should exit when SIGTERM is signalled', async () => {
|
||||
const killSigterm = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGTERM'), 1000)
|
||||
const { code, signal } = await runNextCommand(['build', dir], {
|
||||
ignoreFail: true,
|
||||
instance: killSigterm,
|
||||
})
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGTERM' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
await testExitSignal('SIGTERM', ['build', dir])
|
||||
})
|
||||
|
||||
test('invalid directory', async () => {
|
||||
|
@ -169,61 +333,113 @@ describe('CLI Usage', () => {
|
|||
|
||||
test('custom directory', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev([dir, '--port', port], true)
|
||||
expect(output).toMatch(/started server/i)
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir, '--port', port], undefined, {
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
})
|
||||
try {
|
||||
await check(() => output, /started server/i)
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('--port', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev([dir, '--port', port], true)
|
||||
expect(output).toMatch(new RegExp(`on 0.0.0.0:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir, '--port', port], undefined, {
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
})
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('--port 0', async () => {
|
||||
const output = await runNextCommandDev([dir, '--port', '0'], true)
|
||||
const port = await findPort()
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir, '--port', port], undefined, {
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
})
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
const matches = /on 0.0.0.0:(\d+)/.exec(output)
|
||||
expect(matches).not.toBe(null)
|
||||
|
||||
const port = parseInt(matches[1])
|
||||
const _port = parseInt(matches[1])
|
||||
// Regression test: port 0 was interpreted as if no port had been
|
||||
// provided, falling back to 3000.
|
||||
expect(port).not.toBe(3000)
|
||||
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
expect(_port).not.toBe(3000)
|
||||
})
|
||||
|
||||
test('PORT=0', async () => {
|
||||
const output = await runNextCommandDev([dir], true, {
|
||||
env: { PORT: 0 },
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir], undefined, {
|
||||
env: {
|
||||
PORT: 0,
|
||||
},
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
})
|
||||
const matches = /on 0.0.0.0:(\d+)/.exec(output)
|
||||
expect(matches).not.toBe(null)
|
||||
|
||||
const port = parseInt(matches[1])
|
||||
// Regression test: port 0 was interpreted as if no port had been
|
||||
// provided, falling back to 3000.
|
||||
expect(port).not.toBe(3000)
|
||||
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
try {
|
||||
await check(() => output, /on 0.0.0.0:(\d+)/)
|
||||
const matches = /on 0.0.0.0:(\d+)/.exec(output)
|
||||
const _port = parseInt(matches[1])
|
||||
expect(matches).not.toBe(null)
|
||||
// Regression test: port 0 was interpreted as if no port had been
|
||||
// provided, falling back to 3000.
|
||||
expect(_port).not.toBe(3000)
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test("NODE_OPTIONS='--inspect'", async () => {
|
||||
// this test checks that --inspect works by launching a single debugger for the main Next.js process,
|
||||
// not for its subprocesses
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev([dir, '--port', port], true, {
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir, '--port', port], undefined, {
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
env: { NODE_OPTIONS: '--inspect' },
|
||||
})
|
||||
expect(output).toMatch(new RegExp(`on 0.0.0.0:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('-p', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev([dir, '-p', port], true)
|
||||
expect(output).toMatch(new RegExp(`on 0.0.0.0:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
let output = ''
|
||||
const app = await runNextCommandDev([dir, '-p', port], undefined, {
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
env: { NODE_OPTIONS: '--inspect' },
|
||||
})
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('-p conflict', async () => {
|
||||
|
@ -261,32 +477,62 @@ describe('CLI Usage', () => {
|
|||
|
||||
test('--hostname', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev(
|
||||
let output = ''
|
||||
const app = await runNextCommandDev(
|
||||
[dir, '--hostname', '0.0.0.0', '--port', port],
|
||||
true
|
||||
undefined,
|
||||
{
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(output).toMatch(new RegExp(`on 0.0.0.0:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('-H', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev(
|
||||
let output = ''
|
||||
const app = await runNextCommandDev(
|
||||
[dir, '-H', '0.0.0.0', '--port', port],
|
||||
true
|
||||
undefined,
|
||||
{
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(output).toMatch(new RegExp(`on 0.0.0.0:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://localhost:${port}`))
|
||||
try {
|
||||
await check(() => output, new RegExp(`on 0.0.0.0:${port}`))
|
||||
await check(() => output, new RegExp(`http://localhost:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('should format IPv6 addresses correctly', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommandDev(
|
||||
let output = ''
|
||||
const app = await runNextCommandDev(
|
||||
[dir, '--hostname', '::', '--port', port],
|
||||
true
|
||||
undefined,
|
||||
{
|
||||
onStdout(msg) {
|
||||
output += stripAnsi(msg)
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(output).toMatch(new RegExp(`on \\[::\\]:${port}`))
|
||||
expect(output).toMatch(new RegExp(`http://\\[::1\\]:${port}`))
|
||||
try {
|
||||
await check(() => output, new RegExp(`on \\[::\\]:${port}`))
|
||||
await check(() => output, new RegExp(`http://\\[::1\\]:${port}`))
|
||||
} finally {
|
||||
await killApp(app)
|
||||
}
|
||||
})
|
||||
|
||||
test('should warn when unknown argument provided', async () => {
|
||||
|
@ -303,34 +549,20 @@ describe('CLI Usage', () => {
|
|||
})
|
||||
|
||||
test('should exit when SIGINT is signalled', async () => {
|
||||
const killSigint = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGINT'), 2000)
|
||||
const port = await findPort()
|
||||
const { code, signal } = await runNextCommand(['dev', dir, '-p', port], {
|
||||
ignoreFail: true,
|
||||
instance: killSigint,
|
||||
})
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGINT' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
await testExitSignal(
|
||||
'SIGINT',
|
||||
['dev', dir, '-p', port],
|
||||
/started server on/
|
||||
)
|
||||
})
|
||||
test('should exit when SIGTERM is signalled', async () => {
|
||||
const killSigterm = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGTERM'), 2000)
|
||||
const port = await findPort()
|
||||
const { code, signal } = await runNextCommand(['dev', dir, '-p', port], {
|
||||
ignoreFail: true,
|
||||
instance: killSigterm,
|
||||
})
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGTERM' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
await testExitSignal(
|
||||
'SIGTERM',
|
||||
['dev', dir, '-p', port],
|
||||
/started server on/
|
||||
)
|
||||
})
|
||||
|
||||
test('invalid directory', async () => {
|
||||
|
@ -343,161 +575,6 @@ describe('CLI Usage', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('start', () => {
|
||||
test('--help', async () => {
|
||||
const help = await runNextCommand(['start', '--help'], {
|
||||
stdout: true,
|
||||
})
|
||||
expect(help.stdout).toMatch(/Starts the application in production mode/)
|
||||
})
|
||||
|
||||
test('-h', async () => {
|
||||
const help = await runNextCommand(['start', '-h'], {
|
||||
stdout: true,
|
||||
})
|
||||
expect(help.stdout).toMatch(/Starts the application in production mode/)
|
||||
})
|
||||
|
||||
test('should format IPv6 addresses correctly', async () => {
|
||||
const port = await findPort()
|
||||
const output = await runNextCommand(
|
||||
['start', '--hostname', '::', '--port', port],
|
||||
{
|
||||
stdout: true,
|
||||
}
|
||||
)
|
||||
expect(output.stdout).toMatch(new RegExp(`on \\[::\\]:${port}`))
|
||||
expect(output.stdout).toMatch(new RegExp(`http://\\[::1\\]:${port}`))
|
||||
})
|
||||
|
||||
test('should warn when unknown argument provided', async () => {
|
||||
const { stderr } = await runNextCommand(['start', '--random'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(stderr).toEqual('Unknown or unexpected option: --random\n')
|
||||
})
|
||||
test('should not throw UnhandledPromiseRejectionWarning', async () => {
|
||||
const { stderr } = await runNextCommand(['start', '--random'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
|
||||
})
|
||||
|
||||
test('duplicate sass deps', async () => {
|
||||
const port = await findPort()
|
||||
|
||||
let stderr = ''
|
||||
let instance = await launchApp(dirDuplicateSass, port, {
|
||||
stderr: true,
|
||||
onStderr(msg) {
|
||||
stderr += msg
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
await check(() => stderr, /both `sass` and `node-sass` installed/)
|
||||
} finally {
|
||||
await killApp(instance)
|
||||
}
|
||||
})
|
||||
|
||||
test('should exit when SIGINT is signalled', async () => {
|
||||
const killSigint = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGINT'), 1000)
|
||||
await nextBuild(dir)
|
||||
const port = await findPort()
|
||||
const { code, signal } = await runNextCommand(
|
||||
['start', dir, '-p', port],
|
||||
{
|
||||
ignoreFail: true,
|
||||
instance: killSigint,
|
||||
}
|
||||
)
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGINT' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
})
|
||||
test('should exit when SIGTERM is signalled', async () => {
|
||||
const killSigterm = (instance) =>
|
||||
setTimeout(() => instance.kill('SIGTERM'), 1000)
|
||||
await nextBuild(dir)
|
||||
const port = await findPort()
|
||||
const { code, signal } = await runNextCommand(
|
||||
['start', dir, '-p', port],
|
||||
{
|
||||
ignoreFail: true,
|
||||
instance: killSigterm,
|
||||
}
|
||||
)
|
||||
// Node can only partially emulate signals on Windows. Our signal handlers won't affect the exit code.
|
||||
// See: https://nodejs.org/api/process.html#process_signal_events
|
||||
const expectedExitCode = process.platform === `win32` ? null : 0
|
||||
const expectedExitSignal = process.platform === `win32` ? 'SIGTERM' : null
|
||||
expect(code).toBe(expectedExitCode)
|
||||
expect(signal).toBe(expectedExitSignal)
|
||||
})
|
||||
|
||||
test('invalid directory', async () => {
|
||||
const output = await runNextCommand(['start', 'non-existent'], {
|
||||
stderr: true,
|
||||
})
|
||||
expect(output.stderr).toContain(
|
||||
'Invalid project directory provided, no such directory'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout string arg', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', 'string'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "NaN"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout negative number', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout=-100'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "-100"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout Infinity', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', 'Infinity'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).toContain(
|
||||
'Invalid --keepAliveTimeout, expected a non negative number but received "Infinity"'
|
||||
)
|
||||
})
|
||||
|
||||
test('--keepAliveTimeout happy path', async () => {
|
||||
const { stderr } = await runNextCommand(
|
||||
['start', '--keepAliveTimeout', '100'],
|
||||
{
|
||||
stderr: true,
|
||||
}
|
||||
)
|
||||
expect(stderr).not.toContain(
|
||||
'Invalid keep alive timeout provided, expected a non negative number'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('export', () => {
|
||||
test('--help', async () => {
|
||||
const help = await runNextCommand(['export', '--help'], {
|
||||
|
|
|
@ -110,6 +110,12 @@ export class Playwright extends BrowserInterface {
|
|||
await oldPage.close()
|
||||
}
|
||||
page = await context.newPage()
|
||||
|
||||
// in development compilation can take longer due to
|
||||
// lower CPU availability in GH actions
|
||||
page.setDefaultTimeout(60 * 1000)
|
||||
page.setDefaultNavigationTimeout(60 * 1000)
|
||||
|
||||
pageLogs = []
|
||||
websocketFrames = []
|
||||
|
||||
|
|
Loading…
Reference in a new issue