diff --git a/CODEOWNERS b/CODEOWNERS
index c864973e8d5a69..f5ddf0e3bba7a3 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -36,7 +36,8 @@ pnpm-lock.yaml @nrwl/nx-core-reviewers
/e2e/react-core/** @nrwl/nx-react-reviewers
/e2e/react-extensions/** @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/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
diff --git a/e2e/next/jest.config.ts b/e2e/next-core/jest.config.ts
similarity index 91%
rename from e2e/next/jest.config.ts
rename to e2e/next-core/jest.config.ts
index 50c96a9e5ab47a..ac14e93c131256 100644
--- a/e2e/next/jest.config.ts
+++ b/e2e/next-core/jest.config.ts
@@ -8,6 +8,6 @@ export default {
globals: {},
globalSetup: '../utils/global-setup.ts',
globalTeardown: '../utils/global-teardown.ts',
- displayName: 'e2e-next',
+ displayName: 'e2e-next-core',
preset: '../../jest.preset.js',
};
diff --git a/e2e/next/project.json b/e2e/next-core/project.json
similarity index 72%
rename from e2e/next/project.json
rename to e2e/next-core/project.json
index 71081e6cd49f33..8fd9a8748c6ee6 100644
--- a/e2e/next/project.json
+++ b/e2e/next-core/project.json
@@ -1,7 +1,7 @@
{
- "name": "e2e-next",
+ "name": "e2e-next-core",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "e2e/next",
+ "sourceRoot": "e2e/next-core/src",
"projectType": "application",
"targets": {
"e2e": {}
diff --git a/e2e/next/src/next-appdir.test.ts b/e2e/next-core/src/next-appdir.test.ts
similarity index 93%
rename from e2e/next/src/next-appdir.test.ts
rename to e2e/next-core/src/next-appdir.test.ts
index 3b00e2a56a087a..be0054f1369b67 100644
--- a/e2e/next/src/next-appdir.test.ts
+++ b/e2e/next-core/src/next-appdir.test.ts
@@ -11,13 +11,9 @@ import { checkApp } from './utils';
describe('Next.js App Router', () => {
let proj: string;
- beforeEach(() => {
- proj = newProject();
- });
+ beforeAll(() => (proj = newProject()));
- afterEach(() => {
- cleanupProject();
- });
+ afterAll(() => cleanupProject());
it('should be able to generate and build app with default App Router', async () => {
const appName = uniq('app');
diff --git a/e2e/next-core/src/next-lock-file.test.ts b/e2e/next-core/src/next-lock-file.test.ts
new file mode 100644
index 00000000000000..ae190babdb68db
--- /dev/null
+++ b/e2e/next-core/src/next-lock-file.test.ts
@@ -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);
+});
diff --git a/e2e/next-core/src/next-structure.test.ts b/e2e/next-core/src/next-structure.test.ts
new file mode 100644
index 00000000000000..272648d818d7c2
--- /dev/null
+++ b/e2e/next-core/src/next-structure.test.ts
@@ -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(
+ ``,
+ `
+
+ {jsLib()}
+ {buildableLib()}
+
+
+ `
+ )}`
+ );
+
+ 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 --prod` works with previous production build (e.g. `nx build `).
+ 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);
+});
diff --git a/e2e/next-core/src/next-webpack.test.ts b/e2e/next-core/src/next-webpack.test.ts
new file mode 100644
index 00000000000000..b1c956d64e622c
--- /dev/null
+++ b/e2e/next-core/src/next-webpack.test.ts
@@ -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);
+});
diff --git a/e2e/next/src/next.test.ts b/e2e/next-core/src/next.test.ts
similarity index 50%
rename from e2e/next/src/next.test.ts
rename to e2e/next-core/src/next.test.ts
index 710054d1875b16..f347914944c869 100644
--- a/e2e/next/src/next.test.ts
+++ b/e2e/next-core/src/next.test.ts
@@ -1,217 +1,35 @@
-import { detectPackageManager, joinPathFragments } from '@nx/devkit';
-import { capitalize } from '@nx/devkit/src/utils/string-utils';
import {
checkFilesDoNotExist,
checkFilesExist,
cleanupProject,
- getPackageManagerCommand,
- isNotWindows,
killPort,
killPorts,
newProject,
- packageManagerLockFile,
readFile,
- rmDist,
runCLI,
- runCommand,
runCommandUntil,
- tmpProjPath,
uniq,
updateFile,
- updateProjectConfig,
} from '@nx/e2e/utils';
import * as http from 'http';
import { checkApp } from './utils';
-import { removeSync, mkdirSync } from 'fs-extra';
describe('Next.js Applications', () => {
let proj: string;
let originalEnv: string;
- let packageManager;
- beforeEach(() => {
+ beforeAll(() => {
proj = newProject();
- packageManager = detectPackageManager(tmpProjPath());
+ });
+ beforeEach(() => {
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(
- ``,
- `
-
- {jsLib()}
- {buildableLib()}
-
-
- `
- )}`
- );
-
- 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 --prod` works with previous production build (e.g. `nx build `).
- 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);
+ afterAll(() => cleanupProject());
it('should support generating projects with the new name and root format', () => {
const appName = uniq('app1');
@@ -254,20 +72,6 @@ describe('Next.js Applications', () => {
);
}, 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', () => {
const appName = uniq('app');
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
@@ -348,82 +152,10 @@ describe('Next.js Applications', () => {
expect(apiData).toContain(`Welcome`);
expect(pageData).toContain(`test value from a file`);
+ await killPort(port);
await killPorts();
}, 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 () => {
const appName = uniq('app');
@@ -563,6 +295,7 @@ describe('Next.js Applications', () => {
}
);
selfContainedProcess.kill();
+ await killPort(port);
await killPorts();
}, 300_000);
});
diff --git a/e2e/next/src/utils.ts b/e2e/next-core/src/utils.ts
similarity index 100%
rename from e2e/next/src/utils.ts
rename to e2e/next-core/src/utils.ts
diff --git a/e2e/next/tsconfig.json b/e2e/next-core/tsconfig.json
similarity index 100%
rename from e2e/next/tsconfig.json
rename to e2e/next-core/tsconfig.json
diff --git a/e2e/next/tsconfig.spec.json b/e2e/next-core/tsconfig.spec.json
similarity index 100%
rename from e2e/next/tsconfig.spec.json
rename to e2e/next-core/tsconfig.spec.json
diff --git a/e2e/next-extensions/jest.config.ts b/e2e/next-extensions/jest.config.ts
new file mode 100644
index 00000000000000..8fa6724cf219d5
--- /dev/null
+++ b/e2e/next-extensions/jest.config.ts
@@ -0,0 +1,13 @@
+/* eslint-disable */
+export default {
+ displayName: 'e2e-next-extensions',
+ transform: {
+ '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/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',
+};
diff --git a/e2e/next-extensions/project.json b/e2e/next-extensions/project.json
new file mode 100644
index 00000000000000..83d41adbdd9d82
--- /dev/null
+++ b/e2e/next-extensions/project.json
@@ -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"]
+}
diff --git a/e2e/next/src/next-component-tests.test.ts b/e2e/next-extensions/src/next-component-tests.test.ts
similarity index 100%
rename from e2e/next/src/next-component-tests.test.ts
rename to e2e/next-extensions/src/next-component-tests.test.ts
diff --git a/e2e/next/src/next-experimental.test.ts b/e2e/next-extensions/src/next-experimental.test.ts
similarity index 94%
rename from e2e/next/src/next-experimental.test.ts
rename to e2e/next-extensions/src/next-experimental.test.ts
index de0778b490c056..91062d7a035ff1 100644
--- a/e2e/next/src/next-experimental.test.ts
+++ b/e2e/next-extensions/src/next-experimental.test.ts
@@ -10,13 +10,9 @@ import { checkApp } from './utils';
describe('Next.js Experimental Features', () => {
let proj: string;
- beforeEach(() => {
- proj = newProject();
- });
+ beforeAll(() => (proj = newProject()));
- afterEach(() => {
- cleanupProject();
- });
+ afterAll(() => cleanupProject());
it('should be able to define server actions in workspace libs', async () => {
const appName = uniq('app');
diff --git a/e2e/next/src/next-storybook.test.ts b/e2e/next-extensions/src/next-storybook.test.ts
similarity index 82%
rename from e2e/next/src/next-storybook.test.ts
rename to e2e/next-extensions/src/next-storybook.test.ts
index e5abbfae9dc750..23fdc2e2e5ef61 100644
--- a/e2e/next/src/next-storybook.test.ts
+++ b/e2e/next-extensions/src/next-storybook.test.ts
@@ -9,17 +9,12 @@ import {
updateJson,
} from '@nx/e2e/utils';
-describe('Next.js Applications', () => {
+describe('Next.js Storybook', () => {
let proj: string;
- beforeEach(() => {
- proj = newProject({
- name: 'proj',
- packageManager: 'npm',
- });
- });
+ beforeAll(() => (proj = newProject({ name: 'proj', packageManager: 'npm' })));
- afterEach(() => cleanupProject());
+ afterAll(() => cleanupProject());
it('should run a Next.js based Storybook setup', async () => {
const appName = uniq('app');
diff --git a/e2e/next/src/next-styles.test.ts b/e2e/next-extensions/src/next-styles.test.ts
similarity index 98%
rename from e2e/next/src/next-styles.test.ts
rename to e2e/next-extensions/src/next-styles.test.ts
index 5ba0700d4e5e96..19b5f15f270711 100644
--- a/e2e/next/src/next-styles.test.ts
+++ b/e2e/next-extensions/src/next-styles.test.ts
@@ -1,7 +1,7 @@
import { cleanupProject, newProject, runCLI, uniq } from '@nx/e2e/utils';
import { checkApp } from './utils';
-describe('Next.js apps', () => {
+describe('Next.js Styles', () => {
let originalEnv: string;
beforeAll(() => {
diff --git a/e2e/next-extensions/src/utils.ts b/e2e/next-extensions/src/utils.ts
new file mode 100644
index 00000000000000..028fcbbb8e66f6
--- /dev/null
+++ b/e2e/next-extensions/src/utils.ts
@@ -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`);
+ }
+}
diff --git a/e2e/next-extensions/tsconfig.json b/e2e/next-extensions/tsconfig.json
new file mode 100644
index 00000000000000..6d5abf84832009
--- /dev/null
+++ b/e2e/next-extensions/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "types": ["node", "jest"]
+ },
+ "include": [],
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ]
+}
diff --git a/e2e/next-extensions/tsconfig.spec.json b/e2e/next-extensions/tsconfig.spec.json
new file mode 100644
index 00000000000000..1a24bfb0a13536
--- /dev/null
+++ b/e2e/next-extensions/tsconfig.spec.json
@@ -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"
+ ]
+}