fix(nextjs): improve e2e test by reusing the project (#18827)

This commit is contained in:
Nicholas Cunningham 2023-08-25 14:15:52 -06:00 committed by GitHub
parent ed89a21e97
commit 34a727926b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 476 additions and 298 deletions

View File

@ -36,7 +36,8 @@ pnpm-lock.yaml @nrwl/nx-core-reviewers
/e2e/react-core/** @nrwl/nx-react-reviewers /e2e/react-core/** @nrwl/nx-react-reviewers
/e2e/react-extensions/** @nrwl/nx-react-reviewers /e2e/react-extensions/** @nrwl/nx-react-reviewers
/packages/next/** @nrwl/nx-react-reviewers /packages/next/** @nrwl/nx-react-reviewers
/e2e/next/** @nrwl/nx-react-reviewers /e2e/next-core/** @nrwl/nx-react-reviewers
/e2e/next-extensions/** @nrwl/nx-react-reviewers
/packages/react/plugins/component-testing/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers /packages/react/plugins/component-testing/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers
/packages/react/src/generators/cypress-component-configuration/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers /packages/react/src/generators/cypress-component-configuration/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers
/packages/react/src/generators/component-test/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers /packages/react/src/generators/component-test/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers

View File

@ -8,6 +8,6 @@ export default {
globals: {}, globals: {},
globalSetup: '../utils/global-setup.ts', globalSetup: '../utils/global-setup.ts',
globalTeardown: '../utils/global-teardown.ts', globalTeardown: '../utils/global-teardown.ts',
displayName: 'e2e-next', displayName: 'e2e-next-core',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
}; };

View File

@ -1,7 +1,7 @@
{ {
"name": "e2e-next", "name": "e2e-next-core",
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/next", "sourceRoot": "e2e/next-core/src",
"projectType": "application", "projectType": "application",
"targets": { "targets": {
"e2e": {} "e2e": {}

View File

@ -11,13 +11,9 @@ import { checkApp } from './utils';
describe('Next.js App Router', () => { describe('Next.js App Router', () => {
let proj: string; let proj: string;
beforeEach(() => { beforeAll(() => (proj = newProject()));
proj = newProject();
});
afterEach(() => { afterAll(() => cleanupProject());
cleanupProject();
});
it('should be able to generate and build app with default App Router', async () => { it('should be able to generate and build app with default App Router', async () => {
const appName = uniq('app'); const appName = uniq('app');

View File

@ -0,0 +1,43 @@
import { detectPackageManager, joinPathFragments } from '@nx/devkit';
import {
checkFilesExist,
cleanupProject,
getPackageManagerCommand,
newProject,
packageManagerLockFile,
runCLI,
runCommand,
tmpProjPath,
uniq,
} from '@nx/e2e/utils';
describe('Next.js Lock File', () => {
let proj: string;
let originalEnv: string;
let packageManager;
beforeEach(() => {
proj = newProject();
packageManager = detectPackageManager(tmpProjPath());
originalEnv = process.env.NODE_ENV;
});
afterEach(() => {
process.env.NODE_ENV = originalEnv;
cleanupProject();
});
it('should build and install pruned lock file', () => {
const appName = uniq('app');
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
const result = runCLI(`build ${appName} --generateLockfile=true`);
expect(result).not.toMatch(/Graph is not consistent/);
checkFilesExist(
`dist/apps/${appName}/${packageManagerLockFile[packageManager]}`
);
runCommand(`${getPackageManagerCommand().ciInstall}`, {
cwd: joinPathFragments(tmpProjPath(), 'dist/apps', appName),
});
}, 1_000_000);
});

View File

@ -0,0 +1,205 @@
import { removeSync, mkdirSync } from 'fs-extra';
import { capitalize } from '@nx/devkit/src/utils/string-utils';
import { checkApp } from './utils';
import {
checkFilesExist,
cleanupProject,
isNotWindows,
killPort,
newProject,
readFile,
runCLI,
runCommandUntil,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nx/e2e/utils';
describe('Next.js Apps Libs', () => {
let proj: string;
let originalEnv: string;
beforeEach(() => {
proj = newProject();
originalEnv = process.env.NODE_ENV;
});
afterEach(() => {
process.env.NODE_ENV = originalEnv;
cleanupProject();
});
it('should generate app + libs', async () => {
// Remove apps/libs folder and use packages.
// Allows us to test other integrated monorepo setup that had a regression.
// See: https://github.com/nrwl/nx/issues/16658
removeSync(`${tmpProjPath()}/libs`);
removeSync(`${tmpProjPath()}/apps`);
mkdirSync(`${tmpProjPath()}/packages`);
const appName = uniq('app');
const nextLib = uniq('nextlib');
const jsLib = uniq('tslib');
const buildableLib = uniq('buildablelib');
runCLI(
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
);
runCLI(`generate @nx/next:lib ${nextLib} --no-interactive`);
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
runCLI(
`generate @nx/js:lib ${buildableLib} --no-interactive --bundler=vite`
);
// Create file in public that should be copied to dist
updateFile(`packages/${appName}/public/a/b.txt`, `Hello World!`);
// Additional assets that should be copied to dist
const sharedLib = uniq('sharedLib');
await updateProjectConfig(appName, (json) => {
json.targets.build.options.assets = [
{
glob: '**/*',
input: `packages/${sharedLib}/src/assets`,
output: 'shared/ui',
},
];
return json;
});
updateFile(`packages/${sharedLib}/src/assets/hello.txt`, 'Hello World!');
// create a css file in node_modules so that it can be imported in a lib
// to test that it works as expected
updateFile(
'node_modules/@nx/next/test-styles.css',
'h1 { background-color: red; }'
);
updateFile(
`packages/${jsLib}/src/lib/${jsLib}.ts`,
`
export function jsLib(): string {
return 'Hello Nx';
};
// testing whether async-await code in Node / Next.js api routes works as expected
export async function jsLibAsync() {
return await Promise.resolve('hell0');
}
`
);
updateFile(
`packages/${buildableLib}/src/lib/${buildableLib}.ts`,
`
export function buildableLib(): string {
return 'Hello Buildable';
};
`
);
const mainPath = `packages/${appName}/pages/index.tsx`;
const content = readFile(mainPath);
updateFile(
`packages/${appName}/pages/api/hello.ts`,
`
import { jsLibAsync } from '@${proj}/${jsLib}';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default async function handler(_: any, res: any) {
const value = await jsLibAsync();
res.send(value);
}
`
);
updateFile(
mainPath,
`
import { jsLib } from '@${proj}/${jsLib}';
import { buildableLib } from '@${proj}/${buildableLib}';
/* eslint-disable */
import dynamic from 'next/dynamic';
const TestComponent = dynamic(
() => import('@${proj}/${nextLib}').then(d => d.${capitalize(
nextLib
)})
);
${content.replace(
`</h2>`,
`</h2>
<div>
{jsLib()}
{buildableLib()}
<TestComponent />
</div>
`
)}`
);
const e2eTestPath = `packages/${appName}-e2e/src/e2e/app.cy.ts`;
const e2eContent = readFile(e2eTestPath);
updateFile(
e2eTestPath,
`
${
e2eContent +
`
it('should successfully call async API route', () => {
cy.request('/api/hello').its('body').should('include', 'hell0');
});
`
}
`
);
await checkApp(appName, {
checkUnitTest: true,
checkLint: true,
checkE2E: isNotWindows(),
checkExport: false,
appsDir: 'packages',
});
// public and shared assets should both be copied to dist
checkFilesExist(
`dist/packages/${appName}/public/a/b.txt`,
`dist/packages/${appName}/public/shared/ui/hello.txt`
);
// Check that compiled next config does not contain bad imports
const nextConfigPath = `dist/packages/${appName}/next.config.js`;
expect(nextConfigPath).not.toContain(`require("../`); // missing relative paths
expect(nextConfigPath).not.toContain(`require("nx/`); // dev-only packages
expect(nextConfigPath).not.toContain(`require("@nx/`); // dev-only packages
// Check that `nx serve <app> --prod` works with previous production build (e.g. `nx build <app>`).
const prodServePort = 4001;
const prodServeProcess = await runCommandUntil(
`run ${appName}:serve --prod --port=${prodServePort}`,
(output) => {
return output.includes(`localhost:${prodServePort}`);
}
);
// Check that the output is self-contained (i.e. can run with its own package.json + node_modules)
const selfContainedPort = 3000;
runCLI(
`generate @nx/workspace:run-commands serve-prod --project ${appName} --cwd=dist/packages/${appName} --command="npx next start --port=${selfContainedPort}"`
);
const selfContainedProcess = await runCommandUntil(
`run ${appName}:serve-prod`,
(output) => {
return output.includes(`localhost:${selfContainedPort}`);
}
);
prodServeProcess.kill();
selfContainedProcess.kill();
await killPort(prodServePort);
await killPort(selfContainedPort);
}, 600_000);
});

View File

@ -0,0 +1,98 @@
import {
checkFilesExist,
cleanupProject,
newProject,
rmDist,
runCLI,
uniq,
updateFile,
updateProjectConfig,
} from '@nx/e2e/utils';
describe('Next.js Webpack', () => {
let proj: string;
let originalEnv: string;
beforeEach(() => {
proj = newProject();
originalEnv = process.env.NODE_ENV;
});
afterEach(() => {
process.env.NODE_ENV = originalEnv;
cleanupProject();
});
it('should support custom webpack and run-commands using withNx', async () => {
const appName = uniq('app');
runCLI(
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
);
updateFile(
`apps/${appName}/next.config.js`,
`
const { withNx } = require('@nx/next');
const nextConfig = {
nx: {
svgr: false,
},
webpack: (config, context) => {
// Make sure SVGR plugin is disabled if nx.svgr === false (see above)
const found = config.module.rules.find(r => {
if (!r.test || !r.test.test('test.svg')) return false;
if (!r.oneOf || !r.oneOf.use) return false;
return r.oneOf.use.some(rr => /svgr/.test(rr.loader));
});
if (found) throw new Error('Found SVGR plugin');
console.log('NODE_ENV is', process.env.NODE_ENV);
return config;
}
};
module.exports = withNx(nextConfig);
`
);
// deleting `NODE_ENV` value, so that it's `undefined`, and not `"test"`
// by the time it reaches the build executor.
// this simulates existing behaviour of running a next.js build executor via Nx
delete process.env.NODE_ENV;
const result = runCLI(`build ${appName}`);
checkFilesExist(`dist/apps/${appName}/next.config.js`);
expect(result).toContain('NODE_ENV is production');
updateFile(
`apps/${appName}/next.config.js`,
`
const { withNx } = require('@nx/next');
// Not including "nx" entry should still work.
const nextConfig = {};
module.exports = withNx(nextConfig);
`
);
rmDist();
runCLI(`build ${appName}`);
checkFilesExist(`dist/apps/${appName}/next.config.js`);
// Make sure withNx works with run-commands.
await updateProjectConfig(appName, (json) => {
json.targets.build = {
command: 'npx next build',
outputs: [`apps/${appName}/.next`],
options: {
cwd: `apps/${appName}`,
},
};
return json;
});
expect(() => {
runCLI(`build ${appName}`);
}).not.toThrow();
checkFilesExist(`apps/${appName}/.next/build-manifest.json`);
}, 300_000);
});

View File

@ -1,217 +1,35 @@
import { detectPackageManager, joinPathFragments } from '@nx/devkit';
import { capitalize } from '@nx/devkit/src/utils/string-utils';
import { import {
checkFilesDoNotExist, checkFilesDoNotExist,
checkFilesExist, checkFilesExist,
cleanupProject, cleanupProject,
getPackageManagerCommand,
isNotWindows,
killPort, killPort,
killPorts, killPorts,
newProject, newProject,
packageManagerLockFile,
readFile, readFile,
rmDist,
runCLI, runCLI,
runCommand,
runCommandUntil, runCommandUntil,
tmpProjPath,
uniq, uniq,
updateFile, updateFile,
updateProjectConfig,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';
import * as http from 'http'; import * as http from 'http';
import { checkApp } from './utils'; import { checkApp } from './utils';
import { removeSync, mkdirSync } from 'fs-extra';
describe('Next.js Applications', () => { describe('Next.js Applications', () => {
let proj: string; let proj: string;
let originalEnv: string; let originalEnv: string;
let packageManager;
beforeEach(() => { beforeAll(() => {
proj = newProject(); proj = newProject();
packageManager = detectPackageManager(tmpProjPath()); });
beforeEach(() => {
originalEnv = process.env.NODE_ENV; originalEnv = process.env.NODE_ENV;
}); });
afterEach(() => { afterEach(() => {
process.env.NODE_ENV = originalEnv; process.env.NODE_ENV = originalEnv;
cleanupProject();
}); });
it('should generate app + libs', async () => { afterAll(() => cleanupProject());
// Remove apps/libs folder and use packages.
// Allows us to test other integrated monorepo setup that had a regression.
// See: https://github.com/nrwl/nx/issues/16658
removeSync(`${tmpProjPath()}/libs`);
removeSync(`${tmpProjPath()}/apps`);
mkdirSync(`${tmpProjPath()}/packages`);
const appName = uniq('app');
const nextLib = uniq('nextlib');
const jsLib = uniq('tslib');
const buildableLib = uniq('buildablelib');
runCLI(
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
);
runCLI(`generate @nx/next:lib ${nextLib} --no-interactive`);
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
runCLI(
`generate @nx/js:lib ${buildableLib} --no-interactive --bundler=vite`
);
// Create file in public that should be copied to dist
updateFile(`packages/${appName}/public/a/b.txt`, `Hello World!`);
// Additional assets that should be copied to dist
const sharedLib = uniq('sharedLib');
await updateProjectConfig(appName, (json) => {
json.targets.build.options.assets = [
{
glob: '**/*',
input: `packages/${sharedLib}/src/assets`,
output: 'shared/ui',
},
];
return json;
});
updateFile(`packages/${sharedLib}/src/assets/hello.txt`, 'Hello World!');
// create a css file in node_modules so that it can be imported in a lib
// to test that it works as expected
updateFile(
'node_modules/@nx/next/test-styles.css',
'h1 { background-color: red; }'
);
updateFile(
`packages/${jsLib}/src/lib/${jsLib}.ts`,
`
export function jsLib(): string {
return 'Hello Nx';
};
// testing whether async-await code in Node / Next.js api routes works as expected
export async function jsLibAsync() {
return await Promise.resolve('hell0');
}
`
);
updateFile(
`packages/${buildableLib}/src/lib/${buildableLib}.ts`,
`
export function buildableLib(): string {
return 'Hello Buildable';
};
`
);
const mainPath = `packages/${appName}/pages/index.tsx`;
const content = readFile(mainPath);
updateFile(
`packages/${appName}/pages/api/hello.ts`,
`
import { jsLibAsync } from '@${proj}/${jsLib}';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default async function handler(_: any, res: any) {
const value = await jsLibAsync();
res.send(value);
}
`
);
updateFile(
mainPath,
`
import { jsLib } from '@${proj}/${jsLib}';
import { buildableLib } from '@${proj}/${buildableLib}';
/* eslint-disable */
import dynamic from 'next/dynamic';
const TestComponent = dynamic(
() => import('@${proj}/${nextLib}').then(d => d.${capitalize(
nextLib
)})
);
${content.replace(
`</h2>`,
`</h2>
<div>
{jsLib()}
{buildableLib()}
<TestComponent />
</div>
`
)}`
);
const e2eTestPath = `packages/${appName}-e2e/src/e2e/app.cy.ts`;
const e2eContent = readFile(e2eTestPath);
updateFile(
e2eTestPath,
`
${
e2eContent +
`
it('should successfully call async API route', () => {
cy.request('/api/hello').its('body').should('include', 'hell0');
});
`
}
`
);
await checkApp(appName, {
checkUnitTest: true,
checkLint: true,
checkE2E: isNotWindows(),
checkExport: false,
appsDir: 'packages',
});
// public and shared assets should both be copied to dist
checkFilesExist(
`dist/packages/${appName}/public/a/b.txt`,
`dist/packages/${appName}/public/shared/ui/hello.txt`
);
// Check that compiled next config does not contain bad imports
const nextConfigPath = `dist/packages/${appName}/next.config.js`;
expect(nextConfigPath).not.toContain(`require("../`); // missing relative paths
expect(nextConfigPath).not.toContain(`require("nx/`); // dev-only packages
expect(nextConfigPath).not.toContain(`require("@nx/`); // dev-only packages
// Check that `nx serve <app> --prod` works with previous production build (e.g. `nx build <app>`).
const prodServePort = 4000;
const prodServeProcess = await runCommandUntil(
`run ${appName}:serve --prod --port=${prodServePort}`,
(output) => {
return output.includes(`localhost:${prodServePort}`);
}
);
// Check that the output is self-contained (i.e. can run with its own package.json + node_modules)
const selfContainedPort = 3000;
runCLI(
`generate @nx/workspace:run-commands serve-prod --project ${appName} --cwd=dist/packages/${appName} --command="npx next start --port=${selfContainedPort}"`
);
const selfContainedProcess = await runCommandUntil(
`run ${appName}:serve-prod`,
(output) => {
return output.includes(`localhost:${selfContainedPort}`);
}
);
prodServeProcess.kill();
selfContainedProcess.kill();
await killPort(prodServePort);
await killPort(selfContainedPort);
}, 600_000);
it('should support generating projects with the new name and root format', () => { it('should support generating projects with the new name and root format', () => {
const appName = uniq('app1'); const appName = uniq('app1');
@ -254,20 +72,6 @@ describe('Next.js Applications', () => {
); );
}, 600_000); }, 600_000);
it('should build and install pruned lock file', () => {
const appName = uniq('app');
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
const result = runCLI(`build ${appName} --generateLockfile=true`);
expect(result).not.toMatch(/Graph is not consistent/);
checkFilesExist(
`dist/apps/${appName}/${packageManagerLockFile[packageManager]}`
);
runCommand(`${getPackageManagerCommand().ciInstall}`, {
cwd: joinPathFragments(tmpProjPath(), 'dist/apps', appName),
});
}, 1_000_000);
it('should build app and .next artifacts at the outputPath if provided by the CLI', () => { it('should build app and .next artifacts at the outputPath if provided by the CLI', () => {
const appName = uniq('app'); const appName = uniq('app');
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`); runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
@ -348,82 +152,10 @@ describe('Next.js Applications', () => {
expect(apiData).toContain(`Welcome`); expect(apiData).toContain(`Welcome`);
expect(pageData).toContain(`test value from a file`); expect(pageData).toContain(`test value from a file`);
await killPort(port);
await killPorts(); await killPorts();
}, 300_000); }, 300_000);
it('should support custom webpack and run-commands using withNx', async () => {
const appName = uniq('app');
runCLI(
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
);
updateFile(
`apps/${appName}/next.config.js`,
`
const { withNx } = require('@nx/next');
const nextConfig = {
nx: {
svgr: false,
},
webpack: (config, context) => {
// Make sure SVGR plugin is disabled if nx.svgr === false (see above)
const found = config.module.rules.find(r => {
if (!r.test || !r.test.test('test.svg')) return false;
if (!r.oneOf || !r.oneOf.use) return false;
return r.oneOf.use.some(rr => /svgr/.test(rr.loader));
});
if (found) throw new Error('Found SVGR plugin');
console.log('NODE_ENV is', process.env.NODE_ENV);
return config;
}
};
module.exports = withNx(nextConfig);
`
);
// deleting `NODE_ENV` value, so that it's `undefined`, and not `"test"`
// by the time it reaches the build executor.
// this simulates existing behaviour of running a next.js build executor via Nx
delete process.env.NODE_ENV;
const result = runCLI(`build ${appName}`);
checkFilesExist(`dist/apps/${appName}/next.config.js`);
expect(result).toContain('NODE_ENV is production');
updateFile(
`apps/${appName}/next.config.js`,
`
const { withNx } = require('@nx/next');
// Not including "nx" entry should still work.
const nextConfig = {};
module.exports = withNx(nextConfig);
`
);
rmDist();
runCLI(`build ${appName}`);
checkFilesExist(`dist/apps/${appName}/next.config.js`);
// Make sure withNx works with run-commands.
await updateProjectConfig(appName, (json) => {
json.targets.build = {
command: 'npx next build',
outputs: [`apps/${appName}/.next`],
options: {
cwd: `apps/${appName}`,
},
};
return json;
});
expect(() => {
runCLI(`build ${appName}`);
}).not.toThrow();
checkFilesExist(`apps/${appName}/.next/build-manifest.json`);
}, 300_000);
it('should build in dev mode without errors', async () => { it('should build in dev mode without errors', async () => {
const appName = uniq('app'); const appName = uniq('app');
@ -563,6 +295,7 @@ describe('Next.js Applications', () => {
} }
); );
selfContainedProcess.kill(); selfContainedProcess.kill();
await killPort(port);
await killPorts(); await killPorts();
}, 300_000); }, 300_000);
}); });

View File

@ -0,0 +1,13 @@
/* eslint-disable */
export default {
displayName: 'e2e-next-extensions',
transform: {
'^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
maxWorkers: 1,
globals: {},
globalSetup: '../utils/global-setup.ts',
globalTeardown: '../utils/global-teardown.ts',
preset: '../../jest.preset.js',
};

View File

@ -0,0 +1,10 @@
{
"name": "e2e-next-extensions",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/next-extensions/src",
"projectType": "application",
"targets": {
"e2e": {}
},
"implicitDependencies": ["next"]
}

View File

@ -10,13 +10,9 @@ import { checkApp } from './utils';
describe('Next.js Experimental Features', () => { describe('Next.js Experimental Features', () => {
let proj: string; let proj: string;
beforeEach(() => { beforeAll(() => (proj = newProject()));
proj = newProject();
});
afterEach(() => { afterAll(() => cleanupProject());
cleanupProject();
});
it('should be able to define server actions in workspace libs', async () => { it('should be able to define server actions in workspace libs', async () => {
const appName = uniq('app'); const appName = uniq('app');

View File

@ -9,17 +9,12 @@ import {
updateJson, updateJson,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';
describe('Next.js Applications', () => { describe('Next.js Storybook', () => {
let proj: string; let proj: string;
beforeEach(() => { beforeAll(() => (proj = newProject({ name: 'proj', packageManager: 'npm' })));
proj = newProject({
name: 'proj',
packageManager: 'npm',
});
});
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
it('should run a Next.js based Storybook setup', async () => { it('should run a Next.js based Storybook setup', async () => {
const appName = uniq('app'); const appName = uniq('app');

View File

@ -1,7 +1,7 @@
import { cleanupProject, newProject, runCLI, uniq } from '@nx/e2e/utils'; import { cleanupProject, newProject, runCLI, uniq } from '@nx/e2e/utils';
import { checkApp } from './utils'; import { checkApp } from './utils';
describe('Next.js apps', () => { describe('Next.js Styles', () => {
let originalEnv: string; let originalEnv: string;
beforeAll(() => { beforeAll(() => {

View File

@ -0,0 +1,55 @@
import {
checkFilesExist,
killPorts,
readJson,
runCLI,
runCLIAsync,
runE2ETests,
} from '../../utils';
export async function checkApp(
appName: string,
opts: {
checkUnitTest: boolean;
checkLint: boolean;
checkE2E: boolean;
checkExport: boolean;
appsDir?: string;
}
) {
const appsDir = opts.appsDir ?? 'apps';
if (opts.checkLint) {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
}
if (opts.checkUnitTest) {
const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.combinedOutput).toContain(
'Test Suites: 1 passed, 1 total'
);
}
const buildResult = runCLI(`build ${appName}`);
expect(buildResult).toContain(`Successfully ran target build`);
checkFilesExist(`dist/${appsDir}/${appName}/.next/build-manifest.json`);
const packageJson = readJson(`dist/${appsDir}/${appName}/package.json`);
expect(packageJson.dependencies.react).toBeDefined();
expect(packageJson.dependencies['react-dom']).toBeDefined();
expect(packageJson.dependencies.next).toBeDefined();
if (opts.checkE2E && runE2ETests()) {
const e2eResults = runCLI(
`e2e ${appName}-e2e --no-watch --configuration=production`
);
expect(e2eResults).toContain('Successfully ran target e2e for project');
expect(await killPorts()).toBeTruthy();
}
if (opts.checkExport) {
runCLI(`export ${appName}`);
checkFilesExist(`dist/${appsDir}/${appName}/exported/index.html`);
}
}

View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts",
"jest.config.ts"
]
}