Skip to content

Commit

Permalink
fix(nextjs): Add missing e2e-ci target for cypress
Browse files Browse the repository at this point in the history
  • Loading branch information
ndcunningham committed Feb 14, 2024
1 parent 343c0f6 commit 4302fb6
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ CHANGELOG.md

# Next.js
.next
out

# Angular Cache
.angular
Expand Down
26 changes: 26 additions & 0 deletions e2e/next-core/src/next.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import {
checkFilesDoNotExist,
checkFilesExist,
cleanupProject,
killPorts,
newProject,
readFile,
runCLI,
runE2ETests,
uniq,
updateFile,
} from '@nx/e2e/utils';
Expand Down Expand Up @@ -183,6 +185,30 @@ describe('Next.js Applications', () => {
`Successfully ran target build for project ${appName}`
);
}, 300_000);

it('should run e2e-ci test', async () => {
const appName = uniq('app');

// Build first so it uses cache for e2e
runCLI(
`generate @nx/next:app ${appName} --no-interactive --style=css --project-name-and-root-format=as-provided`
);

if (runE2ETests()) {
const buildResult = runCLI(`build ${appName}`);

expect(buildResult).toContain(
`Successfully ran target build for project ${appName}`
);

const e2eResults = runCLI(`e2e-ci ${appName}-e2e --verbose`, {
verbose: true,
});
expect(e2eResults).toContain(
'Successfully ran target e2e-ci for project'
);
}
}, 1_000_000);
});

function getData(port, path = ''): Promise<any> {
Expand Down
6 changes: 6 additions & 0 deletions packages/next/plugins/with-nx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ function withNx(
: joinPathFragments(outputDir, '.next');
}

// If we are running a static serve of the Next.js app, we need to change the output to 'export' and the distDir to 'out'.
if (process.env.NX_SERVE_STATIC_BUILD_RUNNING === 'true') {
nextConfig.output = 'export';
nextConfig.distDir = 'out';
}

const userWebpackConfig = nextConfig.webpack;

const { createWebpackConfig } = require('@nx/next/src/utils/config');
Expand Down
20 changes: 20 additions & 0 deletions packages/next/src/generators/application/lib/add-e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Linter } from '@nx/eslint';

import { nxVersion } from '../../../utils/versions';
import { NormalizedSchema } from './normalize-options';
import { webStaticServeGenerator } from '@nx/web';

export async function addE2e(host: Tree, options: NormalizedSchema) {
const nxJson = readNxJson(host);
Expand All @@ -18,17 +19,28 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
? p === '@nx/next/plugin'
: p.plugin === '@nx/next/plugin'
);

if (options.e2eTestRunner === 'cypress') {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);

if (!hasPlugin) {
webStaticServeGenerator(host, {
buildTarget: `${options.projectName}:build`,
outputPath: `${options.outputPath}/out`,
targetName: 'serve-static',
});
}

addProjectConfiguration(host, options.e2eProjectName, {
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});

return configurationGenerator(host, {
...options,
linter: Linter.EsLint,
Expand All @@ -40,6 +52,14 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
}`,
baseUrl: `http://localhost:${hasPlugin ? '3000' : '4200'}`,
jsx: true,
webServerCommands: hasPlugin
? {
default: `nx run ${options.projectName}:start`,
}
: undefined,
ciWebServerCommand: hasPlugin
? `nx run ${options.projectName}:serve-static`
: undefined,
});
} else if (options.e2eTestRunner === 'playwright') {
const { configurationGenerator } = ensurePackage<
Expand Down
8 changes: 8 additions & 0 deletions packages/next/src/generators/application/lib/add-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export function addProject(host: Tree, options: NormalizedSchema) {
buildTarget: `${options.projectName}:build:production`,
},
};
} else {
// We need to add the serve-static target so that the port can be configured.
targets['serve-static'] = {
executor: '@nx/web:file-server',
options: {
port: 3000,
},
};
}

const project: ProjectConfiguration = {
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/generators/init/lib/add-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function addPlugin(tree: Tree) {
buildTargetName: 'build',
devTargetName: 'dev',
startTargetName: 'start',
serveStaticTargetName: 'serve-static',
},
});

Expand Down
14 changes: 14 additions & 0 deletions packages/next/src/plugins/__snapshots__/plugin.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ exports[`@nx/next/plugin integrated projects should create nodes 1`] = `
"cwd": "my-app",
},
},
"my-serve-static": {
"executor": "@nx/web:file-server",
"options": {
"buildTarget": "my-build",
"staticFilePath": "{projectRoot}/out",
},
},
"my-start": {
"command": "next start",
"dependsOn": [
Expand Down Expand Up @@ -85,6 +92,13 @@ exports[`@nx/next/plugin root projects should create nodes 1`] = `
"cwd": ".",
},
},
"serve-static": {
"executor": "@nx/web:file-server",
"options": {
"buildTarget": "build",
"staticFilePath": "{projectRoot}/out",
},
},
"start": {
"command": "next start",
"dependsOn": [
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/plugins/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('@nx/next/plugin', () => {
buildTargetName: 'build',
devTargetName: 'dev',
startTargetName: 'start',
serveStaticTargetName: 'serve-static',
},
context
);
Expand Down Expand Up @@ -73,6 +74,7 @@ describe('@nx/next/plugin', () => {
buildTargetName: 'my-build',
devTargetName: 'my-serve',
startTargetName: 'my-start',
serveStaticTargetName: 'my-serve-static',
},
context
);
Expand Down
18 changes: 17 additions & 1 deletion packages/next/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface NextPluginOptions {
buildTargetName?: string;
devTargetName?: string;
startTargetName?: string;
serveStaticTargetName?: string;
}

const cachePath = join(projectGraphCacheDirectory, 'next.hash');
Expand Down Expand Up @@ -62,7 +63,6 @@ export const createNodes: CreateNodes<NextPluginOptions> = [
) {
return {};
}

options = normalizeOptions(options);

const hash = calculateHashForCreateNodes(projectRoot, options, context, [
Expand Down Expand Up @@ -106,6 +106,9 @@ async function buildNextTargets(
targets[options.devTargetName] = getDevTargetConfig(projectRoot);

targets[options.startTargetName] = getStartTargetConfig(options, projectRoot);

targets[options.serveStaticTargetName] = getStaticServeTargetConfig(options);

return targets;
}

Expand Down Expand Up @@ -152,6 +155,18 @@ function getStartTargetConfig(options: NextPluginOptions, projectRoot: string) {
return targetConfig;
}

function getStaticServeTargetConfig(options: NextPluginOptions) {
const targetConfig: TargetConfiguration = {
executor: '@nx/web:file-server',
options: {
buildTarget: options.buildTargetName,
staticFilePath: '{projectRoot}/out',
},
};

return targetConfig;
}

async function getOutputs(projectRoot, nextConfig) {
let dir = '.next';
const { PHASE_PRODUCTION_BUILD } = require('next/constants');
Expand Down Expand Up @@ -196,6 +211,7 @@ function normalizeOptions(options: NextPluginOptions): NextPluginOptions {
options.buildTargetName ??= 'build';
options.devTargetName ??= 'dev';
options.startTargetName ??= 'start';
options.serveStaticTargetName ??= 'serve-static';
return options;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/utils/add-gitignore-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function addGitIgnoreEntry(host: Tree) {
ig.add(host.read('.gitignore', 'utf-8'));

if (!ig.ignores('apps/example/.next')) {
content = `${content}\n\n# Next.js\n.next\n`;
content = `${content}\n\n# Next.js\n.next\nout\n`;
}

host.write('.gitignore', content);
Expand Down
7 changes: 7 additions & 0 deletions packages/web/src/executors/file-server/file-server.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ export default async function* fileServerExecutor(
const run = () => {
if (!running) {
running = true;
/**
* Expose a variable to the build target to know if it's being run by the serve-static executor
* This is useful because a config might need to change if it's being run by serve-static without the user's input
* or if being ran by another executor (eg. E2E tests)
* */
process.env.NX_SERVE_STATIC_BUILD_RUNNING = 'true';
try {
const args = getBuildTargetCommand(options, context);
execFileSync(pmCmd, args, {
Expand All @@ -159,6 +165,7 @@ export default async function* fileServerExecutor(
`Build target failed: ${chalk.bold(options.buildTarget)}`
);
} finally {
process.env.NX_SERVE_STATIC_BUILD_RUNNING = undefined;
running = false;
}
}
Expand Down

0 comments on commit 4302fb6

Please sign in to comment.