Colum Ferry 320d9f223f
fix(testing): application generators should accurately configure e2e projects (#27453)
- feat(devkit): add util for determining the e2e web server info
- feat(vite): add util for determining the e2e web server info
- feat(webpack): add util for determining the e2e web server info
- fix(webpack): allow port override
- fix(devkit): e2e web server info util should handle target defaults
- feat(webpack): export the e2e web server info utils
- fix(vite): rename util
- fix(devkit): util should determine the devTarget for cypress
- fix(react): improve accuracy of e2e project generation

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
The logic for finding the correct targets and web addresses to use when
setting up e2e projects is flawed and missing some key considerations.


## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
The logic is accurate and usage is simplified across plugins 

Projects:
- [x] Angular
- [x] Expo
- [x] Next
- [x] Nuxt
- [x] Vue
- [x] Web
- [x] Remix
- [x] React
- [x] React Native


## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2024-08-27 10:00:43 -04:00

197 lines
5.9 KiB
TypeScript

import {
type Tree,
addProjectConfiguration,
joinPathFragments,
readProjectConfiguration,
updateProjectConfiguration,
ensurePackage,
getPackageManagerCommand,
readNxJson,
} from '@nx/devkit';
import { type NormalizedSchema } from './normalize-options';
import { getPackageVersion } from '../../../utils/versions';
import { findPluginForConfigFile } from '@nx/devkit/src/utils/find-plugin-for-config-file';
import { addE2eCiTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
import { getE2EWebServerInfo } from '@nx/devkit/src/generators/e2e-web-server-info-utils';
export async function addE2E(tree: Tree, options: NormalizedSchema) {
const hasRemixPlugin = readNxJson(tree).plugins?.find((p) =>
typeof p === 'string'
? p === '@nx/remix/plugin'
: p.plugin === '@nx/remix/plugin'
);
let e2eWebsServerInfo = await getRemixE2EWebServerInfo(
tree,
options.projectName,
joinPathFragments(options.projectRoot, 'remix.config.js'),
options.addPlugin ?? Boolean(hasRemixPlugin)
);
if (options.e2eTestRunner === 'cypress') {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', getPackageVersion(tree, 'nx'));
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
const e2eTask = await configurationGenerator(tree, {
project: options.e2eProjectName,
directory: 'src',
skipFormat: true,
devServerTarget: e2eWebsServerInfo.e2eDevServerTarget,
baseUrl: e2eWebsServerInfo.e2eWebServerAddress,
webServerCommands: hasRemixPlugin
? {
default: e2eWebsServerInfo.e2eWebServerCommand,
production: e2eWebsServerInfo.e2eCiWebServerCommand,
}
: undefined,
ciWebServerCommand: hasRemixPlugin
? e2eWebsServerInfo.e2eCiWebServerCommand
: undefined,
ciBaseUrl: e2eWebsServerInfo.e2eCiBaseUrl,
addPlugin: options.addPlugin,
});
if (
options.addPlugin ||
readNxJson(tree).plugins?.find((p) =>
typeof p === 'string'
? p === '@nx/cypress/plugin'
: p.plugin === '@nx/cypress/plugin'
)
) {
let buildTarget = '^build';
if (hasRemixPlugin) {
const matchingPlugin = await findPluginForConfigFile(
tree,
`@nx/remix/plugin`,
joinPathFragments(options.projectRoot, 'remix.config.js')
);
if (matchingPlugin && typeof matchingPlugin !== 'string') {
buildTarget = `^${
(matchingPlugin.options as any)?.buildTargetName ?? 'build'
}`;
}
}
await addE2eCiTargetDefaults(
tree,
'@nx/cypress/plugin',
buildTarget,
joinPathFragments(
options.e2eProjectRoot,
`cypress.config.${options.js ? 'js' : 'ts'}`
)
);
}
return e2eTask;
} else if (options.e2eTestRunner === 'playwright') {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/playwright')
>('@nx/playwright', getPackageVersion(tree, 'nx'));
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
const e2eTask = await configurationGenerator(tree, {
project: options.e2eProjectName,
skipFormat: true,
skipPackageJson: false,
directory: 'src',
js: false,
linter: options.linter,
setParserOptionsProject: false,
webServerCommand: e2eWebsServerInfo.e2eCiWebServerCommand,
webServerAddress: e2eWebsServerInfo.e2eCiBaseUrl,
rootProject: options.rootProject,
addPlugin: options.addPlugin,
});
if (
options.addPlugin ||
readNxJson(tree).plugins?.find((p) =>
typeof p === 'string'
? p === '@nx/playwright/plugin'
: p.plugin === '@nx/playwright/plugin'
)
) {
let buildTarget = '^build';
if (hasRemixPlugin) {
const matchingPlugin = await findPluginForConfigFile(
tree,
`@nx/remix/plugin`,
joinPathFragments(options.projectRoot, 'remix.config.js')
);
if (matchingPlugin && typeof matchingPlugin !== 'string') {
buildTarget = `^${
(matchingPlugin.options as any)?.buildTargetName ?? 'build'
}`;
}
}
await addE2eCiTargetDefaults(
tree,
'@nx/playwright/plugin',
buildTarget,
joinPathFragments(options.e2eProjectRoot, `playwright.config.ts`)
);
}
return e2eTask;
} else {
return () => {};
}
}
async function getRemixE2EWebServerInfo(
tree: Tree,
projectName: string,
configFilePath: string,
isPluginBeingAdded: boolean
) {
const nxJson = readNxJson(tree);
let e2ePort = isPluginBeingAdded ? 3000 : 4200;
const defaultServeTarget = isPluginBeingAdded ? 'dev' : 'serve';
if (
nxJson.targetDefaults?.[defaultServeTarget] &&
nxJson.targetDefaults?.[defaultServeTarget].options?.port
) {
e2ePort = nxJson.targetDefaults?.[defaultServeTarget].options?.port;
}
return getE2EWebServerInfo(
tree,
projectName,
{
plugin: '@nx/remix/plugin',
serveTargetName: 'serveTargetName',
serveStaticTargetName: 'serveStaticTargetName',
configFilePath,
},
{
defaultServeTargetName: defaultServeTarget,
defaultServeStaticTargetName: 'serve-static',
defaultE2EWebServerAddress: `http://localhost:${e2ePort}`,
defaultE2ECiBaseUrl: 'http://localhost:3000',
defaultE2EPort: e2ePort,
},
isPluginBeingAdded
);
}