@nx/playwright 17.0.2 → 17.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +9 -4
- package/executors.json +1 -1
- package/generators.json +2 -2
- package/index.d.ts +1 -1
- package/index.js +2 -2
- package/migrations.json +16 -0
- package/package.json +11 -4
- package/plugin.d.ts +1 -0
- package/plugin.js +6 -0
- package/src/executors/playwright/{playwright.d.ts → playwright.impl.d.ts} +2 -1
- package/src/executors/playwright/{playwright.js → playwright.impl.js} +6 -2
- package/src/executors/playwright/schema.json +10 -2
- package/src/generators/configuration/configuration.d.ts +1 -0
- package/src/generators/configuration/configuration.js +91 -11
- package/src/generators/configuration/files/playwright.config.ts.template +39 -2
- package/src/generators/configuration/schema.d.ts +3 -0
- package/src/generators/configuration/schema.json +13 -1
- package/src/generators/init/init.d.ts +1 -0
- package/src/generators/init/init.js +34 -30
- package/src/generators/init/schema.d.ts +5 -3
- package/src/generators/init/schema.json +10 -3
- package/src/migrations/update-17-3-1/add-project-to-config.d.ts +2 -0
- package/src/migrations/update-17-3-1/add-project-to-config.js +95 -0
- package/src/migrations/update-18-1-0/remove-baseUrl-from-project-json.d.ts +2 -0
- package/src/migrations/update-18-1-0/remove-baseUrl-from-project-json.js +42 -0
- package/src/plugins/plugin.d.ts +7 -0
- package/src/plugins/plugin.js +205 -0
- package/src/utils/add-linter.d.ts +1 -0
- package/src/utils/add-linter.js +1 -3
- package/src/utils/preset.d.ts +1 -10
- package/src/utils/preset.js +1 -39
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
<p style="text-align: center;"
|
|
1
|
+
<p style="text-align: center;">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
|
|
4
|
+
<img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
8
|
<div style="text-align: center;">
|
|
4
9
|
|
|
@@ -15,9 +20,9 @@
|
|
|
15
20
|
|
|
16
21
|
<hr>
|
|
17
22
|
|
|
18
|
-
# Nx: Smart
|
|
23
|
+
# Nx: Smart Monorepos · Fast CI
|
|
19
24
|
|
|
20
|
-
Nx is a
|
|
25
|
+
Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
|
|
21
26
|
|
|
22
27
|
## Getting Started
|
|
23
28
|
|
|
@@ -57,5 +62,5 @@ npx nx@latest init
|
|
|
57
62
|
- [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
|
|
58
63
|
|
|
59
64
|
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
|
|
60
|
-
width="100%" alt="Nx - Smart
|
|
65
|
+
width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
|
|
61
66
|
|
package/executors.json
CHANGED
package/generators.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"version": "0.1",
|
|
4
4
|
"generators": {
|
|
5
5
|
"configuration": {
|
|
6
|
-
"factory": "./src/generators/configuration/configuration",
|
|
6
|
+
"factory": "./src/generators/configuration/configuration#configurationGeneratorInternal",
|
|
7
7
|
"schema": "./src/generators/configuration/schema.json",
|
|
8
8
|
"description": "Add Nx Playwright configuration to your project"
|
|
9
9
|
},
|
|
10
10
|
"init": {
|
|
11
|
-
"factory": "./src/generators/init/init",
|
|
11
|
+
"factory": "./src/generators/init/init#initGeneratorInternal",
|
|
12
12
|
"schema": "./src/generators/init/schema.json",
|
|
13
13
|
"description": "Initializes a Playwright project in the current workspace"
|
|
14
14
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { playwrightExecutor, PlaywrightExecutorSchema, } from './src/executors/playwright/playwright';
|
|
1
|
+
export { playwrightExecutor, PlaywrightExecutorSchema, } from './src/executors/playwright/playwright.impl';
|
|
2
2
|
export { initGenerator } from './src/generators/init/init';
|
|
3
3
|
export { configurationGenerator } from './src/generators/configuration/configuration';
|
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.configurationGenerator = exports.initGenerator = exports.playwrightExecutor = void 0;
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "playwrightExecutor", { enumerable: true, get: function () { return
|
|
4
|
+
var playwright_impl_1 = require("./src/executors/playwright/playwright.impl");
|
|
5
|
+
Object.defineProperty(exports, "playwrightExecutor", { enumerable: true, get: function () { return playwright_impl_1.playwrightExecutor; } });
|
|
6
6
|
var init_1 = require("./src/generators/init/init");
|
|
7
7
|
Object.defineProperty(exports, "initGenerator", { enumerable: true, get: function () { return init_1.initGenerator; } });
|
|
8
8
|
var configuration_1 = require("./src/generators/configuration/configuration");
|
package/migrations.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generators": {
|
|
3
|
+
"17-3-1-add-project-to-config": {
|
|
4
|
+
"cli": "nx",
|
|
5
|
+
"version": "17.3.1-beta.0",
|
|
6
|
+
"description": "Add project property to playwright config",
|
|
7
|
+
"implementation": "./src/migrations/update-17-3-1/add-project-to-config"
|
|
8
|
+
},
|
|
9
|
+
"18-1-0-remove-baseUrl-from-project-json": {
|
|
10
|
+
"cli": "nx",
|
|
11
|
+
"version": "18.1.0-beta.3",
|
|
12
|
+
"description": "Remove invalid baseUrl option from @nx/playwright:playwright targets in project.json.",
|
|
13
|
+
"implementation": "./src/migrations/update-18-1-0/remove-baseUrl-from-project-json"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/playwright",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.4",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"homepage": "https://nx.dev",
|
|
6
6
|
"private": false,
|
|
@@ -32,9 +32,12 @@
|
|
|
32
32
|
"directory": "packages/playwright"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@
|
|
36
|
-
"@nx/
|
|
37
|
-
"
|
|
35
|
+
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
36
|
+
"@nx/devkit": "17.0.4",
|
|
37
|
+
"@nx/eslint": "17.0.4",
|
|
38
|
+
"@nx/js": "17.0.4",
|
|
39
|
+
"tslib": "^2.3.0",
|
|
40
|
+
"minimatch": "9.0.3"
|
|
38
41
|
},
|
|
39
42
|
"peerDependencies": {
|
|
40
43
|
"@playwright/test": "^1.36.0"
|
|
@@ -54,6 +57,10 @@
|
|
|
54
57
|
"./generators/*/schema.json": "./src/generators/*/schema.json",
|
|
55
58
|
"./executors.json": "./executors.json",
|
|
56
59
|
"./executors/*/schema.json": "./src/executors/*/schema.json",
|
|
60
|
+
"./plugin": "./plugin.js",
|
|
57
61
|
"./preset": "./src/utils/preset.js"
|
|
62
|
+
},
|
|
63
|
+
"nx-migrations": {
|
|
64
|
+
"migrations": "./migrations.json"
|
|
58
65
|
}
|
|
59
66
|
}
|
package/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createNodes, PlaywrightPluginOptions, createDependencies, } from './src/plugins/plugin';
|
package/plugin.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDependencies = exports.createNodes = void 0;
|
|
4
|
+
var plugin_1 = require("./src/plugins/plugin");
|
|
5
|
+
Object.defineProperty(exports, "createNodes", { enumerable: true, get: function () { return plugin_1.createNodes; } });
|
|
6
|
+
Object.defineProperty(exports, "createDependencies", { enumerable: true, get: function () { return plugin_1.createDependencies; } });
|
|
@@ -8,6 +8,7 @@ export interface PlaywrightExecutorSchema {
|
|
|
8
8
|
grep?: string;
|
|
9
9
|
globalTimeout?: number;
|
|
10
10
|
grepInvert?: string;
|
|
11
|
+
testFiles?: string[];
|
|
11
12
|
headed?: boolean;
|
|
12
13
|
ignoreSnapshots?: boolean;
|
|
13
14
|
workers?: string;
|
|
@@ -27,7 +28,7 @@ export interface PlaywrightExecutorSchema {
|
|
|
27
28
|
updateSnapshots?: boolean;
|
|
28
29
|
ui?: boolean;
|
|
29
30
|
uiHost?: string;
|
|
30
|
-
uiPort?:
|
|
31
|
+
uiPort?: number;
|
|
31
32
|
skipInstall?: boolean;
|
|
32
33
|
}
|
|
33
34
|
export declare function playwrightExecutor(options: PlaywrightExecutorSchema, context: ExecutorContext): Promise<{
|
|
@@ -36,14 +36,18 @@ async function playwrightExecutor(options, context) {
|
|
|
36
36
|
exports.playwrightExecutor = playwrightExecutor;
|
|
37
37
|
function createArgs(opts, exclude = ['skipInstall']) {
|
|
38
38
|
const args = [];
|
|
39
|
-
|
|
39
|
+
const { testFiles, ...rest } = opts;
|
|
40
|
+
if (testFiles) {
|
|
41
|
+
args.push(...testFiles);
|
|
42
|
+
}
|
|
43
|
+
for (const key in rest) {
|
|
40
44
|
if (exclude.includes(key))
|
|
41
45
|
continue;
|
|
42
46
|
const value = opts[key];
|
|
43
47
|
// NOTE: playwright doesn't accept pascalCase args, only kebab-case
|
|
44
48
|
const arg = (0, devkit_1.names)(key).fileName;
|
|
45
49
|
if (Array.isArray(value)) {
|
|
46
|
-
args.push(
|
|
50
|
+
args.push(...value.map((v) => `--${arg}=${v.trim()}`));
|
|
47
51
|
}
|
|
48
52
|
else if (typeof value === 'boolean') {
|
|
49
53
|
// NOTE: playwright don't accept --arg=false, instead just don't pass the arg.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
3
|
"version": 2,
|
|
4
4
|
"title": "Playwright executor",
|
|
5
5
|
"description": "Run Playwright tests.",
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
"type": "string",
|
|
44
44
|
"description": "Only run tests that do not match this regular expression"
|
|
45
45
|
},
|
|
46
|
+
"testFiles": {
|
|
47
|
+
"alias": "t",
|
|
48
|
+
"type": "array",
|
|
49
|
+
"description": "Test files to run",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
46
54
|
"headed": {
|
|
47
55
|
"type": "boolean",
|
|
48
56
|
"description": "Run tests in headed browsers",
|
|
@@ -147,7 +155,7 @@
|
|
|
147
155
|
"description": "Host to serve UI on; specifying this option opens UI in a browser tab"
|
|
148
156
|
},
|
|
149
157
|
"uiPort": {
|
|
150
|
-
"type": "
|
|
158
|
+
"type": "number",
|
|
151
159
|
"description": "Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab"
|
|
152
160
|
},
|
|
153
161
|
"skipInstall": {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GeneratorCallback, Tree } from '@nx/devkit';
|
|
2
2
|
import { ConfigurationGeneratorSchema } from './schema';
|
|
3
3
|
export declare function configurationGenerator(tree: Tree, options: ConfigurationGeneratorSchema): Promise<GeneratorCallback>;
|
|
4
|
+
export declare function configurationGeneratorInternal(tree: Tree, options: ConfigurationGeneratorSchema): Promise<GeneratorCallback>;
|
|
4
5
|
export default configurationGenerator;
|
|
@@ -1,26 +1,67 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.configurationGenerator = void 0;
|
|
3
|
+
exports.configurationGeneratorInternal = exports.configurationGenerator = void 0;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const js_1 = require("@nx/js");
|
|
6
|
+
const versions_1 = require("@nx/js/src/utils/versions");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
5
8
|
const path = require("path");
|
|
6
|
-
const init_1 = require("../init/init");
|
|
7
9
|
const add_linter_1 = require("../../utils/add-linter");
|
|
8
|
-
|
|
10
|
+
const versions_2 = require("../../utils/versions");
|
|
11
|
+
const init_1 = require("../init/init");
|
|
12
|
+
function configurationGenerator(tree, options) {
|
|
13
|
+
return configurationGeneratorInternal(tree, { addPlugin: false, ...options });
|
|
14
|
+
}
|
|
15
|
+
exports.configurationGenerator = configurationGenerator;
|
|
16
|
+
async function configurationGeneratorInternal(tree, options) {
|
|
17
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
18
|
+
options.addPlugin ??=
|
|
19
|
+
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
20
|
+
nxJson.useInferencePlugins !== false;
|
|
9
21
|
const tasks = [];
|
|
10
|
-
tasks.push(await (0, init_1.
|
|
22
|
+
tasks.push(await (0, init_1.initGenerator)(tree, {
|
|
11
23
|
skipFormat: true,
|
|
12
24
|
skipPackageJson: options.skipPackageJson,
|
|
25
|
+
addPlugin: options.addPlugin,
|
|
13
26
|
}));
|
|
14
27
|
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
|
|
28
|
+
const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
|
|
29
|
+
const offsetFromProjectRoot = (0, devkit_1.offsetFromRoot)(projectConfig.root);
|
|
15
30
|
(0, devkit_1.generateFiles)(tree, path.join(__dirname, 'files'), projectConfig.root, {
|
|
16
|
-
offsetFromRoot:
|
|
31
|
+
offsetFromRoot: offsetFromProjectRoot,
|
|
17
32
|
projectRoot: projectConfig.root,
|
|
18
33
|
webServerCommand: options.webServerCommand ?? null,
|
|
19
34
|
webServerAddress: options.webServerAddress ?? null,
|
|
20
35
|
...options,
|
|
21
36
|
});
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
if (!hasTsConfig) {
|
|
38
|
+
tree.write(`${projectConfig.root}/tsconfig.json`, JSON.stringify({
|
|
39
|
+
extends: (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
|
|
40
|
+
compilerOptions: {
|
|
41
|
+
allowJs: true,
|
|
42
|
+
outDir: `${offsetFromProjectRoot}dist/out-tsc`,
|
|
43
|
+
module: 'commonjs',
|
|
44
|
+
sourceMap: false,
|
|
45
|
+
},
|
|
46
|
+
include: [
|
|
47
|
+
'**/*.ts',
|
|
48
|
+
'**/*.js',
|
|
49
|
+
'playwright.config.ts',
|
|
50
|
+
'src/**/*.spec.ts',
|
|
51
|
+
'src/**/*.spec.js',
|
|
52
|
+
'src/**/*.test.ts',
|
|
53
|
+
'src/**/*.test.js',
|
|
54
|
+
'src/**/*.d.ts',
|
|
55
|
+
],
|
|
56
|
+
}, null, 2));
|
|
57
|
+
}
|
|
58
|
+
const hasPlugin = (0, devkit_1.readNxJson)(tree).plugins?.some((p) => typeof p === 'string'
|
|
59
|
+
? p === '@nx/playwright/plugin'
|
|
60
|
+
: p.plugin === '@nx/playwright/plugin');
|
|
61
|
+
if (!hasPlugin) {
|
|
62
|
+
addE2eTarget(tree, options);
|
|
63
|
+
setupE2ETargetDefaults(tree);
|
|
64
|
+
}
|
|
24
65
|
tasks.push(await (0, add_linter_1.addLinterToPlaywrightProject)(tree, {
|
|
25
66
|
project: options.project,
|
|
26
67
|
linter: options.linter,
|
|
@@ -28,17 +69,55 @@ async function configurationGenerator(tree, options) {
|
|
|
28
69
|
js: options.js,
|
|
29
70
|
directory: options.directory,
|
|
30
71
|
setParserOptionsProject: options.setParserOptionsProject,
|
|
31
|
-
rootProject: projectConfig.root === '.',
|
|
72
|
+
rootProject: options.rootProject ?? projectConfig.root === '.',
|
|
73
|
+
addPlugin: options.addPlugin,
|
|
32
74
|
}));
|
|
33
75
|
if (options.js) {
|
|
34
|
-
(0, devkit_1.
|
|
76
|
+
const { ModuleKind } = (0, devkit_1.ensurePackage)('typescript', versions_1.typescriptVersion);
|
|
77
|
+
(0, devkit_1.toJS)(tree, { extension: '.cjs', module: ModuleKind.CommonJS });
|
|
78
|
+
}
|
|
79
|
+
recommendVsCodeExtensions(tree);
|
|
80
|
+
if (!options.skipPackageJson) {
|
|
81
|
+
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
82
|
+
// required since used in playwright config
|
|
83
|
+
'@nx/devkit': versions_2.nxVersion,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
if (!options.skipInstall) {
|
|
87
|
+
tasks.push(getBrowsersInstallTask());
|
|
35
88
|
}
|
|
36
89
|
if (!options.skipFormat) {
|
|
37
90
|
await (0, devkit_1.formatFiles)(tree);
|
|
38
91
|
}
|
|
39
92
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
40
93
|
}
|
|
41
|
-
exports.
|
|
94
|
+
exports.configurationGeneratorInternal = configurationGeneratorInternal;
|
|
95
|
+
function getBrowsersInstallTask() {
|
|
96
|
+
return () => {
|
|
97
|
+
devkit_1.output.log({
|
|
98
|
+
title: 'Ensuring Playwright is installed.',
|
|
99
|
+
bodyLines: ['use --skipInstall to skip installation.'],
|
|
100
|
+
});
|
|
101
|
+
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
|
102
|
+
(0, child_process_1.execSync)(`${pmc.exec} playwright install`, { cwd: devkit_1.workspaceRoot });
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function recommendVsCodeExtensions(tree) {
|
|
106
|
+
if (tree.exists('.vscode/extensions.json')) {
|
|
107
|
+
(0, devkit_1.updateJson)(tree, '.vscode/extensions.json', (json) => {
|
|
108
|
+
json.recommendations ??= [];
|
|
109
|
+
const recs = new Set(json.recommendations);
|
|
110
|
+
recs.add('ms-playwright.playwright');
|
|
111
|
+
json.recommendations = Array.from(recs);
|
|
112
|
+
return json;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
(0, devkit_1.writeJson)(tree, '.vscode/extensions.json', {
|
|
117
|
+
recommendations: ['ms-playwright.playwright'],
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
42
121
|
function setupE2ETargetDefaults(tree) {
|
|
43
122
|
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
44
123
|
if (!nxJson.namedInputs) {
|
|
@@ -48,6 +127,7 @@ function setupE2ETargetDefaults(tree) {
|
|
|
48
127
|
nxJson.targetDefaults ??= {};
|
|
49
128
|
const productionFileSet = !!nxJson.namedInputs?.production;
|
|
50
129
|
nxJson.targetDefaults.e2e ??= {};
|
|
130
|
+
nxJson.targetDefaults.e2e.cache ??= true;
|
|
51
131
|
nxJson.targetDefaults.e2e.inputs ??= [
|
|
52
132
|
'default',
|
|
53
133
|
productionFileSet ? '^production' : '^default',
|
|
@@ -65,7 +145,7 @@ Rename or remove the existing e2e target.`);
|
|
|
65
145
|
executor: '@nx/playwright:playwright',
|
|
66
146
|
outputs: [`{workspaceRoot}/dist/.playwright/${projectConfig.root}`],
|
|
67
147
|
options: {
|
|
68
|
-
config: `${projectConfig.root}/playwright.config.${options.js ? '
|
|
148
|
+
config: `${projectConfig.root}/playwright.config.${options.js ? 'cjs' : 'ts'}`,
|
|
69
149
|
},
|
|
70
150
|
};
|
|
71
151
|
(0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineConfig } from '@playwright/test';
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
2
|
import { nxE2EPreset } from '@nx/playwright/preset';
|
|
3
3
|
<% if(!webServerCommand || !webServerAddress) { %>// eslint-disable-next-line @typescript-eslint/no-unused-vars <% } %>
|
|
4
4
|
import { workspaceRoot } from '@nx/devkit';
|
|
@@ -29,10 +29,47 @@ export default defineConfig({
|
|
|
29
29
|
url: '<%= webServerAddress %>',
|
|
30
30
|
reuseExistingServer: !process.env.CI,
|
|
31
31
|
cwd: workspaceRoot
|
|
32
|
-
},<% } else {
|
|
32
|
+
},<% } else {%>
|
|
33
|
+
// webServer: {
|
|
33
34
|
// command: 'npm run start',
|
|
34
35
|
// url: 'http://127.0.0.1:3000',
|
|
35
36
|
// reuseExistingServer: !process.env.CI,
|
|
36
37
|
// cwd: workspaceRoot,
|
|
37
38
|
// },<% } %>
|
|
39
|
+
projects: [
|
|
40
|
+
{
|
|
41
|
+
name: "chromium",
|
|
42
|
+
use: { ...devices["Desktop Chrome"] },
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
name: "firefox",
|
|
47
|
+
use: { ...devices["Desktop Firefox"] },
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
name: "webkit",
|
|
52
|
+
use: { ...devices["Desktop Safari"] },
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// Uncomment for mobile browsers support
|
|
56
|
+
/* {
|
|
57
|
+
name: 'Mobile Chrome',
|
|
58
|
+
use: { ...devices['Pixel 5'] },
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Mobile Safari',
|
|
62
|
+
use: { ...devices['iPhone 12'] },
|
|
63
|
+
}, */
|
|
64
|
+
|
|
65
|
+
// Uncomment for branded browsers
|
|
66
|
+
/* {
|
|
67
|
+
name: 'Microsoft Edge',
|
|
68
|
+
use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Google Chrome',
|
|
72
|
+
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
|
73
|
+
} */
|
|
74
|
+
],
|
|
38
75
|
});
|
|
@@ -9,6 +9,7 @@ export interface ConfigurationGeneratorSchema {
|
|
|
9
9
|
js: boolean; // default is false
|
|
10
10
|
skipFormat: boolean;
|
|
11
11
|
skipPackageJson: boolean;
|
|
12
|
+
skipInstall?: boolean;
|
|
12
13
|
linter: Linter;
|
|
13
14
|
setParserOptionsProject: boolean; // default is false
|
|
14
15
|
/**
|
|
@@ -21,4 +22,6 @@ export interface ConfigurationGeneratorSchema {
|
|
|
21
22
|
* @example: "http://localhost:4200"
|
|
22
23
|
**/
|
|
23
24
|
webServerAddress?: string;
|
|
25
|
+
rootProject?: boolean;
|
|
26
|
+
addPlugin?: boolean;
|
|
24
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
3
|
"$id": "NxPlaywrightConfiguration",
|
|
4
4
|
"description": "Add a Playwright configuration.",
|
|
5
5
|
"title": "Add a Playwright configuration",
|
|
@@ -55,6 +55,18 @@
|
|
|
55
55
|
"default": false,
|
|
56
56
|
"description": "Do not add dependencies to `package.json`.",
|
|
57
57
|
"x-priority": "internal"
|
|
58
|
+
},
|
|
59
|
+
"rootProject": {
|
|
60
|
+
"description": "Create a application at the root of the workspace",
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"default": false,
|
|
63
|
+
"hidden": true,
|
|
64
|
+
"x-priority": "internal"
|
|
65
|
+
},
|
|
66
|
+
"skipInstall": {
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"description": "Skip running `playwright install`. This is to ensure that playwright browsers are installed.",
|
|
69
|
+
"default": false
|
|
58
70
|
}
|
|
59
71
|
},
|
|
60
72
|
"required": ["project"]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GeneratorCallback, Tree } from '@nx/devkit';
|
|
2
2
|
import { InitGeneratorSchema } from './schema';
|
|
3
3
|
export declare function initGenerator(tree: Tree, options: InitGeneratorSchema): Promise<GeneratorCallback>;
|
|
4
|
+
export declare function initGeneratorInternal(tree: Tree, options: InitGeneratorSchema): Promise<GeneratorCallback>;
|
|
4
5
|
export default initGenerator;
|
|
@@ -1,47 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initGenerator = void 0;
|
|
3
|
+
exports.initGeneratorInternal = exports.initGenerator = void 0;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const update_package_scripts_1 = require("@nx/devkit/src/utils/update-package-scripts");
|
|
6
|
+
const plugin_1 = require("../../plugins/plugin");
|
|
5
7
|
const versions_1 = require("../../utils/versions");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
function initGenerator(tree, options) {
|
|
9
|
+
return initGeneratorInternal(tree, { addPlugin: false, ...options });
|
|
10
|
+
}
|
|
11
|
+
exports.initGenerator = initGenerator;
|
|
12
|
+
async function initGeneratorInternal(tree, options) {
|
|
8
13
|
const tasks = [];
|
|
14
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
15
|
+
const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
16
|
+
nxJson.useInferencePlugins !== false;
|
|
17
|
+
options.addPlugin ??= addPluginDefault;
|
|
9
18
|
if (!options.skipPackageJson) {
|
|
10
19
|
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
11
20
|
'@nx/playwright': versions_1.nxVersion,
|
|
12
|
-
// required since used in playwright config
|
|
13
|
-
'@nx/devkit': versions_1.nxVersion,
|
|
14
21
|
'@playwright/test': versions_1.playwrightVersion,
|
|
15
|
-
}));
|
|
22
|
+
}, undefined, options.keepExistingVersions));
|
|
16
23
|
}
|
|
17
|
-
if (
|
|
18
|
-
|
|
24
|
+
if (options.addPlugin) {
|
|
25
|
+
addPlugin(tree);
|
|
19
26
|
}
|
|
20
|
-
if (
|
|
21
|
-
(0,
|
|
22
|
-
json.recommendations ??= [];
|
|
23
|
-
const recs = new Set(json.recommendations);
|
|
24
|
-
recs.add('ms-playwright.playwright');
|
|
25
|
-
json.recommendations = Array.from(recs);
|
|
26
|
-
return json;
|
|
27
|
-
});
|
|
27
|
+
if (options.updatePackageScripts) {
|
|
28
|
+
await (0, update_package_scripts_1.updatePackageScripts)(tree, plugin_1.createNodes);
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
recommendations: ['ms-playwright.playwright'],
|
|
32
|
-
}, null, 2));
|
|
30
|
+
if (!options.skipFormat) {
|
|
31
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
34
|
+
}
|
|
35
|
+
exports.initGeneratorInternal = initGeneratorInternal;
|
|
36
|
+
function addPlugin(tree) {
|
|
37
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
38
|
+
nxJson.plugins ??= [];
|
|
39
|
+
if (!nxJson.plugins.some((p) => typeof p === 'string'
|
|
40
|
+
? p === '@nx/playwright/plugin'
|
|
41
|
+
: p.plugin === '@nx/playwright/plugin')) {
|
|
42
|
+
nxJson.plugins.push({
|
|
43
|
+
plugin: '@nx/playwright/plugin',
|
|
44
|
+
options: {
|
|
45
|
+
targetName: 'e2e',
|
|
46
|
+
},
|
|
42
47
|
});
|
|
48
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
43
49
|
}
|
|
44
|
-
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
45
50
|
}
|
|
46
|
-
exports.initGenerator = initGenerator;
|
|
47
51
|
exports.default = initGenerator;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export interface InitGeneratorSchema {
|
|
2
|
-
skipFormat
|
|
3
|
-
skipPackageJson
|
|
4
|
-
|
|
2
|
+
skipFormat?: boolean;
|
|
3
|
+
skipPackageJson?: boolean;
|
|
4
|
+
keepExistingVersions?: boolean;
|
|
5
|
+
updatePackageScripts?: boolean;
|
|
6
|
+
addPlugin?: boolean;
|
|
5
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
3
|
"$id": "NxPlaywrightInit",
|
|
4
4
|
"title": "Playwright Init Generator",
|
|
5
5
|
"description": "Initializes a Playwright project in the current workspace.",
|
|
@@ -17,9 +17,16 @@
|
|
|
17
17
|
"description": "Do not add dependencies to `package.json`.",
|
|
18
18
|
"x-priority": "internal"
|
|
19
19
|
},
|
|
20
|
-
"
|
|
20
|
+
"keepExistingVersions": {
|
|
21
21
|
"type": "boolean",
|
|
22
|
-
"
|
|
22
|
+
"x-priority": "internal",
|
|
23
|
+
"description": "Keep existing dependencies versions",
|
|
24
|
+
"default": false
|
|
25
|
+
},
|
|
26
|
+
"updatePackageScripts": {
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"x-priority": "internal",
|
|
29
|
+
"description": "Update `package.json` scripts with inferred targets",
|
|
23
30
|
"default": false
|
|
24
31
|
}
|
|
25
32
|
},
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const devkit_1 = require("@nx/devkit");
|
|
4
|
+
const ts = require("typescript");
|
|
5
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
6
|
+
async function update(tree) {
|
|
7
|
+
const projects = (0, devkit_1.getProjects)(tree);
|
|
8
|
+
projects.forEach((project) => {
|
|
9
|
+
// Check if the project contains playwright config
|
|
10
|
+
const configPath = (0, devkit_1.joinPathFragments)(project.root, 'playwright.config.ts');
|
|
11
|
+
if (tree.exists(configPath)) {
|
|
12
|
+
addProjectIfExists(tree, (0, devkit_1.joinPathFragments)(configPath));
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
exports.default = update;
|
|
17
|
+
function addProjectIfExists(tree, configFilePath) {
|
|
18
|
+
const configFileContent = tree.read(configFilePath, 'utf-8');
|
|
19
|
+
const sourceFile = tsquery_1.tsquery.ast(configFileContent);
|
|
20
|
+
const printer = ts.createPrinter();
|
|
21
|
+
const updatedStatements = updateOrCreateImportStatement(sourceFile, '@playwright/test', ['devices']);
|
|
22
|
+
const exportAssignment = tsquery_1.tsquery.query(sourceFile, 'ExportAssignment')[0];
|
|
23
|
+
if (!exportAssignment) {
|
|
24
|
+
// No export found in the file
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const exportAssignemntObject = exportAssignment.expression;
|
|
28
|
+
if (!(ts.isCallExpression(exportAssignemntObject) &&
|
|
29
|
+
exportAssignemntObject.getText(sourceFile).startsWith('defineConfig') &&
|
|
30
|
+
exportAssignemntObject.arguments.length > 0)) {
|
|
31
|
+
// Export is not a call expression with defineConfig ex. export default defineConfig({ ... })
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let firstArgument = exportAssignemntObject.arguments[0];
|
|
35
|
+
if (!ts.isObjectLiteralExpression(firstArgument)) {
|
|
36
|
+
// First argument is not an object literal ex. defineConfig('foo')
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const projectProperty = tsquery_1.tsquery.query(exportAssignemntObject, 'PropertyAssignment > Identifier[name="projects"]')[0];
|
|
40
|
+
if (projectProperty) {
|
|
41
|
+
// Projects property already exists in the config
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Add projects property to the config
|
|
45
|
+
const projectsArray = ts.factory.createArrayLiteralExpression([
|
|
46
|
+
createProperty('chromium', 'Desktop Chrome'),
|
|
47
|
+
createProperty('firefox', 'Desktop Firefox'),
|
|
48
|
+
createProperty('webkit', 'Desktop Safari'),
|
|
49
|
+
], true);
|
|
50
|
+
const newProjectsProperty = ts.factory.createPropertyAssignment('projects', projectsArray);
|
|
51
|
+
const newObj = ts.factory.createObjectLiteralExpression([
|
|
52
|
+
...firstArgument.properties,
|
|
53
|
+
newProjectsProperty,
|
|
54
|
+
]);
|
|
55
|
+
const newCallExpression = ts.factory.updateCallExpression(exportAssignemntObject, exportAssignemntObject.expression, exportAssignemntObject.typeArguments, [newObj]);
|
|
56
|
+
const newExportAssignment = ts.factory.updateExportAssignment(exportAssignment, exportAssignment.modifiers, newCallExpression);
|
|
57
|
+
const transformedStatements = updatedStatements.map((statement) => {
|
|
58
|
+
return statement === exportAssignment ? newExportAssignment : statement;
|
|
59
|
+
});
|
|
60
|
+
const transformedSourceFile = ts.factory.updateSourceFile(sourceFile, transformedStatements);
|
|
61
|
+
const updatedConfigFileContent = printer.printFile(transformedSourceFile);
|
|
62
|
+
tree.write(configFilePath, updatedConfigFileContent);
|
|
63
|
+
}
|
|
64
|
+
function createProperty(name, device) {
|
|
65
|
+
return ts.factory.createObjectLiteralExpression([
|
|
66
|
+
ts.factory.createPropertyAssignment('name', ts.factory.createStringLiteral(name)),
|
|
67
|
+
ts.factory.createPropertyAssignment('use', ts.factory.createObjectLiteralExpression([
|
|
68
|
+
ts.factory.createSpreadAssignment(ts.factory.createElementAccessExpression(ts.factory.createIdentifier('devices'), ts.factory.createStringLiteral(device))),
|
|
69
|
+
])),
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
function updateOrCreateImportStatement(sourceFile, moduleName, importNames) {
|
|
73
|
+
let importDeclarationFound = false;
|
|
74
|
+
const newStatements = sourceFile.statements.map((statement) => {
|
|
75
|
+
if (ts.isImportDeclaration(statement) &&
|
|
76
|
+
statement.moduleSpecifier.getText(sourceFile) === `'${moduleName}'`) {
|
|
77
|
+
importDeclarationFound = true;
|
|
78
|
+
const existingSpecifiers = statement.importClause?.namedBindings &&
|
|
79
|
+
ts.isNamedImports(statement.importClause.namedBindings)
|
|
80
|
+
? statement.importClause.namedBindings.elements.map((e) => e.name.text)
|
|
81
|
+
: [];
|
|
82
|
+
// Merge with new import names, avoiding duplicates
|
|
83
|
+
const mergedImportNames = Array.from(new Set([...existingSpecifiers, ...importNames]));
|
|
84
|
+
// Create new import specifiers
|
|
85
|
+
const importSpecifiers = mergedImportNames.map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)));
|
|
86
|
+
return ts.factory.updateImportDeclaration(statement, statement.modifiers, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(importSpecifiers)), statement.moduleSpecifier, undefined);
|
|
87
|
+
}
|
|
88
|
+
return statement;
|
|
89
|
+
});
|
|
90
|
+
if (!importDeclarationFound) {
|
|
91
|
+
const importDeclaration = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(importNames.map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))))), ts.factory.createStringLiteral(moduleName));
|
|
92
|
+
newStatements.push(importDeclaration);
|
|
93
|
+
}
|
|
94
|
+
return newStatements;
|
|
95
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const devkit_1 = require("@nx/devkit");
|
|
4
|
+
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
5
|
+
async function default_1(tree) {
|
|
6
|
+
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/playwright:playwright', (options, projectName, targetName, configurationName) => {
|
|
7
|
+
if (options?.['baseUrl']) {
|
|
8
|
+
const project = (0, devkit_1.readProjectConfiguration)(tree, projectName);
|
|
9
|
+
if (configurationName) {
|
|
10
|
+
delete project.targets[targetName].configurations[configurationName]['baseUrl'];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
delete project.targets[targetName].options['baseUrl'];
|
|
14
|
+
}
|
|
15
|
+
(0, devkit_1.updateProjectConfiguration)(tree, projectName, project);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
19
|
+
for (const [targetNameOrExecutor, target] of Object.entries(nxJson.targetDefaults)) {
|
|
20
|
+
if (targetNameOrExecutor === '@nx/playwright:playwright' ||
|
|
21
|
+
(target.executor && target.executor === '@nx/playwright:playwright')) {
|
|
22
|
+
let updated = false;
|
|
23
|
+
if (target.options?.['baseUrl']) {
|
|
24
|
+
delete nxJson.targetDefaults[targetNameOrExecutor].options['baseUrl'];
|
|
25
|
+
updated = true;
|
|
26
|
+
}
|
|
27
|
+
if (target.configurations) {
|
|
28
|
+
for (const [configurationName, configuration] of Object.entries(target.configurations)) {
|
|
29
|
+
if (configuration['baseUrl']) {
|
|
30
|
+
delete nxJson.targetDefaults[targetNameOrExecutor].configurations[configurationName]['baseUrl'];
|
|
31
|
+
updated = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (updated) {
|
|
36
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
41
|
+
}
|
|
42
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CreateDependencies, CreateNodes } from '@nx/devkit';
|
|
2
|
+
export interface PlaywrightPluginOptions {
|
|
3
|
+
targetName?: string;
|
|
4
|
+
ciTargetName?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const createDependencies: CreateDependencies;
|
|
7
|
+
export declare const createNodes: CreateNodes<PlaywrightPluginOptions>;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodes = exports.createDependencies = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const devkit_1 = require("@nx/devkit");
|
|
7
|
+
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
|
8
|
+
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
|
9
|
+
const workspace_context_1 = require("nx/src/utils/workspace-context");
|
|
10
|
+
const minimatch_1 = require("minimatch");
|
|
11
|
+
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
|
12
|
+
const js_1 = require("@nx/js");
|
|
13
|
+
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
|
14
|
+
const cachePath = (0, path_1.join)(cache_directory_1.projectGraphCacheDirectory, 'playwright.hash');
|
|
15
|
+
const targetsCache = (0, fs_1.existsSync)(cachePath) ? readTargetsCache() : {};
|
|
16
|
+
const calculatedTargets = {};
|
|
17
|
+
function readTargetsCache() {
|
|
18
|
+
return (0, devkit_1.readJsonFile)(cachePath);
|
|
19
|
+
}
|
|
20
|
+
function writeTargetsToCache(targets) {
|
|
21
|
+
(0, devkit_1.writeJsonFile)(cachePath, targets);
|
|
22
|
+
}
|
|
23
|
+
const createDependencies = () => {
|
|
24
|
+
writeTargetsToCache(calculatedTargets);
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
exports.createDependencies = createDependencies;
|
|
28
|
+
exports.createNodes = [
|
|
29
|
+
'**/playwright.config.{js,ts,cjs,cts,mjs,mts}',
|
|
30
|
+
async (configFilePath, options, context) => {
|
|
31
|
+
const projectRoot = (0, path_1.dirname)(configFilePath);
|
|
32
|
+
// Do not create a project if package.json and project.json isn't there.
|
|
33
|
+
const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
|
|
34
|
+
if (!siblingFiles.includes('package.json') &&
|
|
35
|
+
!siblingFiles.includes('project.json')) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const normalizedOptions = normalizeOptions(options);
|
|
39
|
+
const hash = (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context, [
|
|
40
|
+
(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot)),
|
|
41
|
+
]);
|
|
42
|
+
const targets = targetsCache[hash] ??
|
|
43
|
+
(await buildPlaywrightTargets(configFilePath, projectRoot, normalizedOptions, context));
|
|
44
|
+
calculatedTargets[hash] = targets;
|
|
45
|
+
return {
|
|
46
|
+
projects: {
|
|
47
|
+
[projectRoot]: {
|
|
48
|
+
root: projectRoot,
|
|
49
|
+
targets,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
async function buildPlaywrightTargets(configFilePath, projectRoot, options, context) {
|
|
56
|
+
// Playwright forbids importing the `@playwright/test` module twice. This would affect running the tests,
|
|
57
|
+
// but we're just reading the config so let's delete the variable they are using to detect this.
|
|
58
|
+
// See: https://github.com/microsoft/playwright/pull/11218/files
|
|
59
|
+
delete process['__pw_initiator__'];
|
|
60
|
+
const playwrightConfig = await (0, config_utils_1.loadConfigFile)((0, path_1.join)(context.workspaceRoot, configFilePath));
|
|
61
|
+
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
|
|
62
|
+
const targets = {};
|
|
63
|
+
const baseTargetConfig = {
|
|
64
|
+
command: 'playwright test',
|
|
65
|
+
options: {
|
|
66
|
+
cwd: '{projectRoot}',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
targets[options.targetName] = {
|
|
70
|
+
...baseTargetConfig,
|
|
71
|
+
cache: true,
|
|
72
|
+
inputs: 'production' in namedInputs
|
|
73
|
+
? ['default', '^production']
|
|
74
|
+
: ['default', '^default'],
|
|
75
|
+
outputs: getOutputs(projectRoot, playwrightConfig),
|
|
76
|
+
};
|
|
77
|
+
if (options.ciTargetName) {
|
|
78
|
+
const ciBaseTargetConfig = {
|
|
79
|
+
...baseTargetConfig,
|
|
80
|
+
cache: true,
|
|
81
|
+
inputs: 'production' in namedInputs
|
|
82
|
+
? ['default', '^production']
|
|
83
|
+
: ['default', '^default'],
|
|
84
|
+
outputs: getOutputs(projectRoot, playwrightConfig),
|
|
85
|
+
};
|
|
86
|
+
const testDir = playwrightConfig.testDir
|
|
87
|
+
? (0, devkit_1.joinPathFragments)(projectRoot, playwrightConfig.testDir)
|
|
88
|
+
: projectRoot;
|
|
89
|
+
// Playwright defaults to the following pattern.
|
|
90
|
+
playwrightConfig.testMatch ??= '**/*.@(spec|test).?(c|m)[jt]s?(x)';
|
|
91
|
+
const dependsOn = [];
|
|
92
|
+
forEachTestFile((testFile) => {
|
|
93
|
+
const relativeToProjectRoot = (0, devkit_1.normalizePath)((0, path_1.relative)(projectRoot, testFile));
|
|
94
|
+
const targetName = `${options.ciTargetName}--${relativeToProjectRoot}`;
|
|
95
|
+
targets[targetName] = {
|
|
96
|
+
...ciBaseTargetConfig,
|
|
97
|
+
command: `${baseTargetConfig.command} ${relativeToProjectRoot}`,
|
|
98
|
+
};
|
|
99
|
+
dependsOn.push({
|
|
100
|
+
target: targetName,
|
|
101
|
+
projects: 'self',
|
|
102
|
+
params: 'forward',
|
|
103
|
+
});
|
|
104
|
+
}, {
|
|
105
|
+
context,
|
|
106
|
+
path: testDir,
|
|
107
|
+
config: playwrightConfig,
|
|
108
|
+
});
|
|
109
|
+
targets[options.ciTargetName] ??= {};
|
|
110
|
+
targets[options.ciTargetName] = {
|
|
111
|
+
executor: 'nx:noop',
|
|
112
|
+
cache: ciBaseTargetConfig.cache,
|
|
113
|
+
inputs: ciBaseTargetConfig.inputs,
|
|
114
|
+
outputs: ciBaseTargetConfig.outputs,
|
|
115
|
+
dependsOn,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return targets;
|
|
119
|
+
}
|
|
120
|
+
async function forEachTestFile(cb, opts) {
|
|
121
|
+
const files = (0, workspace_context_1.getFilesInDirectoryUsingContext)(opts.context.workspaceRoot, opts.path);
|
|
122
|
+
const matcher = createMatcher(opts.config.testMatch);
|
|
123
|
+
const ignoredMatcher = opts.config.testIgnore
|
|
124
|
+
? createMatcher(opts.config.testIgnore)
|
|
125
|
+
: () => false;
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
if (matcher(file) && !ignoredMatcher(file)) {
|
|
128
|
+
cb(file);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function createMatcher(pattern) {
|
|
133
|
+
if (Array.isArray(pattern)) {
|
|
134
|
+
const matchers = pattern.map((p) => createMatcher(p));
|
|
135
|
+
return (path) => matchers.some((m) => m(path));
|
|
136
|
+
}
|
|
137
|
+
else if (pattern instanceof RegExp) {
|
|
138
|
+
return (path) => pattern.test(path);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
return (path) => {
|
|
142
|
+
try {
|
|
143
|
+
return (0, minimatch_1.minimatch)(path, pattern);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
throw new Error(`Error matching ${path} with ${pattern}: ${e.message}`);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function getOutputs(projectRoot, playwrightConfig) {
|
|
152
|
+
function getOutput(path) {
|
|
153
|
+
if (path.startsWith('..')) {
|
|
154
|
+
return (0, path_1.join)('{workspaceRoot}', (0, path_1.join)(projectRoot, path));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
return (0, path_1.join)('{projectRoot}', path);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const outputs = [];
|
|
161
|
+
const { reporter, outputDir } = playwrightConfig;
|
|
162
|
+
if (reporter) {
|
|
163
|
+
const DEFAULT_REPORTER_OUTPUT = getOutput('playwright-report');
|
|
164
|
+
if (reporter === 'html' || reporter === 'json') {
|
|
165
|
+
// Reporter is a string, so it uses the default output directory.
|
|
166
|
+
outputs.push(DEFAULT_REPORTER_OUTPUT);
|
|
167
|
+
}
|
|
168
|
+
else if (Array.isArray(reporter)) {
|
|
169
|
+
for (const r of reporter) {
|
|
170
|
+
const [, opts] = r;
|
|
171
|
+
// There are a few different ways to specify an output file or directory
|
|
172
|
+
// depending on the reporter. This is a best effort to find the output.
|
|
173
|
+
if (!opts) {
|
|
174
|
+
outputs.push(DEFAULT_REPORTER_OUTPUT);
|
|
175
|
+
}
|
|
176
|
+
else if (opts.outputFile) {
|
|
177
|
+
outputs.push(getOutput(opts.outputFile));
|
|
178
|
+
}
|
|
179
|
+
else if (opts.outputDir) {
|
|
180
|
+
outputs.push(getOutput(opts.outputDir));
|
|
181
|
+
}
|
|
182
|
+
else if (opts.outputFolder) {
|
|
183
|
+
outputs.push(getOutput(opts.outputFolder));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
outputs.push(DEFAULT_REPORTER_OUTPUT);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (outputDir) {
|
|
192
|
+
outputs.push(getOutput(outputDir));
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
outputs.push(getOutput('./test-results'));
|
|
196
|
+
}
|
|
197
|
+
return outputs;
|
|
198
|
+
}
|
|
199
|
+
function normalizeOptions(options) {
|
|
200
|
+
return {
|
|
201
|
+
...options,
|
|
202
|
+
targetName: options.targetName ?? 'e2e',
|
|
203
|
+
ciTargetName: options.ciTargetName ?? 'e2e-ci',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -11,5 +11,6 @@ export interface PlaywrightLinterOptions {
|
|
|
11
11
|
* Directory from the project root, where the playwright files will be located.
|
|
12
12
|
**/
|
|
13
13
|
directory: string;
|
|
14
|
+
addPlugin?: boolean;
|
|
14
15
|
}
|
|
15
16
|
export declare function addLinterToPlaywrightProject(tree: Tree, options: PlaywrightLinterOptions): Promise<GeneratorCallback>;
|
package/src/utils/add-linter.js
CHANGED
|
@@ -19,12 +19,10 @@ async function addLinterToPlaywrightProject(tree, options) {
|
|
|
19
19
|
linter: options.linter,
|
|
20
20
|
skipFormat: true,
|
|
21
21
|
tsConfigPaths: [(0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json')],
|
|
22
|
-
eslintFilePatterns: [
|
|
23
|
-
`${projectConfig.root}/**/*.${options.js ? 'js' : '{js,ts}'}`,
|
|
24
|
-
],
|
|
25
22
|
setParserOptionsProject: options.setParserOptionsProject,
|
|
26
23
|
skipPackageJson: options.skipPackageJson,
|
|
27
24
|
rootProject: options.rootProject,
|
|
25
|
+
addPlugin: options.addPlugin,
|
|
28
26
|
}));
|
|
29
27
|
}
|
|
30
28
|
if (!options.linter || options.linter !== eslint_1.Linter.EsLint) {
|
package/src/utils/preset.d.ts
CHANGED
|
@@ -4,16 +4,6 @@ export interface NxPlaywrightOptions {
|
|
|
4
4
|
* @default './src'
|
|
5
5
|
**/
|
|
6
6
|
testDir?: string;
|
|
7
|
-
/**
|
|
8
|
-
* Include Mobile Chome and Mobile Safari browsers in test projects
|
|
9
|
-
* @default false
|
|
10
|
-
**/
|
|
11
|
-
includeMobileBrowsers?: boolean;
|
|
12
|
-
/**
|
|
13
|
-
* Include Microsoft Edge and Google Chrome browsers in test projects
|
|
14
|
-
* @default false
|
|
15
|
-
**/
|
|
16
|
-
includeBrandedBrowsers?: boolean;
|
|
17
7
|
}
|
|
18
8
|
/**
|
|
19
9
|
* nx E2E Preset for Playwright
|
|
@@ -25,6 +15,7 @@ export interface NxPlaywrightOptions {
|
|
|
25
15
|
* - chromium
|
|
26
16
|
* - firefox
|
|
27
17
|
* - webkit
|
|
18
|
+
* These are generated by default.
|
|
28
19
|
*
|
|
29
20
|
* you can easily extend this within your playwright config via spreading the preset
|
|
30
21
|
* @example
|
package/src/utils/preset.js
CHANGED
|
@@ -15,6 +15,7 @@ const test_1 = require("@playwright/test");
|
|
|
15
15
|
* - chromium
|
|
16
16
|
* - firefox
|
|
17
17
|
* - webkit
|
|
18
|
+
* These are generated by default.
|
|
18
19
|
*
|
|
19
20
|
* you can easily extend this within your playwright config via spreading the preset
|
|
20
21
|
* @example
|
|
@@ -34,44 +35,6 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
34
35
|
const offset = (0, node_path_1.relative)(normalizedPath, devkit_1.workspaceRoot);
|
|
35
36
|
const testResultOuputDir = (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'test-output');
|
|
36
37
|
const reporterOutputDir = (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'playwright-report');
|
|
37
|
-
const projects = [
|
|
38
|
-
{
|
|
39
|
-
name: 'chromium',
|
|
40
|
-
use: { ...test_1.devices['Desktop Chrome'] },
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'firefox',
|
|
44
|
-
use: { ...test_1.devices['Desktop Firefox'] },
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: 'webkit',
|
|
48
|
-
use: { ...test_1.devices['Desktop Safari'] },
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
if (options?.includeMobileBrowsers) {
|
|
52
|
-
projects.push(...[
|
|
53
|
-
{
|
|
54
|
-
name: 'Mobile Chrome',
|
|
55
|
-
use: { ...test_1.devices['Pixel 5'] },
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: 'Mobile Safari',
|
|
59
|
-
use: { ...test_1.devices['iPhone 12'] },
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
}
|
|
63
|
-
if (options?.includeBrandedBrowsers) {
|
|
64
|
-
projects.push(...[
|
|
65
|
-
{
|
|
66
|
-
name: 'Microsoft Edge',
|
|
67
|
-
use: { ...test_1.devices['Desktop Edge'], channel: 'msedge' },
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
name: 'Google Chrome',
|
|
71
|
-
use: { ...test_1.devices['Desktop Chrome'], channel: 'chrome' },
|
|
72
|
-
},
|
|
73
|
-
]);
|
|
74
|
-
}
|
|
75
38
|
return (0, test_1.defineConfig)({
|
|
76
39
|
testDir: options?.testDir ?? './src',
|
|
77
40
|
outputDir: testResultOuputDir,
|
|
@@ -92,7 +55,6 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
92
55
|
},
|
|
93
56
|
],
|
|
94
57
|
],
|
|
95
|
-
projects,
|
|
96
58
|
});
|
|
97
59
|
}
|
|
98
60
|
exports.nxE2EPreset = nxE2EPreset;
|