Skip to content

Commit

Permalink
Re-enable and fix create-next-app integration tests (#68084)
Browse files Browse the repository at this point in the history
These tests were disabled as part of #65845, probably due to mismatching peer dependency errors. This has been resolved in the meantime.

As part of this PR we're also fixing how the tests are installing Next.js in preparation for running `create-next-app`:

The `run-tests.js` script already calls `createNextInstall` and does all the heavy lifting. Calling it again in the `create-next-app` integration tests is wasteful.

In addition, the built `next` tarball is exposed by `run-tests.js` via the `NEXT_TEST_PKG_PATHS` env variable. We need to provide the filename of the tgz as `NEXT_PRIVATE_TEST_VERSION` so that `create-next-app` uses it in the generated `package.json` as the `next` dependency "version".

With the previous approach, the installation was pointing `next` at the `installDir`, which has its own version of React installed. This created runtime errors (`TypeError: Cannot read properties of null (reading 'useContext')`) when running `next dev` in a pages project that was created with `create-next-app`.

Some of the tests were even downloading a published `canary` version of `next` instead of using the version from the current branch. This has been fixed as well.
  • Loading branch information
unstubbable committed Jul 25, 2024
1 parent 19d66ef commit eba4b31
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 215 deletions.
7 changes: 6 additions & 1 deletion packages/create-next-app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ const program = new Command(packageJson.name)
.argument('[project-directory]')
.usage(`${green('[project-directory]')} [options]`)
.action((name) => {
projectPath = name
// Commander does not implicitly support negated options. When they are used
// by the user they will be interpreted as the positional argument (name) in
// the action handler. See https://github.com/tj/commander.js/pull/1355
if (name && !name.startsWith('--no-')) {
projectPath = name
}
})
.option(
'--ts, --typescript',
Expand Down
41 changes: 23 additions & 18 deletions test/integration/create-next-app/examples.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { trace } from 'next/dist/trace'
import { createNextInstall } from '../../lib/create-next-install'
import {
EXAMPLE_PATH,
EXAMPLE_REPO,
Expand All @@ -11,20 +9,27 @@ import {
useTempDir,
} from './utils'

describe.skip('create-next-app --example', () => {
let nextInstall: Awaited<ReturnType<typeof createNextInstall>>
beforeAll(async () => {
nextInstall = await createNextInstall({
parentSpan: trace('test'),
keepRepoDir: Boolean(process.env.NEXT_TEST_SKIP_CLEANUP),
})
describe('create-next-app --example', () => {
let nextTgzFilename: string

beforeAll(() => {
if (!process.env.NEXT_TEST_PKG_PATHS) {
throw new Error('This test needs to be run with `node run-tests.js`.')
}

const pkgPaths = new Map<string, string>(
JSON.parse(process.env.NEXT_TEST_PKG_PATHS)
)

nextTgzFilename = pkgPaths.get('next')
})

it('should create on valid Next.js example name', async () => {
await useTempDir(async (cwd) => {
const projectName = 'valid-example'
const res = await run(
[projectName, '--example', 'basic-css'],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand All @@ -49,7 +54,7 @@ describe.skip('create-next-app --example', () => {
const projectName = 'github-url'
const res = await run(
[projectName, '--example', FULL_EXAMPLE_PATH],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -81,7 +86,7 @@ describe.skip('create-next-app --example', () => {
// GH#39665
'https://github.com/vercel/nextjs-portfolio-starter/',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -115,7 +120,7 @@ describe.skip('create-next-app --example', () => {
'--example-path',
EXAMPLE_PATH,
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -150,7 +155,7 @@ describe.skip('create-next-app --example', () => {
'--example-path',
EXAMPLE_PATH,
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -188,7 +193,7 @@ describe.skip('create-next-app --example', () => {
'__internal-testing-retry',
'--import-alias=@/*',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
input: '\n', // 'Yes' to retry
Expand Down Expand Up @@ -220,7 +225,7 @@ describe.skip('create-next-app --example', () => {
'default',
'--import-alias=@/*',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand All @@ -241,7 +246,7 @@ describe.skip('create-next-app --example', () => {
const projectName = 'invalid-example'
const res = await run(
[projectName, '--example', 'not a real example'],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
reject: false,
Expand Down Expand Up @@ -270,7 +275,7 @@ describe.skip('create-next-app --example', () => {
'--no-tailwind',
'--example',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
reject: false,
Expand Down
29 changes: 17 additions & 12 deletions test/integration/create-next-app/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import {
projectFilesShouldExist,
projectFilesShouldNotExist,
} from './utils'
import { createNextInstall } from '../../lib/create-next-install'
import { trace } from 'next/dist/trace'

let nextInstall: Awaited<ReturnType<typeof createNextInstall>>
beforeAll(async () => {
nextInstall = await createNextInstall({
parentSpan: trace('test'),
keepRepoDir: Boolean(process.env.NEXT_TEST_SKIP_CLEANUP),
describe('create-next-app', () => {
let nextTgzFilename: string

beforeAll(() => {
if (!process.env.NEXT_TEST_PKG_PATHS) {
throw new Error('This test needs to be run with `node run-tests.js`.')
}

const pkgPaths = new Map<string, string>(
JSON.parse(process.env.NEXT_TEST_PKG_PATHS)
)

nextTgzFilename = pkgPaths.get('next')
})
})

describe.skip('create-next-app', () => {
it('should not create if the target directory is not empty', async () => {
await useTempDir(async (cwd) => {
const projectName = 'non-empty-dir'
Expand All @@ -36,7 +40,7 @@ describe.skip('create-next-app', () => {
'--no-src-dir',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
reject: false,
Expand Down Expand Up @@ -71,12 +75,13 @@ describe.skip('create-next-app', () => {
projectName,
'--ts',
'--app',
'--no-turbo',
'--eslint',
'--no-tailwind',
'--no-src-dir',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
reject: false,
Expand Down Expand Up @@ -107,7 +112,7 @@ describe.skip('create-next-app', () => {
'--no-import-alias',
'--skip-install',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down
20 changes: 1 addition & 19 deletions test/integration/create-next-app/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This file contains utilities for `create-next-app` testing.
*/

import { ChildProcess, execSync, spawn, SpawnOptions } from 'child_process'
import { execSync, spawn, SpawnOptions } from 'child_process'
import { existsSync } from 'fs'
import { join, resolve } from 'path'
import glob from 'glob'
Expand Down Expand Up @@ -58,24 +58,6 @@ export const createNextApp = (
})
}

/**
* Return a Promise that resolves when the process exits with code 0 and rejects
* otherwise.
*/
export const spawnExitPromise = (childProcess: ChildProcess) => {
return new Promise((resolve, reject) => {
childProcess
.on('exit', (code) => {
if (code === 0) {
resolve(code)
} else {
reject(code)
}
})
.on('error', reject)
})
}

export const projectShouldHaveNoGitChanges = ({
cwd,
projectName,
Expand Down
38 changes: 20 additions & 18 deletions test/integration/create-next-app/package-manager/bun.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { trace } from 'next/dist/trace'
import { createNextInstall } from '../../../lib/create-next-install'
import {
command,
DEFAULT_FILES,
Expand All @@ -12,21 +10,25 @@ import {
const lockFile = 'bun.lockb'
const files = [...DEFAULT_FILES, lockFile]

beforeEach(async () => {
await command('bun', ['--version'])
// install bun if not available
.catch(() => command('npm', ['i', '-g', 'bun']))
})
describe('create-next-app with package manager bun', () => {
let nextTgzFilename: string

beforeAll(async () => {
if (!process.env.NEXT_TEST_PKG_PATHS) {
throw new Error('This test needs to be run with `node run-tests.js`.')
}

const pkgPaths = new Map<string, string>(
JSON.parse(process.env.NEXT_TEST_PKG_PATHS)
)

let nextInstall: Awaited<ReturnType<typeof createNextInstall>>
beforeAll(async () => {
nextInstall = await createNextInstall({
parentSpan: trace('test'),
keepRepoDir: Boolean(process.env.NEXT_TEST_SKIP_CLEANUP),
nextTgzFilename = pkgPaths.get('next')

await command('bun', ['--version'])
// install bun if not available
.catch(() => command('npm', ['i', '-g', 'bun']))
})
})

describe.skip('create-next-app with package manager bun', () => {
it('should use bun for --use-bun flag', async () => {
await useTempDir(async (cwd) => {
const projectName = 'use-bun'
Expand All @@ -42,7 +44,7 @@ describe.skip('create-next-app with package manager bun', () => {
'--no-tailwind',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -71,7 +73,7 @@ describe.skip('create-next-app with package manager bun', () => {
'--no-tailwind',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
env: { npm_config_user_agent: 'bun' },
Expand All @@ -92,7 +94,7 @@ describe.skip('create-next-app with package manager bun', () => {
const projectName = 'use-bun-with-example'
const res = await run(
[projectName, '--use-bun', '--example', FULL_EXAMPLE_PATH],
nextInstall.installDir,
nextTgzFilename,
{ cwd }
)

Expand All @@ -110,7 +112,7 @@ describe.skip('create-next-app with package manager bun', () => {
const projectName = 'user-agent-bun-with-example'
const res = await run(
[projectName, '--example', FULL_EXAMPLE_PATH],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
env: { npm_config_user_agent: 'bun' },
Expand Down
30 changes: 17 additions & 13 deletions test/integration/create-next-app/package-manager/npm.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { trace } from 'next/dist/trace'
import {
DEFAULT_FILES,
FULL_EXAMPLE_PATH,
projectFilesShouldExist,
run,
useTempDir,
} from '../utils'
import { createNextInstall } from '../../../lib/create-next-install'

const lockFile = 'package-lock.json'
const files = [...DEFAULT_FILES, lockFile]

let nextInstall: Awaited<ReturnType<typeof createNextInstall>>
beforeAll(async () => {
nextInstall = await createNextInstall({
parentSpan: trace('test'),
keepRepoDir: Boolean(process.env.NEXT_TEST_SKIP_CLEANUP),
describe('create-next-app with package manager npm', () => {
let nextTgzFilename: string

beforeAll(() => {
if (!process.env.NEXT_TEST_PKG_PATHS) {
throw new Error('This test needs to be run with `node run-tests.js`.')
}

const pkgPaths = new Map<string, string>(
JSON.parse(process.env.NEXT_TEST_PKG_PATHS)
)

nextTgzFilename = pkgPaths.get('next')
})
})

describe.skip('create-next-app with package manager npm', () => {
it('should use npm for --use-npm flag', async () => {
await useTempDir(async (cwd) => {
const projectName = 'use-npm'
Expand All @@ -35,7 +39,7 @@ describe.skip('create-next-app with package manager npm', () => {
'--no-tailwind',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
}
Expand Down Expand Up @@ -64,7 +68,7 @@ describe.skip('create-next-app with package manager npm', () => {
'--no-tailwind',
'--no-import-alias',
],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
env: { npm_config_user_agent: 'npm' },
Expand All @@ -85,7 +89,7 @@ describe.skip('create-next-app with package manager npm', () => {
const projectName = 'use-npm-with-example'
const res = await run(
[projectName, '--use-npm', '--example', FULL_EXAMPLE_PATH],
nextInstall.installDir,
nextTgzFilename,
{ cwd }
)

Expand All @@ -103,7 +107,7 @@ describe.skip('create-next-app with package manager npm', () => {
const projectName = 'user-agent-npm-with-example'
const res = await run(
[projectName, '--example', FULL_EXAMPLE_PATH],
nextInstall.installDir,
nextTgzFilename,
{
cwd,
env: { npm_config_user_agent: 'npm' },
Expand Down
Loading

0 comments on commit eba4b31

Please sign in to comment.