feat(testing): support continuous tasks (#30632)
## Current Behavior The Cypress and Playwright graph plugins do not infer tasks configured to take advantage of continuous tasks (do not add the task they run to start the app/server as a dependency of the e2e task). ## Expected Behavior The Cypress and Playwright graph plugins should infer tasks configured to take advantage of continuous tasks. ## Related Issue(s) Fixes #
This commit is contained in:
parent
46888b294c
commit
9a60dec0de
@ -444,6 +444,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"migrations": {
|
"migrations": {
|
||||||
|
"/nx-api/angular/migrations/set-continuous-option": {
|
||||||
|
"description": "Set the `continuous` option to `true` for continuous tasks.",
|
||||||
|
"file": "generated/packages/angular/migrations/set-continuous-option.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "set-continuous-option",
|
||||||
|
"version": "21.0.0-beta.3",
|
||||||
|
"originalFilePath": "/packages/angular",
|
||||||
|
"path": "/nx-api/angular/migrations/set-continuous-option",
|
||||||
|
"type": "migration"
|
||||||
|
},
|
||||||
"/nx-api/angular/migrations/20.5.0-angular-eslint-package-updates": {
|
"/nx-api/angular/migrations/20.5.0-angular-eslint-package-updates": {
|
||||||
"description": "",
|
"description": "",
|
||||||
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
||||||
|
|||||||
@ -439,6 +439,16 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"migrations": [
|
"migrations": [
|
||||||
|
{
|
||||||
|
"description": "Set the `continuous` option to `true` for continuous tasks.",
|
||||||
|
"file": "generated/packages/angular/migrations/set-continuous-option.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "set-continuous-option",
|
||||||
|
"version": "21.0.0-beta.3",
|
||||||
|
"originalFilePath": "/packages/angular",
|
||||||
|
"path": "angular/migrations/set-continuous-option",
|
||||||
|
"type": "migration"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "",
|
"description": "",
|
||||||
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "set-continuous-option",
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "21.0.0-beta.3",
|
||||||
|
"description": "Set the `continuous` option to `true` for continuous tasks.",
|
||||||
|
"factory": "./src/migrations/update-21-0-0/set-continuous-option",
|
||||||
|
"implementation": "/packages/angular/src/migrations/update-21-0-0/set-continuous-option.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/angular",
|
||||||
|
"schema": null,
|
||||||
|
"type": "migration",
|
||||||
|
"examplesFile": "#### Set `continuous` Option for Continuous Tasks\n\nThis migration sets the `continuous` option to `true` for tasks that are known to run continuously, and only if the option is not already explicitly set.\n\nSpecifically, it updates Angular targets using the following executors:\n\n- `@angular-devkit/build-angular:dev-server`\n- `@angular-devkit/build-angular:ssr-dev-server`\n- `@nx/angular:dev-server`\n- `@nx/angular:module-federation-dev-server`\n- `@nx/angular:module-federation-dev-ssr`\n\n#### Examples\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/app1/project.json\" %}\n{\n // ...\n \"targets\": {\n // ...\n \"serve\": {\n \"executor\": \"@angular-devkit/build-angular:dev-server\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200\n }\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"apps/app1/project.json\" highlightLines=[6] %}\n{\n // ...\n \"targets\": {\n // ...\n \"serve\": {\n \"continuous\": true,\n \"executor\": \"@angular-devkit/build-angular:dev-server\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200\n }\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n\nWhen a target is already explicitly configured with a `continuous` option, the migration will not modify it:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/app1/project.json\" highlightLines=[6] %}\n{\n // ...\n \"targets\": {\n // ...\n \"serve\": {\n \"continuous\": false,\n \"executor\": \"@nx/angular:dev-server\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200\n }\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"apps/app1/project.json\" highlightLines=[6] %}\n{\n // ...\n \"targets\": {\n // ...\n \"serve\": {\n \"continuous\": false,\n \"executor\": \"@nx/angular:dev-server\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200\n }\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
|
||||||
|
}
|
||||||
@ -356,6 +356,12 @@
|
|||||||
},
|
},
|
||||||
"description": "Update the @angular/cli package version to ~19.2.0.",
|
"description": "Update the @angular/cli package version to ~19.2.0.",
|
||||||
"factory": "./src/migrations/update-20-5-0/update-angular-cli"
|
"factory": "./src/migrations/update-20-5-0/update-angular-cli"
|
||||||
|
},
|
||||||
|
"set-continuous-option": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "21.0.0-beta.3",
|
||||||
|
"description": "Set the `continuous` option to `true` for continuous tasks.",
|
||||||
|
"factory": "./src/migrations/update-21-0-0/set-continuous-option"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {
|
"packageJsonUpdates": {
|
||||||
|
|||||||
@ -737,10 +737,12 @@ exports[`app nested should create project configs 1`] = `
|
|||||||
"buildTarget": "my-app:build:production",
|
"buildTarget": "my-app:build:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@angular-devkit/build-angular:dev-server",
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
},
|
},
|
||||||
"serve-static": {
|
"serve-static": {
|
||||||
|
"continuous": true,
|
||||||
"executor": "@nx/web:file-server",
|
"executor": "@nx/web:file-server",
|
||||||
"options": {
|
"options": {
|
||||||
"buildTarget": "my-app:build",
|
"buildTarget": "my-app:build",
|
||||||
@ -853,10 +855,12 @@ exports[`app not nested should create project configs 1`] = `
|
|||||||
"buildTarget": "my-app:build:production",
|
"buildTarget": "my-app:build:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@angular-devkit/build-angular:dev-server",
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
},
|
},
|
||||||
"serve-static": {
|
"serve-static": {
|
||||||
|
"continuous": true,
|
||||||
"executor": "@nx/web:file-server",
|
"executor": "@nx/web:file-server",
|
||||||
"options": {
|
"options": {
|
||||||
"buildTarget": "my-app:build",
|
"buildTarget": "my-app:build",
|
||||||
|
|||||||
@ -30,6 +30,7 @@ function addFileServerTarget(
|
|||||||
|
|
||||||
const projectConfig = readProjectConfiguration(tree, options.name);
|
const projectConfig = readProjectConfiguration(tree, options.name);
|
||||||
projectConfig.targets[targetName] = {
|
projectConfig.targets[targetName] = {
|
||||||
|
continuous: true,
|
||||||
executor: '@nx/web:file-server',
|
executor: '@nx/web:file-server',
|
||||||
options: {
|
options: {
|
||||||
buildTarget: `${options.name}:build`,
|
buildTarget: `${options.name}:build`,
|
||||||
|
|||||||
@ -93,6 +93,7 @@ export function createProject(tree: Tree, options: NormalizedSchema) {
|
|||||||
defaultConfiguration: 'production',
|
defaultConfiguration: 'production',
|
||||||
},
|
},
|
||||||
serve: {
|
serve: {
|
||||||
|
continuous: true,
|
||||||
executor: '@angular-devkit/build-angular:dev-server',
|
executor: '@angular-devkit/build-angular:dev-server',
|
||||||
options: options.port
|
options: options.port
|
||||||
? {
|
? {
|
||||||
|
|||||||
@ -251,6 +251,7 @@ exports[`Host App Generator --ssr should generate the correct files 10`] = `
|
|||||||
"serverTarget": "test:server:production",
|
"serverTarget": "test:server:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@nx/angular:module-federation-dev-ssr",
|
"executor": "@nx/angular:module-federation-dev-ssr",
|
||||||
}
|
}
|
||||||
@ -475,6 +476,7 @@ exports[`Host App Generator --ssr should generate the correct files for standalo
|
|||||||
"serverTarget": "test:server:production",
|
"serverTarget": "test:server:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@nx/angular:module-federation-dev-ssr",
|
"executor": "@nx/angular:module-federation-dev-ssr",
|
||||||
}
|
}
|
||||||
@ -700,6 +702,7 @@ exports[`Host App Generator --ssr should generate the correct files for standalo
|
|||||||
"serverTarget": "test:server:production",
|
"serverTarget": "test:server:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@nx/angular:module-federation-dev-ssr",
|
"executor": "@nx/angular:module-federation-dev-ssr",
|
||||||
}
|
}
|
||||||
@ -910,6 +913,7 @@ exports[`Host App Generator --ssr should generate the correct files when --types
|
|||||||
"serverTarget": "test:server:production",
|
"serverTarget": "test:server:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"executor": "@nx/angular:module-federation-dev-ssr",
|
"executor": "@nx/angular:module-federation-dev-ssr",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export function setupServeTarget(host: Tree, options: Schema) {
|
|||||||
|
|
||||||
if (options.mfType === 'remote') {
|
if (options.mfType === 'remote') {
|
||||||
appConfig.targets['serve-static'] = {
|
appConfig.targets['serve-static'] = {
|
||||||
|
continuous: true,
|
||||||
executor: '@nx/web:file-server',
|
executor: '@nx/web:file-server',
|
||||||
defaultConfiguration: 'production',
|
defaultConfiguration: 'production',
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@ -118,6 +118,7 @@ export function updateProjectConfigForBrowserBuilder(
|
|||||||
};
|
};
|
||||||
|
|
||||||
projectConfig.targets['serve-ssr'] = {
|
projectConfig.targets['serve-ssr'] = {
|
||||||
|
continuous: true,
|
||||||
executor: '@angular-devkit/build-angular:ssr-dev-server',
|
executor: '@angular-devkit/build-angular:ssr-dev-server',
|
||||||
configurations: {
|
configurations: {
|
||||||
development: {
|
development: {
|
||||||
|
|||||||
@ -0,0 +1,102 @@
|
|||||||
|
#### Set `continuous` Option for Continuous Tasks
|
||||||
|
|
||||||
|
This migration sets the `continuous` option to `true` for tasks that are known to run continuously, and only if the option is not already explicitly set.
|
||||||
|
|
||||||
|
Specifically, it updates Angular targets using the following executors:
|
||||||
|
|
||||||
|
- `@angular-devkit/build-angular:dev-server`
|
||||||
|
- `@angular-devkit/build-angular:ssr-dev-server`
|
||||||
|
- `@nx/angular:dev-server`
|
||||||
|
- `@nx/angular:module-federation-dev-server`
|
||||||
|
- `@nx/angular:module-federation-dev-ssr`
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab label="Before" %}
|
||||||
|
|
||||||
|
```json {% fileName="apps/app1/project.json" %}
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
"targets": {
|
||||||
|
// ...
|
||||||
|
"serve": {
|
||||||
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "my-app:build",
|
||||||
|
"port": 4200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
|
||||||
|
{% tab label="After" %}
|
||||||
|
|
||||||
|
```json {% fileName="apps/app1/project.json" highlightLines=[6] %}
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
"targets": {
|
||||||
|
// ...
|
||||||
|
"serve": {
|
||||||
|
"continuous": true,
|
||||||
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "my-app:build",
|
||||||
|
"port": 4200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% /tabs %}
|
||||||
|
|
||||||
|
When a target is already explicitly configured with a `continuous` option, the migration will not modify it:
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab label="Before" %}
|
||||||
|
|
||||||
|
```json {% fileName="apps/app1/project.json" highlightLines=[6] %}
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
"targets": {
|
||||||
|
// ...
|
||||||
|
"serve": {
|
||||||
|
"continuous": false,
|
||||||
|
"executor": "@nx/angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "my-app:build",
|
||||||
|
"port": 4200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
|
||||||
|
{% tab label="After" %}
|
||||||
|
|
||||||
|
```json {% fileName="apps/app1/project.json" highlightLines=[6] %}
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
"targets": {
|
||||||
|
// ...
|
||||||
|
"serve": {
|
||||||
|
"continuous": false,
|
||||||
|
"executor": "@nx/angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "my-app:build",
|
||||||
|
"port": 4200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% /tabs %}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import migration, { continuousExecutors } from './set-continuous-option';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit', () => ({
|
||||||
|
...jest.requireActual('@nx/devkit'),
|
||||||
|
formatFiles: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('set-continuous-option migration', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([...continuousExecutors])(
|
||||||
|
'should set continuous option to true for targets using "%s" executor',
|
||||||
|
async (executor) => {
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
root: 'apps/app1',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
serve: {
|
||||||
|
executor,
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migration(tree);
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, 'app1');
|
||||||
|
expect(project.targets.serve.continuous).toBe(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should not change continuous option when it is already set', async () => {
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
root: 'apps/app1',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
serve: {
|
||||||
|
executor: '@angular-devkit/build-angular:dev-server',
|
||||||
|
continuous: false,
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migration(tree);
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, 'app1');
|
||||||
|
expect(project.targets.serve.continuous).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify targets using other executors', async () => {
|
||||||
|
addProjectConfiguration(tree, 'app1', {
|
||||||
|
root: 'apps/app1',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migration(tree);
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, 'app1');
|
||||||
|
expect(project.targets.build.continuous).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
getProjects,
|
||||||
|
type Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
|
||||||
|
export const continuousExecutors = new Set([
|
||||||
|
'@angular-devkit/build-angular:dev-server',
|
||||||
|
'@angular-devkit/build-angular:ssr-dev-server',
|
||||||
|
'@nx/angular:dev-server',
|
||||||
|
'@nx/angular:module-federation-dev-server',
|
||||||
|
'@nx/angular:module-federation-dev-ssr',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default async function (tree: Tree) {
|
||||||
|
const projects = getProjects(tree);
|
||||||
|
|
||||||
|
for (const [projectName, projectConfig] of projects) {
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
|
for (const targetConfig of Object.values(projectConfig.targets ?? {})) {
|
||||||
|
if (
|
||||||
|
continuousExecutors.has(targetConfig.executor) &&
|
||||||
|
targetConfig.continuous === undefined
|
||||||
|
) {
|
||||||
|
targetConfig.continuous = true;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
@ -137,6 +137,7 @@ describe('@nx/angular/plugin', () => {
|
|||||||
"command": "ng run my-app:serve:production",
|
"command": "ng run my-app:serve:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "Run the "serve" target for "my-app".",
|
"description": "Run the "serve" target for "my-app".",
|
||||||
"help": {
|
"help": {
|
||||||
@ -435,6 +436,7 @@ describe('@nx/angular/plugin', () => {
|
|||||||
"command": "ng run org1-app1:serve:production",
|
"command": "ng run org1-app1:serve:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "Run the "serve" target for "org1-app1".",
|
"description": "Run the "serve" target for "org1-app1".",
|
||||||
"help": {
|
"help": {
|
||||||
@ -619,6 +621,7 @@ describe('@nx/angular/plugin', () => {
|
|||||||
"command": "ng run org2-app1:serve:production",
|
"command": "ng run org2-app1:serve:production",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"continuous": true,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"description": "Run the "serve" target for "org2-app1".",
|
"description": "Run the "serve" target for "org2-app1".",
|
||||||
"help": {
|
"help": {
|
||||||
|
|||||||
@ -208,6 +208,7 @@ async function buildAngularProjects(
|
|||||||
namedInputs
|
namedInputs
|
||||||
);
|
);
|
||||||
} else if (knownExecutors.devServer.has(angularTarget.builder)) {
|
} else if (knownExecutors.devServer.has(angularTarget.builder)) {
|
||||||
|
targets[nxTargetName].continuous = true;
|
||||||
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
||||||
} else if (knownExecutors.extractI18n.has(angularTarget.builder)) {
|
} else if (knownExecutors.extractI18n.has(angularTarget.builder)) {
|
||||||
targets[nxTargetName].metadata.help.example.options = {
|
targets[nxTargetName].metadata.help.example.options = {
|
||||||
@ -233,6 +234,7 @@ async function buildAngularProjects(
|
|||||||
namedInputs
|
namedInputs
|
||||||
);
|
);
|
||||||
} else if (knownExecutors.serveSsr.has(angularTarget.builder)) {
|
} else if (knownExecutors.serveSsr.has(angularTarget.builder)) {
|
||||||
|
targets[nxTargetName].continuous = true;
|
||||||
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
||||||
} else if (knownExecutors.prerender.has(angularTarget.builder)) {
|
} else if (knownExecutors.prerender.has(angularTarget.builder)) {
|
||||||
prerenderTargets.push({ target: nxTargetName, project: projectName });
|
prerenderTargets.push({ target: nxTargetName, project: projectName });
|
||||||
|
|||||||
@ -139,6 +139,7 @@ export function nxE2EPreset(
|
|||||||
webServerCommands: options?.webServerCommands,
|
webServerCommands: options?.webServerCommands,
|
||||||
ciWebServerCommand: options?.ciWebServerCommand,
|
ciWebServerCommand: options?.ciWebServerCommand,
|
||||||
ciBaseUrl: options?.ciBaseUrl,
|
ciBaseUrl: options?.ciBaseUrl,
|
||||||
|
reuseExistingServer: options?.webServerConfig?.reuseExistingServer,
|
||||||
},
|
},
|
||||||
|
|
||||||
async setupNodeEvents(on, config) {
|
async setupNodeEvents(on, config) {
|
||||||
|
|||||||
@ -11,6 +11,8 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
let createNodesFunction = createNodesV2[1];
|
let createNodesFunction = createNodesV2[1];
|
||||||
let context: CreateNodesContext;
|
let context: CreateNodesContext;
|
||||||
let tempFs: TempFs;
|
let tempFs: TempFs;
|
||||||
|
let cwd = process.cwd();
|
||||||
|
let originalCacheProjectGraph: string | undefined;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
tempFs = new TempFs('cypress-plugin');
|
tempFs = new TempFs('cypress-plugin');
|
||||||
@ -37,12 +39,18 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
workspaceRoot: tempFs.tempDir,
|
workspaceRoot: tempFs.tempDir,
|
||||||
configFiles: [],
|
configFiles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
process.chdir(tempFs.tempDir);
|
||||||
|
originalCacheProjectGraph = process.env.NX_CACHE_PROJECT_GRAPH;
|
||||||
|
process.env.NX_CACHE_PROJECT_GRAPH = 'false';
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
tempFs.cleanup();
|
tempFs.cleanup();
|
||||||
tempFs = null;
|
tempFs = null;
|
||||||
|
process.chdir(cwd);
|
||||||
|
process.env.NX_CACHE_PROJECT_GRAPH = originalCacheProjectGraph;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@ -90,6 +98,14 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
"command": "cypress run --env webServerCommand="nx run my-app:serve:production"",
|
"command": "cypress run --env webServerCommand="nx run my-app:serve:production"",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"my-app",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
"default",
|
"default",
|
||||||
"^production",
|
"^production",
|
||||||
@ -124,7 +140,6 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
"{projectRoot}/dist/videos",
|
"{projectRoot}/dist/videos",
|
||||||
"{projectRoot}/dist/screenshots",
|
"{projectRoot}/dist/screenshots",
|
||||||
],
|
],
|
||||||
"parallelism": false,
|
|
||||||
},
|
},
|
||||||
"open-cypress": {
|
"open-cypress": {
|
||||||
"command": "cypress open",
|
"command": "cypress open",
|
||||||
@ -451,6 +466,399 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should infer dependsOn using the task run in the webServerCommands.default and ciWebServerCommand for the e2e and atomized e2e-ci targets respectively and not set parallelism to false', async () => {
|
||||||
|
mockCypressConfig(
|
||||||
|
defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(join(tempFs.tempDir, 'cypress.config.js'), {
|
||||||
|
webServerCommands: {
|
||||||
|
default: 'npx nx run my-app:serve',
|
||||||
|
production: 'npx nx run my-app:serve:production',
|
||||||
|
},
|
||||||
|
ciWebServerCommand: 'npx nx run my-app:serve-static',
|
||||||
|
}),
|
||||||
|
specPattern: '**/*.cy.ts',
|
||||||
|
videosFolder: './dist/videos',
|
||||||
|
screenshotsFolder: './dist/screenshots',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const nodes = await createNodesFunction(
|
||||||
|
['cypress.config.js'],
|
||||||
|
{ targetName: 'e2e' },
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(nodes).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"cypress.config.js",
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
".": {
|
||||||
|
"metadata": {
|
||||||
|
"targetGroups": {
|
||||||
|
"E2E (CI)": [
|
||||||
|
"e2e-ci--src/test.cy.ts",
|
||||||
|
"e2e-ci",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"e2e": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "cypress run",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"command": "cypress run --env webServerCommand="npx nx run my-app:serve:production"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"my-app",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos",
|
||||||
|
"{projectRoot}/dist/screenshots",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"e2e-ci": {
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--src/test.cy.ts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"executor": "nx:noop",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nonAtomizedTarget": "e2e",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos",
|
||||||
|
"{projectRoot}/dist/screenshots",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
},
|
||||||
|
"e2e-ci--src/test.cy.ts": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "cypress run --env webServerCommand="npx nx run my-app:serve-static" --spec src/test.cy.ts --config="{\\"e2e\\":{\\"videosFolder\\":\\"dist/videos/src-test-cy-ts\\",\\"screenshotsFolder\\":\\"dist/screenshots/src-test-cy-ts\\"}}"",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"my-app",
|
||||||
|
],
|
||||||
|
"target": "serve-static",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in src/test.cy.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos/src-test-cy-ts",
|
||||||
|
"{projectRoot}/dist/screenshots/src-test-cy-ts",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"open-cypress": {
|
||||||
|
"command": "cypress open",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Opens Cypress",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress open --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--e2e",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set parallelism to false and not infer commands in dependsOn if reuseExistingServer is false', async () => {
|
||||||
|
mockCypressConfig(
|
||||||
|
defineConfig({
|
||||||
|
e2e: {
|
||||||
|
...nxE2EPreset(join(tempFs.tempDir, 'cypress.config.js'), {
|
||||||
|
webServerCommands: {
|
||||||
|
default: 'npx nx run my-app:serve',
|
||||||
|
production: 'npx nx run my-app:serve:production',
|
||||||
|
},
|
||||||
|
ciWebServerCommand: 'npx nx run my-app:serve-static',
|
||||||
|
webServerConfig: {
|
||||||
|
reuseExistingServer: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
specPattern: '**/*.cy.ts',
|
||||||
|
videosFolder: './dist/videos',
|
||||||
|
screenshotsFolder: './dist/screenshots',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const nodes = await createNodesFunction(
|
||||||
|
['cypress.config.js'],
|
||||||
|
{ targetName: 'e2e' },
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(nodes).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"cypress.config.js",
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
".": {
|
||||||
|
"metadata": {
|
||||||
|
"targetGroups": {
|
||||||
|
"E2E (CI)": [
|
||||||
|
"e2e-ci--src/test.cy.ts",
|
||||||
|
"e2e-ci",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"e2e": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "cypress run",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"command": "cypress run --env webServerCommand="npx nx run my-app:serve:production"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos",
|
||||||
|
"{projectRoot}/dist/screenshots",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
},
|
||||||
|
"e2e-ci": {
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--src/test.cy.ts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"executor": "nx:noop",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nonAtomizedTarget": "e2e",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos",
|
||||||
|
"{projectRoot}/dist/screenshots",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
},
|
||||||
|
"e2e-ci--src/test.cy.ts": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "cypress run --env webServerCommand="npx nx run my-app:serve-static" --spec src/test.cy.ts --config="{\\"e2e\\":{\\"videosFolder\\":\\"dist/videos/src-test-cy-ts\\",\\"screenshotsFolder\\":\\"dist/screenshots/src-test-cy-ts\\"}}"",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in src/test.cy.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress run --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--headed",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/dist/videos/src-test-cy-ts",
|
||||||
|
"{projectRoot}/dist/screenshots/src-test-cy-ts",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
},
|
||||||
|
"open-cypress": {
|
||||||
|
"command": "cypress open",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Opens Cypress",
|
||||||
|
"help": {
|
||||||
|
"command": "npx cypress open --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--dev",
|
||||||
|
"--e2e",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": ".",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
function mockCypressConfig(cypressConfig: Cypress.ConfigOptions) {
|
function mockCypressConfig(cypressConfig: Cypress.ConfigOptions) {
|
||||||
// This isn't JS, but all that really matters here
|
// This isn't JS, but all that really matters here
|
||||||
// is that the hash is different after updating the
|
// is that the hash is different after updating the
|
||||||
|
|||||||
@ -36,7 +36,13 @@ export interface CypressPluginOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readTargetsCache(cachePath: string): Record<string, CypressTargets> {
|
function readTargetsCache(cachePath: string): Record<string, CypressTargets> {
|
||||||
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
try {
|
||||||
|
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false'
|
||||||
|
? readJsonFile(cachePath)
|
||||||
|
: {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeTargetsToCache(cachePath: string, results: CypressTargets) {
|
function writeTargetsToCache(cachePath: string, results: CypressTargets) {
|
||||||
@ -257,6 +263,8 @@ async function buildCypressTargets(
|
|||||||
|
|
||||||
const webServerCommands: Record<string, string> =
|
const webServerCommands: Record<string, string> =
|
||||||
pluginPresetOptions?.webServerCommands;
|
pluginPresetOptions?.webServerCommands;
|
||||||
|
const shouldReuseExistingServer =
|
||||||
|
pluginPresetOptions?.reuseExistingServer ?? true;
|
||||||
|
|
||||||
const namedInputs = getNamedInputs(projectRoot, context);
|
const namedInputs = getNamedInputs(projectRoot, context);
|
||||||
|
|
||||||
@ -274,7 +282,6 @@ async function buildCypressTargets(
|
|||||||
cache: true,
|
cache: true,
|
||||||
inputs: getInputs(namedInputs),
|
inputs: getInputs(namedInputs),
|
||||||
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
|
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
|
||||||
parallelism: false,
|
|
||||||
metadata: {
|
metadata: {
|
||||||
technologies: ['cypress'],
|
technologies: ['cypress'],
|
||||||
description: 'Runs Cypress Tests',
|
description: 'Runs Cypress Tests',
|
||||||
@ -288,7 +295,23 @@ async function buildCypressTargets(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (webServerCommands?.default) {
|
if (webServerCommands?.default) {
|
||||||
|
const webServerCommandTask = shouldReuseExistingServer
|
||||||
|
? parseTaskFromCommand(webServerCommands.default)
|
||||||
|
: null;
|
||||||
|
if (webServerCommandTask) {
|
||||||
|
targets[options.targetName].dependsOn = [
|
||||||
|
{
|
||||||
|
projects: [webServerCommandTask.project],
|
||||||
|
target: webServerCommandTask.target,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
targets[options.targetName].parallelism = false;
|
||||||
|
}
|
||||||
|
|
||||||
delete webServerCommands.default;
|
delete webServerCommands.default;
|
||||||
|
} else {
|
||||||
|
targets[options.targetName].parallelism = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(webServerCommands ?? {}).length > 0) {
|
if (Object.keys(webServerCommands ?? {}).length > 0) {
|
||||||
@ -329,6 +352,9 @@ async function buildCypressTargets(
|
|||||||
const groupName = 'E2E (CI)';
|
const groupName = 'E2E (CI)';
|
||||||
metadata = { targetGroups: { [groupName]: [] } };
|
metadata = { targetGroups: { [groupName]: [] } };
|
||||||
const ciTargetGroup = metadata.targetGroups[groupName];
|
const ciTargetGroup = metadata.targetGroups[groupName];
|
||||||
|
const ciWebServerCommandTask = shouldReuseExistingServer
|
||||||
|
? parseTaskFromCommand(ciWebServerCommand)
|
||||||
|
: null;
|
||||||
|
|
||||||
for (const file of specFiles) {
|
for (const file of specFiles) {
|
||||||
const relativeSpecFilePath = normalizePath(relative(projectRoot, file));
|
const relativeSpecFilePath = normalizePath(relative(projectRoot, file));
|
||||||
@ -351,7 +377,6 @@ async function buildCypressTargets(
|
|||||||
cwd: projectRoot,
|
cwd: projectRoot,
|
||||||
env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
|
env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
|
||||||
},
|
},
|
||||||
parallelism: false,
|
|
||||||
metadata: {
|
metadata: {
|
||||||
technologies: ['cypress'],
|
technologies: ['cypress'],
|
||||||
description: `Runs Cypress Tests in ${relativeSpecFilePath} in CI`,
|
description: `Runs Cypress Tests in ${relativeSpecFilePath} in CI`,
|
||||||
@ -368,6 +393,17 @@ async function buildCypressTargets(
|
|||||||
projects: 'self',
|
projects: 'self',
|
||||||
params: 'forward',
|
params: 'forward',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (ciWebServerCommandTask) {
|
||||||
|
targets[targetName].dependsOn = [
|
||||||
|
{
|
||||||
|
target: ciWebServerCommandTask.target,
|
||||||
|
projects: [ciWebServerCommandTask.project],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
targets[targetName].parallelism = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targets[options.ciTargetName] = {
|
targets[options.ciTargetName] = {
|
||||||
@ -457,3 +493,26 @@ function getInputs(
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseTaskFromCommand(command: string): {
|
||||||
|
project: string;
|
||||||
|
target: string;
|
||||||
|
} | null {
|
||||||
|
const nxRunRegex =
|
||||||
|
/^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx run (\S+:\S+)$/;
|
||||||
|
const infixRegex = /^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx (\S+ \S+)$/;
|
||||||
|
|
||||||
|
const nxRunMatch = command.match(nxRunRegex);
|
||||||
|
if (nxRunMatch) {
|
||||||
|
const [project, target] = nxRunMatch[1].split(':');
|
||||||
|
return { project, target };
|
||||||
|
}
|
||||||
|
|
||||||
|
const infixMatch = command.match(infixRegex);
|
||||||
|
if (infixMatch) {
|
||||||
|
const [target, project] = infixMatch[1].split(' ');
|
||||||
|
return { project, target };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
@ -40,12 +40,14 @@ exports[`@nx/next/plugin integrated projects should create nodes 1`] = `
|
|||||||
},
|
},
|
||||||
"my-serve": {
|
"my-serve": {
|
||||||
"command": "next dev",
|
"command": "next dev",
|
||||||
|
"continuous": true,
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "my-app",
|
"cwd": "my-app",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"my-serve-static": {
|
"my-serve-static": {
|
||||||
"command": "next start",
|
"command": "next start",
|
||||||
|
"continuous": true,
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"my-build",
|
"my-build",
|
||||||
],
|
],
|
||||||
@ -55,6 +57,7 @@ exports[`@nx/next/plugin integrated projects should create nodes 1`] = `
|
|||||||
},
|
},
|
||||||
"my-start": {
|
"my-start": {
|
||||||
"command": "next start",
|
"command": "next start",
|
||||||
|
"continuous": true,
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"my-build",
|
"my-build",
|
||||||
],
|
],
|
||||||
@ -117,12 +120,14 @@ exports[`@nx/next/plugin root projects should create nodes 1`] = `
|
|||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"command": "next dev",
|
"command": "next dev",
|
||||||
|
"continuous": true,
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"serve-static": {
|
"serve-static": {
|
||||||
"command": "next start",
|
"command": "next start",
|
||||||
|
"continuous": true,
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"build",
|
"build",
|
||||||
],
|
],
|
||||||
@ -132,6 +137,7 @@ exports[`@nx/next/plugin root projects should create nodes 1`] = `
|
|||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"command": "next start",
|
"command": "next start",
|
||||||
|
"continuous": true,
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"build",
|
"build",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -219,6 +219,7 @@ async function getBuildTargetConfig(
|
|||||||
|
|
||||||
function getDevTargetConfig(projectRoot: string) {
|
function getDevTargetConfig(projectRoot: string) {
|
||||||
const targetConfig: TargetConfiguration = {
|
const targetConfig: TargetConfiguration = {
|
||||||
|
continuous: true,
|
||||||
command: `next dev`,
|
command: `next dev`,
|
||||||
options: {
|
options: {
|
||||||
cwd: projectRoot,
|
cwd: projectRoot,
|
||||||
@ -230,6 +231,7 @@ function getDevTargetConfig(projectRoot: string) {
|
|||||||
|
|
||||||
function getStartTargetConfig(options: NextPluginOptions, projectRoot: string) {
|
function getStartTargetConfig(options: NextPluginOptions, projectRoot: string) {
|
||||||
const targetConfig: TargetConfiguration = {
|
const targetConfig: TargetConfiguration = {
|
||||||
|
continuous: true,
|
||||||
command: `next start`,
|
command: `next start`,
|
||||||
options: {
|
options: {
|
||||||
cwd: projectRoot,
|
cwd: projectRoot,
|
||||||
|
|||||||
@ -26,13 +26,13 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: '<%= webServerCommand %>',
|
command: '<%= webServerCommand %>',
|
||||||
url: '<%= webServerAddress %>',
|
url: '<%= webServerAddress %>',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot
|
cwd: workspaceRoot
|
||||||
},<% } else {%>
|
},<% } else {%>
|
||||||
// webServer: {
|
// webServer: {
|
||||||
// command: 'npm run start',
|
// command: 'npm run start',
|
||||||
// url: 'http://127.0.0.1:3000',
|
// url: 'http://127.0.0.1:3000',
|
||||||
// reuseExistingServer: !process.env.CI,
|
// reuseExistingServer: true,
|
||||||
// cwd: workspaceRoot,
|
// cwd: workspaceRoot,
|
||||||
// },<% } %>
|
// },<% } %>
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@ -137,7 +137,7 @@ function createTestProject(
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx serve myapp',
|
command: 'npx nx serve myapp',
|
||||||
url: 'http://localhost:4200',
|
url: 'http://localhost:4200',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ describe('@nx/playwright/plugin', () => {
|
|||||||
let createNodesFunction = createNodesV2[1];
|
let createNodesFunction = createNodesV2[1];
|
||||||
let context: CreateNodesContext;
|
let context: CreateNodesContext;
|
||||||
let tempFs: TempFs;
|
let tempFs: TempFs;
|
||||||
|
let cwd = process.cwd();
|
||||||
|
let originalCacheProjectGraph: string | undefined;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
tempFs = new TempFs('playwright-plugin');
|
tempFs = new TempFs('playwright-plugin');
|
||||||
@ -26,11 +28,16 @@ describe('@nx/playwright/plugin', () => {
|
|||||||
workspaceRoot: tempFs.tempDir,
|
workspaceRoot: tempFs.tempDir,
|
||||||
configFiles: [],
|
configFiles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
process.chdir(tempFs.tempDir);
|
||||||
|
originalCacheProjectGraph = process.env.NX_CACHE_PROJECT_GRAPH;
|
||||||
|
process.env.NX_CACHE_PROJECT_GRAPH = 'false';
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// tempFs.cleanup();
|
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
|
process.chdir(cwd);
|
||||||
|
process.env.NX_CACHE_PROJECT_GRAPH = originalCacheProjectGraph;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create nodes with default playwright configuration', async () => {
|
it('should create nodes with default playwright configuration', async () => {
|
||||||
@ -431,6 +438,451 @@ describe('@nx/playwright/plugin', () => {
|
|||||||
expect(targets['e2e-ci--tests/ignored/run-me.spec.ts']).not.toBeDefined();
|
expect(targets['e2e-ci--tests/ignored/run-me.spec.ts']).not.toBeDefined();
|
||||||
expect(targets['e2e-ci--not-tests/run-me.spec.ts']).not.toBeDefined();
|
expect(targets['e2e-ci--not-tests/run-me.spec.ts']).not.toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should infer dependsOn using the task run in the webServer.command and not set parallelism to false', async () => {
|
||||||
|
await mockPlaywrightConfig(tempFs, {
|
||||||
|
testDir: 'tests',
|
||||||
|
webServer: {
|
||||||
|
command: 'npx nx run app1:serve',
|
||||||
|
reuseExistingServer: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'tests/run-me.spec.ts': '',
|
||||||
|
'tests/run-me-2.spec.ts': '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await createNodesFunction(
|
||||||
|
['playwright.config.js'],
|
||||||
|
{
|
||||||
|
targetName: 'e2e',
|
||||||
|
ciTargetName: 'e2e-ci',
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const project = results[0][1].projects['.'];
|
||||||
|
const { targets } = project;
|
||||||
|
expect(targets['e2e']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--tests/run-me-2.spec.ts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--tests/run-me.spec.ts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"executor": "nx:noop",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nonAtomizedTarget": "e2e",
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(project.metadata.targetGroups).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"E2E (CI)": [
|
||||||
|
"e2e-ci--tests/run-me-2.spec.ts",
|
||||||
|
"e2e-ci--tests/run-me.spec.ts",
|
||||||
|
"e2e-ci",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci--tests/run-me.spec.ts']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test tests/run-me.spec.ts --output=test-results/tests-run-me-spec-ts",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in tests/run-me.spec.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
"env": {},
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results/tests-run-me-spec-ts",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci--tests/run-me-2.spec.ts']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test tests/run-me-2.spec.ts --output=test-results/tests-run-me-2-spec-ts",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in tests/run-me-2.spec.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
"env": {},
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results/tests-run-me-2-spec-ts",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not set parallelism to false and should infer dependsOn using the tasks run in the different webServer.command that have reuseExistingServer set to true', async () => {
|
||||||
|
await mockPlaywrightConfig(tempFs, {
|
||||||
|
testDir: 'tests',
|
||||||
|
webServer: [
|
||||||
|
{ command: 'npx nx run app1:serve', reuseExistingServer: true },
|
||||||
|
{ command: 'npx nx run api1:serve', reuseExistingServer: true },
|
||||||
|
{ command: 'npx nx run api2:dev', reuseExistingServer: true },
|
||||||
|
{ command: 'npx nx run api3:serve', reuseExistingServer: false }, // this one should not be included in dependsOn
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'tests/run-me.spec.ts': '',
|
||||||
|
'tests/run-me-2.spec.ts': '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await createNodesFunction(
|
||||||
|
['playwright.config.js'],
|
||||||
|
{
|
||||||
|
targetName: 'e2e',
|
||||||
|
ciTargetName: 'e2e-ci',
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
const project = results[0][1].projects['.'];
|
||||||
|
const { targets } = project;
|
||||||
|
expect(targets['e2e']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
"api1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"api2",
|
||||||
|
],
|
||||||
|
"target": "dev",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--tests/run-me-2.spec.ts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "e2e-ci--tests/run-me.spec.ts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"executor": "nx:noop",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nonAtomizedTarget": "e2e",
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results",
|
||||||
|
],
|
||||||
|
"parallelism": false,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(project.metadata.targetGroups).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"E2E (CI)": [
|
||||||
|
"e2e-ci--tests/run-me-2.spec.ts",
|
||||||
|
"e2e-ci--tests/run-me.spec.ts",
|
||||||
|
"e2e-ci",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci--tests/run-me.spec.ts']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test tests/run-me.spec.ts --output=test-results/tests-run-me-spec-ts",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
"api1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"api2",
|
||||||
|
],
|
||||||
|
"target": "dev",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in tests/run-me.spec.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
"env": {},
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results/tests-run-me-spec-ts",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(targets['e2e-ci--tests/run-me-2.spec.ts']).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"cache": true,
|
||||||
|
"command": "playwright test tests/run-me-2.spec.ts --output=test-results/tests-run-me-2-spec-ts",
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"app1",
|
||||||
|
"api1",
|
||||||
|
],
|
||||||
|
"target": "serve",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
"api2",
|
||||||
|
],
|
||||||
|
"target": "dev",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"@playwright/test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Playwright Tests in tests/run-me-2.spec.ts in CI",
|
||||||
|
"help": {
|
||||||
|
"command": "npx playwright test --help",
|
||||||
|
"example": {
|
||||||
|
"options": {
|
||||||
|
"workers": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"playwright",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "{projectRoot}",
|
||||||
|
"env": {},
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/test-results/tests-run-me-2-spec-ts",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function mockPlaywrightConfig(
|
async function mockPlaywrightConfig(
|
||||||
|
|||||||
@ -44,7 +44,13 @@ type PlaywrightTargets = Pick<ProjectConfiguration, 'targets' | 'metadata'>;
|
|||||||
function readTargetsCache(
|
function readTargetsCache(
|
||||||
cachePath: string
|
cachePath: string
|
||||||
): Record<string, PlaywrightTargets> {
|
): Record<string, PlaywrightTargets> {
|
||||||
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
try {
|
||||||
|
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false'
|
||||||
|
? readJsonFile(cachePath)
|
||||||
|
: {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeTargetsToCache(
|
function writeTargetsToCache(
|
||||||
@ -159,12 +165,12 @@ async function buildPlaywrightTargets(
|
|||||||
|
|
||||||
const testOutput = getTestOutput(playwrightConfig);
|
const testOutput = getTestOutput(playwrightConfig);
|
||||||
const reporterOutputs = getReporterOutputs(playwrightConfig);
|
const reporterOutputs = getReporterOutputs(playwrightConfig);
|
||||||
|
const webserverCommandTasks = getWebserverCommandTasks(playwrightConfig);
|
||||||
const baseTargetConfig: TargetConfiguration = {
|
const baseTargetConfig: TargetConfiguration = {
|
||||||
command: 'playwright test',
|
command: 'playwright test',
|
||||||
options: {
|
options: {
|
||||||
cwd: '{projectRoot}',
|
cwd: '{projectRoot}',
|
||||||
},
|
},
|
||||||
parallelism: false,
|
|
||||||
metadata: {
|
metadata: {
|
||||||
technologies: ['playwright'],
|
technologies: ['playwright'],
|
||||||
description: 'Runs Playwright Tests',
|
description: 'Runs Playwright Tests',
|
||||||
@ -179,6 +185,12 @@ async function buildPlaywrightTargets(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (webserverCommandTasks.length) {
|
||||||
|
baseTargetConfig.dependsOn = getDependsOn(webserverCommandTasks);
|
||||||
|
} else {
|
||||||
|
baseTargetConfig.parallelism = false;
|
||||||
|
}
|
||||||
|
|
||||||
targets[options.targetName] = {
|
targets[options.targetName] = {
|
||||||
...baseTargetConfig,
|
...baseTargetConfig,
|
||||||
cache: true,
|
cache: true,
|
||||||
@ -438,6 +450,74 @@ function addSubfolderToOutput(output: string, subfolder?: string): string {
|
|||||||
return join(output, subfolder);
|
return join(output, subfolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWebserverCommandTasks(
|
||||||
|
playwrightConfig: PlaywrightTestConfig
|
||||||
|
): Array<{ project: string; target: string }> {
|
||||||
|
if (!playwrightConfig.webServer) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tasks: Array<{ project: string; target: string }> = [];
|
||||||
|
|
||||||
|
const webServer = Array.isArray(playwrightConfig.webServer)
|
||||||
|
? playwrightConfig.webServer
|
||||||
|
: [playwrightConfig.webServer];
|
||||||
|
|
||||||
|
for (const server of webServer) {
|
||||||
|
if (!server.reuseExistingServer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const task = parseTaskFromCommand(server.command);
|
||||||
|
if (task) {
|
||||||
|
tasks.push(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTaskFromCommand(command: string): {
|
||||||
|
project: string;
|
||||||
|
target: string;
|
||||||
|
} | null {
|
||||||
|
const nxRunRegex =
|
||||||
|
/^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx run (\S+:\S+)$/;
|
||||||
|
const infixRegex = /^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx (\S+ \S+)$/;
|
||||||
|
|
||||||
|
const nxRunMatch = command.match(nxRunRegex);
|
||||||
|
if (nxRunMatch) {
|
||||||
|
const [project, target] = nxRunMatch[1].split(':');
|
||||||
|
return { project, target };
|
||||||
|
}
|
||||||
|
|
||||||
|
const infixMatch = command.match(infixRegex);
|
||||||
|
if (infixMatch) {
|
||||||
|
const [target, project] = infixMatch[1].split(' ');
|
||||||
|
return { project, target };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDependsOn(
|
||||||
|
tasks: Array<{ project: string; target: string }>
|
||||||
|
): TargetConfiguration['dependsOn'] {
|
||||||
|
const projectsPerTask = new Map<string, string[]>();
|
||||||
|
|
||||||
|
for (const { project, target } of tasks) {
|
||||||
|
if (!projectsPerTask.has(target)) {
|
||||||
|
projectsPerTask.set(target, []);
|
||||||
|
}
|
||||||
|
projectsPerTask.get(target).push(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(projectsPerTask.entries()).map(([target, projects]) => ({
|
||||||
|
projects,
|
||||||
|
target,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeOutput(
|
function normalizeOutput(
|
||||||
path: string,
|
path: string,
|
||||||
workspaceRoot: string,
|
workspaceRoot: string,
|
||||||
|
|||||||
@ -180,7 +180,7 @@ describe('app', () => {
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: '${packageCmd} nx run my-app:preview',
|
command: '${packageCmd} nx run my-app:preview',
|
||||||
url: 'http://localhost:4300',
|
url: 'http://localhost:4300',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot
|
cwd: workspaceRoot
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@ -182,7 +182,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run test:serve-static',
|
command: 'npx nx run test:serve-static',
|
||||||
url: 'http://localhost:3000',
|
url: 'http://localhost:3000',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
@ -712,7 +712,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run test:serve-static',
|
command: 'npx nx run test:serve-static',
|
||||||
url: 'http://localhost:3000',
|
url: 'http://localhost:3000',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@ -364,7 +364,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run test:preview',
|
command: 'npx nx run test:preview',
|
||||||
url: 'http://localhost:4300',
|
url: 'http://localhost:4300',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run my-app:preview',
|
command: 'npx nx run my-app:preview',
|
||||||
url: 'http://localhost:4300',
|
url: 'http://localhost:4300',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
@ -101,7 +101,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run cool-app:serve-static',
|
command: 'npx nx run cool-app:serve-static',
|
||||||
url: 'http://localhost:4200',
|
url: 'http://localhost:4200',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
@ -173,7 +173,7 @@ export default defineConfig({
|
|||||||
webServer: {
|
webServer: {
|
||||||
command: 'npx nx run my-app:preview',
|
command: 'npx nx run my-app:preview',
|
||||||
url: 'http://localhost:4300',
|
url: 'http://localhost:4300',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: true,
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user