Test lint config after first-time setup (#67146)

This commit is contained in:
Sebastian Silbermann 2024-06-25 16:13:56 +02:00 committed by GitHub
parent ebc2c68c4c
commit 63908fcd2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 580 additions and 17 deletions

View file

@ -4,7 +4,6 @@ import { join } from 'path'
import { nextBuild } from 'next-test-utils'
const dirFirstTimeSetup = join(__dirname, '../first-time-setup')
const dirCustomConfig = join(__dirname, '../custom-config')
const dirIgnoreDuringBuilds = join(__dirname, '../ignore-during-builds')
const dirBaseDirectories = join(__dirname, '../base-directories')
@ -23,22 +22,6 @@ describe('Next Build', () => {
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
test('first time setup', async () => {
const eslintrcJson = join(dirFirstTimeSetup, '.eslintrc.json')
await fs.writeFile(eslintrcJson, '')
const { stdout, stderr } = await nextBuild(dirFirstTimeSetup, [], {
stdout: true,
stderr: true,
lint: true,
})
const output = stdout + stderr
expect(output).toContain(
'No ESLint configuration detected. Run next lint to begin setup'
)
})
test('shows warnings and errors', async () => {
const { stdout, stderr } = await nextBuild(dirCustomConfig, [], {
stdout: true,

View file

@ -0,0 +1,7 @@
export default function Test() {
return (
<div>
<h1>Hello title</h1>
</div>
)
}

View file

@ -0,0 +1,7 @@
export default function Test() {
return (
<div>
<h1>Hello title</h1>
</div>
)
}

View file

@ -0,0 +1,402 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Next Build production mode first time setup 1`] = `
{
"env": {
"browser": true,
"node": true,
},
"globals": {},
"ignorePatterns": [],
"parserOptions": {
"allowImportExportEverywhere": true,
"babelOptions": {
"caller": {
"supportsTopLevelAwait": true,
},
"presets": [
"next/babel",
],
},
"ecmaFeatures": {
"jsx": true,
},
"requireConfigFile": false,
"sourceType": "module",
},
"plugins": [
"react-hooks",
"jsx-a11y",
"react",
"import",
"@next/next",
],
"rules": {
"@next/next/google-font-display": [
"warn",
],
"@next/next/google-font-preconnect": [
"warn",
],
"@next/next/inline-script-id": [
"error",
],
"@next/next/next-script-for-ga": [
"warn",
],
"@next/next/no-assign-module-variable": [
"error",
],
"@next/next/no-async-client-component": [
"warn",
],
"@next/next/no-before-interactive-script-outside-document": [
"warn",
],
"@next/next/no-css-tags": [
"warn",
],
"@next/next/no-document-import-in-page": [
"error",
],
"@next/next/no-duplicate-head": [
"error",
],
"@next/next/no-head-element": [
"warn",
],
"@next/next/no-head-import-in-document": [
"error",
],
"@next/next/no-html-link-for-pages": [
"error",
],
"@next/next/no-img-element": [
"warn",
],
"@next/next/no-page-custom-font": [
"warn",
],
"@next/next/no-script-component-in-head": [
"error",
],
"@next/next/no-styled-jsx-in-document": [
"warn",
],
"@next/next/no-sync-scripts": [
"error",
],
"@next/next/no-title-in-document-head": [
"warn",
],
"@next/next/no-typos": [
"warn",
],
"@next/next/no-unwanted-polyfillio": [
"warn",
],
"import/no-anonymous-default-export": [
"warn",
],
"jsx-a11y/alt-text": [
"warn",
{
"elements": [
"img",
],
"img": [
"Image",
],
},
],
"jsx-a11y/aria-props": [
"warn",
],
"jsx-a11y/aria-proptypes": [
"warn",
],
"jsx-a11y/aria-unsupported-elements": [
"warn",
],
"jsx-a11y/role-has-required-aria-props": [
"warn",
],
"jsx-a11y/role-supports-aria-props": [
"warn",
],
"react-hooks/exhaustive-deps": [
"warn",
],
"react-hooks/rules-of-hooks": [
"error",
],
"react/display-name": [
2,
],
"react/jsx-key": [
2,
],
"react/jsx-no-comment-textnodes": [
2,
],
"react/jsx-no-duplicate-props": [
2,
],
"react/jsx-no-target-blank": [
"off",
],
"react/jsx-no-undef": [
2,
],
"react/jsx-uses-react": [
2,
],
"react/jsx-uses-vars": [
2,
],
"react/no-children-prop": [
2,
],
"react/no-danger-with-children": [
2,
],
"react/no-deprecated": [
2,
],
"react/no-direct-mutation-state": [
2,
],
"react/no-find-dom-node": [
2,
],
"react/no-is-mounted": [
2,
],
"react/no-render-return-value": [
2,
],
"react/no-string-refs": [
2,
],
"react/no-unescaped-entities": [
2,
],
"react/no-unknown-property": [
"off",
],
"react/no-unsafe": [
0,
],
"react/prop-types": [
"off",
],
"react/react-in-jsx-scope": [
"off",
],
"react/require-render-return": [
2,
],
},
}
`;
exports[`Next Build production mode first time setup with TypeScript 1`] = `
{
"env": {
"browser": true,
"node": true,
},
"globals": {},
"ignorePatterns": [],
"parserOptions": {
"allowImportExportEverywhere": true,
"babelOptions": {
"caller": {
"supportsTopLevelAwait": true,
},
"presets": [
"next/babel",
],
},
"ecmaFeatures": {
"jsx": true,
},
"requireConfigFile": false,
"sourceType": "module",
"warnOnUnsupportedTypeScriptVersion": true,
},
"plugins": [
"react-hooks",
"jsx-a11y",
"react",
"import",
"@next/next",
],
"rules": {
"@next/next/google-font-display": [
"warn",
],
"@next/next/google-font-preconnect": [
"warn",
],
"@next/next/inline-script-id": [
"error",
],
"@next/next/next-script-for-ga": [
"warn",
],
"@next/next/no-assign-module-variable": [
"error",
],
"@next/next/no-async-client-component": [
"warn",
],
"@next/next/no-before-interactive-script-outside-document": [
"warn",
],
"@next/next/no-css-tags": [
"warn",
],
"@next/next/no-document-import-in-page": [
"error",
],
"@next/next/no-duplicate-head": [
"error",
],
"@next/next/no-head-element": [
"warn",
],
"@next/next/no-head-import-in-document": [
"error",
],
"@next/next/no-html-link-for-pages": [
"error",
],
"@next/next/no-img-element": [
"warn",
],
"@next/next/no-page-custom-font": [
"warn",
],
"@next/next/no-script-component-in-head": [
"error",
],
"@next/next/no-styled-jsx-in-document": [
"warn",
],
"@next/next/no-sync-scripts": [
"error",
],
"@next/next/no-title-in-document-head": [
"warn",
],
"@next/next/no-typos": [
"warn",
],
"@next/next/no-unwanted-polyfillio": [
"warn",
],
"import/no-anonymous-default-export": [
"warn",
],
"jsx-a11y/alt-text": [
"warn",
{
"elements": [
"img",
],
"img": [
"Image",
],
},
],
"jsx-a11y/aria-props": [
"warn",
],
"jsx-a11y/aria-proptypes": [
"warn",
],
"jsx-a11y/aria-unsupported-elements": [
"warn",
],
"jsx-a11y/role-has-required-aria-props": [
"warn",
],
"jsx-a11y/role-supports-aria-props": [
"warn",
],
"react-hooks/exhaustive-deps": [
"warn",
],
"react-hooks/rules-of-hooks": [
"error",
],
"react/display-name": [
2,
],
"react/jsx-key": [
2,
],
"react/jsx-no-comment-textnodes": [
2,
],
"react/jsx-no-duplicate-props": [
2,
],
"react/jsx-no-target-blank": [
"off",
],
"react/jsx-no-undef": [
2,
],
"react/jsx-uses-react": [
2,
],
"react/jsx-uses-vars": [
2,
],
"react/no-children-prop": [
2,
],
"react/no-danger-with-children": [
2,
],
"react/no-deprecated": [
2,
],
"react/no-direct-mutation-state": [
2,
],
"react/no-find-dom-node": [
2,
],
"react/no-is-mounted": [
2,
],
"react/no-render-return-value": [
2,
],
"react/no-string-refs": [
2,
],
"react/no-unescaped-entities": [
2,
],
"react/no-unknown-property": [
"off",
],
"react/no-unsafe": [
0,
],
"react/prop-types": [
"off",
],
"react/react-in-jsx-scope": [
"off",
],
"react/require-render-return": [
2,
],
},
}
`;

View file

@ -0,0 +1,164 @@
import fs from 'fs-extra'
import { join } from 'path'
import { execSync } from 'child_process'
import { FileRef, createNext } from 'e2e-utils'
const dirFirstTimeSetup = join(__dirname, '../first-time-setup')
const dirFirstTimeSetupTS = join(__dirname, '../first-time-setup-ts')
describe('Next Build', () => {
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
test('first time setup', async () => {
const next = await createNext({
files: new FileRef(dirFirstTimeSetup),
skipStart: true,
})
try {
const eslintrcJsonPath = join(next.testDir, '.eslintrc.json')
await fs.writeFile(eslintrcJsonPath, '')
const nextBuildCommand = await next.build()
const buildOutput = nextBuildCommand.cliOutput
expect(buildOutput).toContain(
'No ESLint configuration detected. Run next lint to begin setup'
)
// TODO: Should we exit non-zero here if the config was created? Should we maybe even directly start linting?
expect(() => {
execSync(`pnpm next lint --strict`, {
cwd: next.testDir,
encoding: 'utf8',
})
}).toThrow('Command failed: pnpm next lint --strict')
const eslintConfigAfterSetupJSON = await execSync(
`pnpm eslint --print-config pages/index.js`,
{
cwd: next.testDir,
encoding: 'utf8',
}
)
const { parser, settings, ...eslintConfigAfterSetup } = JSON.parse(
eslintConfigAfterSetupJSON
)
expect(eslintConfigAfterSetup).toMatchSnapshot()
expect({
parser,
settings,
}).toEqual({
// parser: require.resolve('eslint-config-next')
parser: expect.stringContaining('eslint-config-next'),
settings: {
'import/parsers': expect.any(Object),
'import/resolver': expect.any(Object),
react: {
version: 'detect',
},
},
})
expect(Object.entries(settings['import/parsers'])).toEqual([
[
// require.resolve('@typescript-eslint/parser')
expect.stringContaining('@typescript-eslint/parser'),
['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
],
])
expect(Object.entries(settings['import/resolver'])).toEqual([
[
// require.resolve('eslint-import-resolver-node')
expect.stringContaining('eslint-import-resolver-node'),
{ extensions: ['.js', '.jsx', '.ts', '.tsx'] },
],
[
// require.resolve('eslint-import-resolver-typescript')
expect.stringContaining('eslint-import-resolver-typescript'),
{ alwaysTryTypes: true },
],
])
} finally {
await next.destroy()
}
})
test('first time setup with TypeScript', async () => {
const next = await createNext({
files: new FileRef(dirFirstTimeSetupTS),
skipStart: true,
})
try {
const eslintrcJsonPath = join(next.testDir, '.eslintrc.json')
await fs.writeFile(eslintrcJsonPath, '')
const nextBuildCommand = await next.build()
const buildOutput = nextBuildCommand.cliOutput
expect(buildOutput).toContain(
'No ESLint configuration detected. Run next lint to begin setup'
)
// TODO: Should we exit non-zero here if the config was created? Should we maybe even directly start linting?
expect(() => {
execSync(`pnpm next lint --strict`, {
cwd: next.testDir,
encoding: 'utf8',
})
}).toThrow('Command failed: pnpm next lint --strict')
const eslintConfigAfterSetupJSON = await execSync(
`pnpm eslint --print-config pages/index.tsx`,
{
cwd: next.testDir,
encoding: 'utf8',
}
)
const { parser, settings, ...eslintConfigAfterSetup } = JSON.parse(
eslintConfigAfterSetupJSON
)
expect(eslintConfigAfterSetup).toMatchSnapshot()
expect({
parser,
settings,
}).toEqual({
// parser: require.resolve('@typescript-eslint/parser')
parser: expect.stringContaining('@typescript-eslint/parser'),
settings: {
'import/parsers': expect.any(Object),
'import/resolver': expect.any(Object),
react: {
version: 'detect',
},
},
})
expect(Object.entries(settings['import/parsers'])).toEqual([
[
// require.resolve('@typescript-eslint/parser')
expect.stringContaining('@typescript-eslint/parser'),
['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
],
])
expect(Object.entries(settings['import/resolver'])).toEqual([
[
// require.resolve('eslint-import-resolver-node')
expect.stringContaining('eslint-import-resolver-node'),
{ extensions: ['.js', '.jsx', '.ts', '.tsx'] },
],
[
// require.resolve('eslint-import-resolver-typescript')
expect.stringContaining('eslint-import-resolver-typescript'),
{ alwaysTryTypes: true },
],
])
} finally {
await next.destroy()
}
})
}
)
})