@nx/playwright 0.0.0-pr-31222-862e973 → 0.0.0-pr-31313-387cdca
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/generators.json +2 -1
- package/migrations.json +0 -12
- package/package.json +4 -6
- package/src/executors/playwright/playwright.impl.js +1 -1
- package/src/generators/configuration/configuration.d.ts +2 -2
- package/src/generators/configuration/configuration.js +147 -15
- package/src/generators/configuration/files/playwright.config.ts.template +3 -4
- package/src/generators/configuration/schema.d.ts +13 -6
- package/src/generators/configuration/schema.json +6 -6
- package/src/migrations/update-19-6-0/add-e2e-ci-target-defaults.js +2 -2
- package/src/migrations/update-19-6-0/use-serve-static-preview-for-command.js +5 -6
- package/src/plugins/plugin.js +157 -49
- package/src/utils/add-linter.js +2 -2
- package/src/utils/preset.js +9 -3
- package/README.md +0 -66
- package/src/migrations/update-17-3-1/add-project-to-config.d.ts +0 -2
- package/src/migrations/update-17-3-1/add-project-to-config.js +0 -95
- package/src/migrations/update-18-1-0/remove-baseUrl-from-project-json.d.ts +0 -2
- package/src/migrations/update-18-1-0/remove-baseUrl-from-project-json.js +0 -42
package/LICENSE
CHANGED
package/generators.json
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"init": {
|
|
11
11
|
"factory": "./src/generators/init/init#initGeneratorInternal",
|
|
12
12
|
"schema": "./src/generators/init/schema.json",
|
|
13
|
-
"description": "Initializes a Playwright project in the current workspace"
|
|
13
|
+
"description": "Initializes a Playwright project in the current workspace",
|
|
14
|
+
"hidden": true
|
|
14
15
|
},
|
|
15
16
|
"convert-to-inferred": {
|
|
16
17
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
package/migrations.json
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
{
|
|
2
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
3
|
"19-6-0-use-serve-static-preview-for-command": {
|
|
16
4
|
"cli": "nx",
|
|
17
5
|
"version": "19.6.0-beta.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/playwright",
|
|
3
|
-
"version": "0.0.0-pr-
|
|
3
|
+
"version": "0.0.0-pr-31313-387cdca",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"homepage": "https://nx.dev",
|
|
6
6
|
"private": false,
|
|
@@ -35,11 +35,9 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
38
|
-
"@nx/devkit": "0.0.0-pr-
|
|
39
|
-
"@nx/eslint": "0.0.0-pr-
|
|
40
|
-
"@nx/
|
|
41
|
-
"@nx/vite": "0.0.0-pr-31222-862e973",
|
|
42
|
-
"@nx/js": "0.0.0-pr-31222-862e973",
|
|
38
|
+
"@nx/devkit": "0.0.0-pr-31313-387cdca",
|
|
39
|
+
"@nx/eslint": "0.0.0-pr-31313-387cdca",
|
|
40
|
+
"@nx/js": "0.0.0-pr-31313-387cdca",
|
|
43
41
|
"tslib": "^2.3.0",
|
|
44
42
|
"minimatch": "9.0.3"
|
|
45
43
|
},
|
|
@@ -17,7 +17,7 @@ async function playwrightExecutor(options, context) {
|
|
|
17
17
|
(0, child_process_1.execSync)(`${pmc.exec} playwright install`, {
|
|
18
18
|
cwd: devkit_1.workspaceRoot,
|
|
19
19
|
stdio: 'inherit',
|
|
20
|
-
windowsHide:
|
|
20
|
+
windowsHide: false,
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
const args = createArgs(options);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GeneratorCallback, Tree } from '@nx/devkit';
|
|
2
|
-
import { ConfigurationGeneratorSchema } from './schema';
|
|
2
|
+
import type { ConfigurationGeneratorSchema } from './schema';
|
|
3
3
|
export declare function configurationGenerator(tree: Tree, options: ConfigurationGeneratorSchema): Promise<GeneratorCallback>;
|
|
4
|
-
export declare function configurationGeneratorInternal(tree: Tree,
|
|
4
|
+
export declare function configurationGeneratorInternal(tree: Tree, rawOptions: ConfigurationGeneratorSchema): Promise<GeneratorCallback>;
|
|
5
5
|
export default configurationGenerator;
|
|
@@ -3,21 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.configurationGenerator = configurationGenerator;
|
|
4
4
|
exports.configurationGeneratorInternal = configurationGeneratorInternal;
|
|
5
5
|
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const project_name_and_root_utils_1 = require("@nx/devkit/src/generators/project-name-and-root-utils");
|
|
7
|
+
const prompt_1 = require("@nx/devkit/src/generators/prompt");
|
|
6
8
|
const js_1 = require("@nx/js");
|
|
7
|
-
const
|
|
9
|
+
const generator_prompts_1 = require("@nx/js/src/utils/generator-prompts");
|
|
10
|
+
const package_manager_workspaces_1 = require("@nx/js/src/utils/package-manager-workspaces");
|
|
11
|
+
const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript");
|
|
12
|
+
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
|
|
8
13
|
const child_process_1 = require("child_process");
|
|
9
14
|
const path = require("path");
|
|
10
15
|
const add_linter_1 = require("../../utils/add-linter");
|
|
11
|
-
const
|
|
16
|
+
const versions_1 = require("../../utils/versions");
|
|
12
17
|
const init_1 = require("../init/init");
|
|
18
|
+
const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
|
|
13
19
|
function configurationGenerator(tree, options) {
|
|
14
20
|
return configurationGeneratorInternal(tree, { addPlugin: false, ...options });
|
|
15
21
|
}
|
|
16
|
-
async function configurationGeneratorInternal(tree,
|
|
17
|
-
const
|
|
18
|
-
options.addPlugin ??=
|
|
19
|
-
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
20
|
-
nxJson.useInferencePlugins !== false;
|
|
22
|
+
async function configurationGeneratorInternal(tree, rawOptions) {
|
|
23
|
+
const options = await normalizeOptions(tree, rawOptions);
|
|
21
24
|
const tasks = [];
|
|
22
25
|
tasks.push(await (0, init_1.initGenerator)(tree, {
|
|
23
26
|
skipFormat: true,
|
|
@@ -25,7 +28,6 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
25
28
|
addPlugin: options.addPlugin,
|
|
26
29
|
}));
|
|
27
30
|
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
|
|
28
|
-
const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
|
|
29
31
|
const offsetFromProjectRoot = (0, devkit_1.offsetFromRoot)(projectConfig.root);
|
|
30
32
|
(0, devkit_1.generateFiles)(tree, path.join(__dirname, 'files'), projectConfig.root, {
|
|
31
33
|
offsetFromRoot: offsetFromProjectRoot,
|
|
@@ -34,13 +36,43 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
34
36
|
webServerAddress: options.webServerAddress ?? null,
|
|
35
37
|
...options,
|
|
36
38
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
const isTsSolutionSetup = (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree);
|
|
40
|
+
const tsconfigPath = (0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json');
|
|
41
|
+
if (tree.exists(tsconfigPath)) {
|
|
42
|
+
if (isTsSolutionSetup) {
|
|
43
|
+
const tsconfig = {
|
|
44
|
+
extends: (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
|
|
45
|
+
compilerOptions: {
|
|
46
|
+
allowJs: true,
|
|
47
|
+
outDir: 'out-tsc/playwright',
|
|
48
|
+
sourceMap: false,
|
|
49
|
+
},
|
|
50
|
+
include: [
|
|
51
|
+
(0, devkit_1.joinPathFragments)(options.directory, '**/*.ts'),
|
|
52
|
+
(0, devkit_1.joinPathFragments)(options.directory, '**/*.js'),
|
|
53
|
+
'playwright.config.ts',
|
|
54
|
+
],
|
|
55
|
+
exclude: ['out-tsc', 'test-output'],
|
|
56
|
+
};
|
|
57
|
+
// skip eslint from typechecking since it extends from root file that is outside rootDir
|
|
58
|
+
if (options.linter === 'eslint') {
|
|
59
|
+
tsconfig.exclude.push('eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs');
|
|
60
|
+
}
|
|
61
|
+
(0, devkit_1.writeJson)(tree, (0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.e2e.json'), tsconfig);
|
|
62
|
+
(0, devkit_1.updateJson)(tree, tsconfigPath, (json) => {
|
|
63
|
+
// add the project tsconfig to the workspace root tsconfig.json references
|
|
64
|
+
json.references ??= [];
|
|
65
|
+
json.references.push({ path: './tsconfig.e2e.json' });
|
|
66
|
+
return json;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const tsconfig = {
|
|
39
72
|
extends: (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
|
|
40
73
|
compilerOptions: {
|
|
41
74
|
allowJs: true,
|
|
42
75
|
outDir: `${offsetFromProjectRoot}dist/out-tsc`,
|
|
43
|
-
module: 'commonjs',
|
|
44
76
|
sourceMap: false,
|
|
45
77
|
},
|
|
46
78
|
include: [
|
|
@@ -53,7 +85,44 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
53
85
|
'src/**/*.test.js',
|
|
54
86
|
'src/**/*.d.ts',
|
|
55
87
|
],
|
|
56
|
-
}
|
|
88
|
+
};
|
|
89
|
+
if (isTsSolutionSetup) {
|
|
90
|
+
tsconfig.exclude = ['out-tsc', 'test-output'];
|
|
91
|
+
// skip eslint from typechecking since it extends from root file that is outside rootDir
|
|
92
|
+
if (options.linter === 'eslint') {
|
|
93
|
+
tsconfig.exclude.push('eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs');
|
|
94
|
+
}
|
|
95
|
+
tsconfig.compilerOptions.outDir = 'out-tsc/playwright';
|
|
96
|
+
if (!options.rootProject) {
|
|
97
|
+
(0, devkit_1.updateJson)(tree, 'tsconfig.json', (json) => {
|
|
98
|
+
// add the project tsconfig to the workspace root tsconfig.json references
|
|
99
|
+
json.references ??= [];
|
|
100
|
+
json.references.push({ path: './' + projectConfig.root });
|
|
101
|
+
return json;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
tsconfig.compilerOptions.outDir = `${offsetFromProjectRoot}dist/out-tsc`;
|
|
107
|
+
tsconfig.compilerOptions.module = 'commonjs';
|
|
108
|
+
}
|
|
109
|
+
(0, devkit_1.writeJson)(tree, tsconfigPath, tsconfig);
|
|
110
|
+
}
|
|
111
|
+
if (isTsSolutionSetup) {
|
|
112
|
+
const packageJsonPath = (0, devkit_1.joinPathFragments)(projectConfig.root, 'package.json');
|
|
113
|
+
if (!tree.exists(packageJsonPath)) {
|
|
114
|
+
const importPath = (0, project_name_and_root_utils_1.resolveImportPath)(tree, projectConfig.name, projectConfig.root);
|
|
115
|
+
const packageJson = {
|
|
116
|
+
name: importPath,
|
|
117
|
+
version: '0.0.1',
|
|
118
|
+
private: true,
|
|
119
|
+
};
|
|
120
|
+
if (options.project !== importPath) {
|
|
121
|
+
packageJson.nx = { name: options.project };
|
|
122
|
+
}
|
|
123
|
+
(0, devkit_1.writeJson)(tree, packageJsonPath, packageJson);
|
|
124
|
+
}
|
|
125
|
+
ignoreTestOutput(tree, options);
|
|
57
126
|
}
|
|
58
127
|
const hasPlugin = (0, devkit_1.readNxJson)(tree).plugins?.some((p) => typeof p === 'string'
|
|
59
128
|
? p === '@nx/playwright/plugin'
|
|
@@ -73,14 +142,14 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
73
142
|
addPlugin: options.addPlugin,
|
|
74
143
|
}));
|
|
75
144
|
if (options.js) {
|
|
76
|
-
const { ModuleKind } = (0,
|
|
145
|
+
const { ModuleKind } = (0, ensure_typescript_1.ensureTypescript)();
|
|
77
146
|
(0, devkit_1.toJS)(tree, { extension: '.cjs', module: ModuleKind.CommonJS });
|
|
78
147
|
}
|
|
79
148
|
recommendVsCodeExtensions(tree);
|
|
80
149
|
if (!options.skipPackageJson) {
|
|
81
150
|
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
82
151
|
// required since used in playwright config
|
|
83
|
-
'@nx/devkit':
|
|
152
|
+
'@nx/devkit': versions_1.nxVersion,
|
|
84
153
|
}));
|
|
85
154
|
}
|
|
86
155
|
if (!options.skipInstall) {
|
|
@@ -89,8 +158,55 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
89
158
|
if (!options.skipFormat) {
|
|
90
159
|
await (0, devkit_1.formatFiles)(tree);
|
|
91
160
|
}
|
|
161
|
+
if (isTsSolutionSetup) {
|
|
162
|
+
const projectPackageManagerWorkspaceState = (0, package_manager_workspaces_1.getProjectPackageManagerWorkspaceState)(tree, projectConfig.root);
|
|
163
|
+
if (projectPackageManagerWorkspaceState !== 'included') {
|
|
164
|
+
tasks.push((0, package_manager_workspaces_1.getProjectPackageManagerWorkspaceStateWarningTask)(projectPackageManagerWorkspaceState, tree.root));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
92
167
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
93
168
|
}
|
|
169
|
+
async function normalizeOptions(tree, options) {
|
|
170
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
171
|
+
const addPlugin = options.addPlugin ??
|
|
172
|
+
(process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
173
|
+
nxJson.useInferencePlugins !== false);
|
|
174
|
+
const linter = await (0, generator_prompts_1.normalizeLinterOption)(tree, options.linter);
|
|
175
|
+
if (!options.webServerCommand || !options.webServerAddress) {
|
|
176
|
+
const { webServerCommand, webServerAddress } = await promptForMissingServeData(options.project);
|
|
177
|
+
options.webServerCommand = webServerCommand;
|
|
178
|
+
options.webServerAddress = webServerAddress;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
...options,
|
|
182
|
+
addPlugin,
|
|
183
|
+
linter,
|
|
184
|
+
directory: options.directory ?? 'e2e',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async function promptForMissingServeData(projectName) {
|
|
188
|
+
const { command, port } = await (0, prompt_1.promptWhenInteractive)([
|
|
189
|
+
{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'command',
|
|
192
|
+
message: 'What command should be run to serve the application locally?',
|
|
193
|
+
initial: `npx nx serve ${projectName}`,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'numeral',
|
|
197
|
+
name: 'port',
|
|
198
|
+
message: 'What port will the application be served on?',
|
|
199
|
+
initial: 3000,
|
|
200
|
+
},
|
|
201
|
+
], {
|
|
202
|
+
command: `npx nx serve ${projectName}`,
|
|
203
|
+
port: 3000,
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
webServerCommand: command,
|
|
207
|
+
webServerAddress: `http://localhost:${port}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
94
210
|
function getBrowsersInstallTask() {
|
|
95
211
|
return () => {
|
|
96
212
|
devkit_1.output.log({
|
|
@@ -100,7 +216,7 @@ function getBrowsersInstallTask() {
|
|
|
100
216
|
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
|
101
217
|
(0, child_process_1.execSync)(`${pmc.exec} playwright install`, {
|
|
102
218
|
cwd: devkit_1.workspaceRoot,
|
|
103
|
-
windowsHide:
|
|
219
|
+
windowsHide: false,
|
|
104
220
|
});
|
|
105
221
|
};
|
|
106
222
|
}
|
|
@@ -152,4 +268,20 @@ Rename or remove the existing e2e target.`);
|
|
|
152
268
|
};
|
|
153
269
|
(0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
|
|
154
270
|
}
|
|
271
|
+
function ignoreTestOutput(tree, options) {
|
|
272
|
+
// Make sure playwright outputs are not linted.
|
|
273
|
+
if (options.linter === 'eslint') {
|
|
274
|
+
(0, eslint_file_1.addIgnoresToLintConfig)(tree, '', ['**/test-output']);
|
|
275
|
+
}
|
|
276
|
+
// Handle gitignore
|
|
277
|
+
if (!tree.exists('.gitignore')) {
|
|
278
|
+
devkit_1.logger.warn(`Couldn't find a root .gitignore file to update.`);
|
|
279
|
+
}
|
|
280
|
+
let content = tree.read('.gitignore', 'utf-8');
|
|
281
|
+
if (/^test-output$/gm.test(content)) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
content = `${content}\ntest-output\n`;
|
|
285
|
+
tree.write('.gitignore', content);
|
|
286
|
+
}
|
|
155
287
|
exports.default = configurationGenerator;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { defineConfig, devices } from '@playwright/test';
|
|
2
2
|
import { nxE2EPreset } from '@nx/playwright/preset';
|
|
3
|
-
<% if(
|
|
4
|
-
import { workspaceRoot } from '@nx/devkit';
|
|
3
|
+
<% if (webServerCommand && webServerAddress) { %>import { workspaceRoot } from '@nx/devkit';<% } %>
|
|
5
4
|
|
|
6
5
|
// For CI, you may want to set BASE_URL to the deployed application.
|
|
7
6
|
const baseURL = process.env['BASE_URL'] || '<% if(webServerAddress) {%><%= webServerAddress %><% } else {%>http://localhost:3000<% } %>';
|
|
@@ -27,13 +26,13 @@ export default defineConfig({
|
|
|
27
26
|
webServer: {
|
|
28
27
|
command: '<%= webServerCommand %>',
|
|
29
28
|
url: '<%= webServerAddress %>',
|
|
30
|
-
reuseExistingServer:
|
|
29
|
+
reuseExistingServer: true,
|
|
31
30
|
cwd: workspaceRoot
|
|
32
31
|
},<% } else {%>
|
|
33
32
|
// webServer: {
|
|
34
33
|
// command: 'npm run start',
|
|
35
34
|
// url: 'http://127.0.0.1:3000',
|
|
36
|
-
// reuseExistingServer:
|
|
35
|
+
// reuseExistingServer: true,
|
|
37
36
|
// cwd: workspaceRoot,
|
|
38
37
|
// },<% } %>
|
|
39
38
|
projects: [
|
|
@@ -5,13 +5,13 @@ export interface ConfigurationGeneratorSchema {
|
|
|
5
5
|
/**
|
|
6
6
|
* this is relative to the projectRoot
|
|
7
7
|
**/
|
|
8
|
-
directory
|
|
9
|
-
js
|
|
10
|
-
skipFormat
|
|
11
|
-
skipPackageJson
|
|
8
|
+
directory?: string;
|
|
9
|
+
js?: boolean; // default is false
|
|
10
|
+
skipFormat?: boolean;
|
|
11
|
+
skipPackageJson?: boolean;
|
|
12
12
|
skipInstall?: boolean;
|
|
13
|
-
linter
|
|
14
|
-
setParserOptionsProject
|
|
13
|
+
linter?: Linter | LinterType;
|
|
14
|
+
setParserOptionsProject?: boolean; // default is false
|
|
15
15
|
/**
|
|
16
16
|
* command to give playwright to run the web server
|
|
17
17
|
* @example: "npx nx serve my-fe-app"
|
|
@@ -25,3 +25,10 @@ export interface ConfigurationGeneratorSchema {
|
|
|
25
25
|
rootProject?: boolean;
|
|
26
26
|
addPlugin?: boolean;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
export interface NormalizedGeneratorOptions
|
|
30
|
+
extends ConfigurationGeneratorSchema {
|
|
31
|
+
addPlugin: boolean;
|
|
32
|
+
directory: string;
|
|
33
|
+
linter: Linter | LinterType;
|
|
34
|
+
}
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"x-priority": "important",
|
|
21
21
|
"default": "e2e"
|
|
22
22
|
},
|
|
23
|
+
"linter": {
|
|
24
|
+
"description": "The tool to use for running lint checks.",
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["none", "eslint"],
|
|
27
|
+
"x-priority": "important"
|
|
28
|
+
},
|
|
23
29
|
"js": {
|
|
24
30
|
"type": "boolean",
|
|
25
31
|
"description": "Generate JavaScript files rather than TypeScript files.",
|
|
@@ -33,12 +39,6 @@
|
|
|
33
39
|
"type": "string",
|
|
34
40
|
"description": "The address of the web server."
|
|
35
41
|
},
|
|
36
|
-
"linter": {
|
|
37
|
-
"description": "The tool to use for running lint checks.",
|
|
38
|
-
"type": "string",
|
|
39
|
-
"enum": ["eslint", "none"],
|
|
40
|
-
"default": "eslint"
|
|
41
|
-
},
|
|
42
42
|
"setParserOptionsProject": {
|
|
43
43
|
"type": "boolean",
|
|
44
44
|
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.default = addE2eCiTargetDefaults;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
5
|
const target_defaults_utils_1 = require("@nx/devkit/src/generators/target-defaults-utils");
|
|
6
|
-
const
|
|
6
|
+
const loaded_nx_plugin_1 = require("nx/src/project-graph/plugins/loaded-nx-plugin");
|
|
7
7
|
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
8
8
|
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
9
9
|
const path_1 = require("path");
|
|
@@ -19,7 +19,7 @@ async function addE2eCiTargetDefaults(tree) {
|
|
|
19
19
|
for (const plugin of matchingPluginRegistrations) {
|
|
20
20
|
let projectConfigs;
|
|
21
21
|
try {
|
|
22
|
-
const loadedPlugin = new
|
|
22
|
+
const loadedPlugin = new loaded_nx_plugin_1.LoadedNxPlugin({ createNodesV2, name: pluginName }, plugin);
|
|
23
23
|
projectConfigs = await (0, devkit_internals_1.retrieveProjectConfigurations)([loadedPlugin], tree.root, nxJson);
|
|
24
24
|
}
|
|
25
25
|
catch (e) {
|
|
@@ -4,11 +4,9 @@ exports.default = default_1;
|
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
5
|
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
6
6
|
const add_e2e_ci_target_defaults_1 = require("./add-e2e-ci-target-defaults");
|
|
7
|
-
const
|
|
7
|
+
const loaded_nx_plugin_1 = require("nx/src/project-graph/plugins/loaded-nx-plugin");
|
|
8
8
|
const retrieve_workspace_files_1 = require("nx/src/project-graph/utils/retrieve-workspace-files");
|
|
9
9
|
const error_types_1 = require("nx/src/project-graph/error-types");
|
|
10
|
-
const plugin_1 = require("@nx/webpack/src/plugins/plugin");
|
|
11
|
-
const plugin_2 = require("@nx/vite/plugin");
|
|
12
10
|
async function default_1(tree) {
|
|
13
11
|
const graph = await (0, devkit_1.createProjectGraphAsync)();
|
|
14
12
|
const collectedProjects = [];
|
|
@@ -86,8 +84,9 @@ async function default_1(tree) {
|
|
|
86
84
|
: 'preview', projectToMigrate.configFileType === 'webpack'
|
|
87
85
|
? 'serveStaticTargetName'
|
|
88
86
|
: 'previewTargetName', projectToMigrate.configFileType === 'webpack'
|
|
89
|
-
?
|
|
90
|
-
:
|
|
87
|
+
? require('@nx/webpack/plugin').createNodesV2
|
|
88
|
+
: require('@nx/vite/plugin')
|
|
89
|
+
.createNodesV2)) ??
|
|
91
90
|
getServeStaticLikeTarget(tree, graph, projectToMigrate.projectName, projectToMigrate.configFileType === 'webpack'
|
|
92
91
|
? '@nx/web:file-server'
|
|
93
92
|
: '@nx/vite:preview-server');
|
|
@@ -143,7 +142,7 @@ async function getServeStaticTargetNameForConfigFile(tree, pluginName, configFil
|
|
|
143
142
|
for (const plugin of matchingPluginRegistrations) {
|
|
144
143
|
let projectConfigs;
|
|
145
144
|
try {
|
|
146
|
-
const loadedPlugin = new
|
|
145
|
+
const loadedPlugin = new loaded_nx_plugin_1.LoadedNxPlugin({ createNodesV2, name: pluginName }, plugin);
|
|
147
146
|
projectConfigs = await (0, retrieve_workspace_files_1.retrieveProjectConfigurations)([loadedPlugin], tree.root, nxJson);
|
|
148
147
|
}
|
|
149
148
|
catch (e) {
|
package/src/plugins/plugin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNodes = exports.createNodesV2 = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
6
|
const devkit_1 = require("@nx/devkit");
|
|
7
7
|
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
|
8
8
|
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
|
@@ -14,7 +14,14 @@ const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
|
|
14
14
|
const file_hasher_1 = require("nx/src/hasher/file-hasher");
|
|
15
15
|
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
|
16
16
|
function readTargetsCache(cachePath) {
|
|
17
|
-
|
|
17
|
+
try {
|
|
18
|
+
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false'
|
|
19
|
+
? (0, devkit_1.readJsonFile)(cachePath)
|
|
20
|
+
: {};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
function writeTargetsToCache(cachePath, results) {
|
|
20
27
|
(0, devkit_1.writeJsonFile)(cachePath, results);
|
|
@@ -24,7 +31,7 @@ exports.createNodesV2 = [
|
|
|
24
31
|
playwrightConfigGlob,
|
|
25
32
|
async (configFilePaths, options, context) => {
|
|
26
33
|
const optionsHash = (0, file_hasher_1.hashObject)(options);
|
|
27
|
-
const cachePath = (0,
|
|
34
|
+
const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `playwright-${optionsHash}.hash`);
|
|
28
35
|
const targetsCache = readTargetsCache(cachePath);
|
|
29
36
|
try {
|
|
30
37
|
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => createNodesInternal(configFile, options, context, targetsCache), configFilePaths, options, context);
|
|
@@ -46,15 +53,15 @@ exports.createNodes = [
|
|
|
46
53
|
},
|
|
47
54
|
];
|
|
48
55
|
async function createNodesInternal(configFilePath, options, context, targetsCache) {
|
|
49
|
-
const projectRoot = (0,
|
|
56
|
+
const projectRoot = (0, node_path_1.dirname)(configFilePath);
|
|
50
57
|
// Do not create a project if package.json and project.json isn't there.
|
|
51
|
-
const siblingFiles = (0,
|
|
58
|
+
const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, projectRoot));
|
|
52
59
|
if (!siblingFiles.includes('package.json') &&
|
|
53
60
|
!siblingFiles.includes('project.json')) {
|
|
54
61
|
return {};
|
|
55
62
|
}
|
|
56
63
|
const normalizedOptions = normalizeOptions(options);
|
|
57
|
-
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot,
|
|
64
|
+
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, normalizedOptions, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
|
|
58
65
|
targetsCache[hash] ??= await buildPlaywrightTargets(configFilePath, projectRoot, normalizedOptions, context);
|
|
59
66
|
const { targets, metadata } = targetsCache[hash];
|
|
60
67
|
return {
|
|
@@ -72,16 +79,18 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
72
79
|
// but we're just reading the config so let's delete the variable they are using to detect this.
|
|
73
80
|
// See: https://github.com/microsoft/playwright/pull/11218/files
|
|
74
81
|
delete process['__pw_initiator__'];
|
|
75
|
-
const playwrightConfig = await (0, config_utils_1.loadConfigFile)((0,
|
|
82
|
+
const playwrightConfig = await (0, config_utils_1.loadConfigFile)((0, node_path_1.join)(context.workspaceRoot, configFilePath));
|
|
76
83
|
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
|
|
77
84
|
const targets = {};
|
|
78
85
|
let metadata;
|
|
86
|
+
const testOutput = getTestOutput(playwrightConfig);
|
|
87
|
+
const reporterOutputs = getReporterOutputs(playwrightConfig);
|
|
88
|
+
const webserverCommandTasks = getWebserverCommandTasks(playwrightConfig);
|
|
79
89
|
const baseTargetConfig = {
|
|
80
90
|
command: 'playwright test',
|
|
81
91
|
options: {
|
|
82
92
|
cwd: '{projectRoot}',
|
|
83
93
|
},
|
|
84
|
-
parallelism: false,
|
|
85
94
|
metadata: {
|
|
86
95
|
technologies: ['playwright'],
|
|
87
96
|
description: 'Runs Playwright Tests',
|
|
@@ -95,6 +104,12 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
95
104
|
},
|
|
96
105
|
},
|
|
97
106
|
};
|
|
107
|
+
if (webserverCommandTasks.length) {
|
|
108
|
+
baseTargetConfig.dependsOn = getDependsOn(webserverCommandTasks);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
baseTargetConfig.parallelism = false;
|
|
112
|
+
}
|
|
98
113
|
targets[options.targetName] = {
|
|
99
114
|
...baseTargetConfig,
|
|
100
115
|
cache: true,
|
|
@@ -104,7 +119,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
104
119
|
: ['default', '^default']),
|
|
105
120
|
{ externalDependencies: ['@playwright/test'] },
|
|
106
121
|
],
|
|
107
|
-
outputs:
|
|
122
|
+
outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot),
|
|
108
123
|
};
|
|
109
124
|
if (options.ciTargetName) {
|
|
110
125
|
const ciBaseTargetConfig = {
|
|
@@ -116,7 +131,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
116
131
|
: ['default', '^default']),
|
|
117
132
|
{ externalDependencies: ['@playwright/test'] },
|
|
118
133
|
],
|
|
119
|
-
outputs:
|
|
134
|
+
outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot),
|
|
120
135
|
};
|
|
121
136
|
const groupName = 'E2E (CI)';
|
|
122
137
|
metadata = { targetGroups: { [groupName]: [] } };
|
|
@@ -128,12 +143,20 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
128
143
|
playwrightConfig.testMatch ??= '**/*.@(spec|test).?(c|m)[jt]s?(x)';
|
|
129
144
|
const dependsOn = [];
|
|
130
145
|
await forEachTestFile((testFile) => {
|
|
131
|
-
const
|
|
146
|
+
const outputSubfolder = (0, node_path_1.relative)(projectRoot, testFile)
|
|
147
|
+
.replace(/[\/\\]/g, '-')
|
|
148
|
+
.replace(/\./g, '-');
|
|
149
|
+
const relativeSpecFilePath = (0, devkit_1.normalizePath)((0, node_path_1.relative)(projectRoot, testFile));
|
|
132
150
|
const targetName = `${options.ciTargetName}--${relativeSpecFilePath}`;
|
|
133
151
|
ciTargetGroup.push(targetName);
|
|
134
152
|
targets[targetName] = {
|
|
135
153
|
...ciBaseTargetConfig,
|
|
136
|
-
|
|
154
|
+
options: {
|
|
155
|
+
...ciBaseTargetConfig.options,
|
|
156
|
+
env: getOutputEnvVars(reporterOutputs, outputSubfolder),
|
|
157
|
+
},
|
|
158
|
+
outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot, outputSubfolder),
|
|
159
|
+
command: `${baseTargetConfig.command} ${relativeSpecFilePath} --output=${(0, node_path_1.join)(testOutput, outputSubfolder)}`,
|
|
137
160
|
metadata: {
|
|
138
161
|
technologies: ['playwright'],
|
|
139
162
|
description: `Runs Playwright Tests in ${relativeSpecFilePath} in CI`,
|
|
@@ -164,7 +187,6 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
164
187
|
inputs: ciBaseTargetConfig.inputs,
|
|
165
188
|
outputs: ciBaseTargetConfig.outputs,
|
|
166
189
|
dependsOn,
|
|
167
|
-
parallelism: false,
|
|
168
190
|
metadata: {
|
|
169
191
|
technologies: ['playwright'],
|
|
170
192
|
description: 'Runs Playwright Tests in CI',
|
|
@@ -179,6 +201,9 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
179
201
|
},
|
|
180
202
|
},
|
|
181
203
|
};
|
|
204
|
+
if (!webserverCommandTasks.length) {
|
|
205
|
+
targets[options.ciTargetName].parallelism = false;
|
|
206
|
+
}
|
|
182
207
|
ciTargetGroup.push(options.ciTargetName);
|
|
183
208
|
}
|
|
184
209
|
return { targets, metadata };
|
|
@@ -214,58 +239,141 @@ function createMatcher(pattern) {
|
|
|
214
239
|
};
|
|
215
240
|
}
|
|
216
241
|
}
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
242
|
+
function normalizeOptions(options) {
|
|
243
|
+
return {
|
|
244
|
+
...options,
|
|
245
|
+
targetName: options?.targetName ?? 'e2e',
|
|
246
|
+
ciTargetName: options?.ciTargetName ?? 'e2e-ci',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function getTestOutput(playwrightConfig) {
|
|
250
|
+
const { outputDir } = playwrightConfig;
|
|
251
|
+
if (outputDir) {
|
|
252
|
+
return outputDir;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
return './test-results';
|
|
225
256
|
}
|
|
257
|
+
}
|
|
258
|
+
function getReporterOutputs(playwrightConfig) {
|
|
226
259
|
const outputs = [];
|
|
227
|
-
const { reporter
|
|
260
|
+
const { reporter } = playwrightConfig;
|
|
228
261
|
if (reporter) {
|
|
229
|
-
const DEFAULT_REPORTER_OUTPUT =
|
|
230
|
-
if (reporter === 'html'
|
|
231
|
-
|
|
232
|
-
|
|
262
|
+
const DEFAULT_REPORTER_OUTPUT = 'playwright-report';
|
|
263
|
+
if (reporter === 'html') {
|
|
264
|
+
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
|
265
|
+
}
|
|
266
|
+
else if (reporter === 'json') {
|
|
267
|
+
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
|
233
268
|
}
|
|
234
269
|
else if (Array.isArray(reporter)) {
|
|
235
270
|
for (const r of reporter) {
|
|
236
|
-
const [, opts] = r;
|
|
271
|
+
const [reporter, opts] = r;
|
|
237
272
|
// There are a few different ways to specify an output file or directory
|
|
238
273
|
// depending on the reporter. This is a best effort to find the output.
|
|
239
|
-
if (
|
|
240
|
-
outputs.push(
|
|
241
|
-
}
|
|
242
|
-
else if (opts.outputFile) {
|
|
243
|
-
outputs.push(getOutput(opts.outputFile));
|
|
274
|
+
if (opts?.outputFile) {
|
|
275
|
+
outputs.push([reporter, opts.outputFile]);
|
|
244
276
|
}
|
|
245
|
-
else if (opts
|
|
246
|
-
outputs.push(
|
|
277
|
+
else if (opts?.outputDir) {
|
|
278
|
+
outputs.push([reporter, opts.outputDir]);
|
|
247
279
|
}
|
|
248
|
-
else if (opts
|
|
249
|
-
outputs.push(
|
|
280
|
+
else if (opts?.outputFolder) {
|
|
281
|
+
outputs.push([reporter, opts.outputFolder]);
|
|
250
282
|
}
|
|
251
283
|
else {
|
|
252
|
-
outputs.push(DEFAULT_REPORTER_OUTPUT);
|
|
284
|
+
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
|
253
285
|
}
|
|
254
286
|
}
|
|
255
287
|
}
|
|
256
288
|
}
|
|
257
|
-
|
|
258
|
-
|
|
289
|
+
return outputs;
|
|
290
|
+
}
|
|
291
|
+
function getTargetOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot, subFolder) {
|
|
292
|
+
const outputs = new Set();
|
|
293
|
+
outputs.add(normalizeOutput(addSubfolderToOutput(testOutput, subFolder), workspaceRoot, projectRoot));
|
|
294
|
+
for (const [, output] of reporterOutputs) {
|
|
295
|
+
outputs.add(normalizeOutput(addSubfolderToOutput(output, subFolder), workspaceRoot, projectRoot));
|
|
259
296
|
}
|
|
260
|
-
|
|
261
|
-
|
|
297
|
+
return Array.from(outputs);
|
|
298
|
+
}
|
|
299
|
+
function addSubfolderToOutput(output, subfolder) {
|
|
300
|
+
if (!subfolder)
|
|
301
|
+
return output;
|
|
302
|
+
const parts = (0, node_path_1.parse)(output);
|
|
303
|
+
if (parts.ext !== '') {
|
|
304
|
+
return (0, node_path_1.join)(parts.dir, subfolder, parts.base);
|
|
262
305
|
}
|
|
263
|
-
return
|
|
306
|
+
return (0, node_path_1.join)(output, subfolder);
|
|
264
307
|
}
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
308
|
+
function getWebserverCommandTasks(playwrightConfig) {
|
|
309
|
+
if (!playwrightConfig.webServer) {
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
const tasks = [];
|
|
313
|
+
const webServer = Array.isArray(playwrightConfig.webServer)
|
|
314
|
+
? playwrightConfig.webServer
|
|
315
|
+
: [playwrightConfig.webServer];
|
|
316
|
+
for (const server of webServer) {
|
|
317
|
+
if (!server.reuseExistingServer) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const task = parseTaskFromCommand(server.command);
|
|
321
|
+
if (task) {
|
|
322
|
+
tasks.push(task);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return tasks;
|
|
326
|
+
}
|
|
327
|
+
function parseTaskFromCommand(command) {
|
|
328
|
+
const nxRunRegex = /^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx run (\S+:\S+)$/;
|
|
329
|
+
const infixRegex = /^(?:(?:npx|yarn|bun|pnpm|pnpm exec|pnpx) )?nx (\S+ \S+)$/;
|
|
330
|
+
const nxRunMatch = command.match(nxRunRegex);
|
|
331
|
+
if (nxRunMatch) {
|
|
332
|
+
const [project, target] = nxRunMatch[1].split(':');
|
|
333
|
+
return { project, target };
|
|
334
|
+
}
|
|
335
|
+
const infixMatch = command.match(infixRegex);
|
|
336
|
+
if (infixMatch) {
|
|
337
|
+
const [target, project] = infixMatch[1].split(' ');
|
|
338
|
+
return { project, target };
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
function getDependsOn(tasks) {
|
|
343
|
+
const projectsPerTask = new Map();
|
|
344
|
+
for (const { project, target } of tasks) {
|
|
345
|
+
if (!projectsPerTask.has(target)) {
|
|
346
|
+
projectsPerTask.set(target, []);
|
|
347
|
+
}
|
|
348
|
+
projectsPerTask.get(target).push(project);
|
|
349
|
+
}
|
|
350
|
+
return Array.from(projectsPerTask.entries()).map(([target, projects]) => ({
|
|
351
|
+
projects,
|
|
352
|
+
target,
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
function normalizeOutput(path, workspaceRoot, projectRoot) {
|
|
356
|
+
const fullProjectRoot = (0, node_path_1.resolve)(workspaceRoot, projectRoot);
|
|
357
|
+
const fullPath = (0, node_path_1.resolve)(fullProjectRoot, path);
|
|
358
|
+
const pathRelativeToProjectRoot = (0, devkit_1.normalizePath)((0, node_path_1.relative)(fullProjectRoot, fullPath));
|
|
359
|
+
if (pathRelativeToProjectRoot.startsWith('..')) {
|
|
360
|
+
return (0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, fullPath));
|
|
361
|
+
}
|
|
362
|
+
return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
|
|
363
|
+
}
|
|
364
|
+
function getOutputEnvVars(reporterOutputs, outputSubfolder) {
|
|
365
|
+
const env = {};
|
|
366
|
+
for (let [reporter, output] of reporterOutputs) {
|
|
367
|
+
if (outputSubfolder) {
|
|
368
|
+
const isFile = (0, node_path_1.parse)(output).ext !== '';
|
|
369
|
+
const envVarName = `PLAYWRIGHT_${reporter.toUpperCase()}_OUTPUT_${isFile ? 'FILE' : 'DIR'}`;
|
|
370
|
+
env[envVarName] = addSubfolderToOutput(output, outputSubfolder);
|
|
371
|
+
// Also set PLAYWRIGHT_HTML_REPORT for Playwright prior to 1.45.0.
|
|
372
|
+
// HTML prior to this version did not follow the pattern of "PLAYWRIGHT_<REPORTER>_OUTPUT_<FILE|DIR>".
|
|
373
|
+
if (reporter === 'html') {
|
|
374
|
+
env['PLAYWRIGHT_HTML_REPORT'] = env[envVarName];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return env;
|
|
271
379
|
}
|
package/src/utils/add-linter.js
CHANGED
|
@@ -8,7 +8,7 @@ const versions_1 = require("./versions");
|
|
|
8
8
|
const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
|
|
9
9
|
const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
|
|
10
10
|
async function addLinterToPlaywrightProject(tree, options) {
|
|
11
|
-
if (options.linter ===
|
|
11
|
+
if (options.linter === 'none') {
|
|
12
12
|
return () => { };
|
|
13
13
|
}
|
|
14
14
|
const tasks = [];
|
|
@@ -26,7 +26,7 @@ async function addLinterToPlaywrightProject(tree, options) {
|
|
|
26
26
|
addPlugin: options.addPlugin,
|
|
27
27
|
}));
|
|
28
28
|
}
|
|
29
|
-
if (!options.linter || options.linter !==
|
|
29
|
+
if (!options.linter || options.linter !== 'eslint') {
|
|
30
30
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
31
31
|
}
|
|
32
32
|
tasks.push(!options.skipPackageJson
|
package/src/utils/preset.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nxE2EPreset = nxE2EPreset;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
|
|
6
|
+
const test_1 = require("@playwright/test");
|
|
5
7
|
const node_fs_1 = require("node:fs");
|
|
6
8
|
const node_path_1 = require("node:path");
|
|
7
|
-
const test_1 = require("@playwright/test");
|
|
8
9
|
/**
|
|
9
10
|
* nx E2E Preset for Playwright
|
|
10
11
|
* @description
|
|
@@ -33,8 +34,13 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
33
34
|
: (0, node_path_1.dirname)(pathToConfig);
|
|
34
35
|
const projectPath = (0, node_path_1.relative)(devkit_1.workspaceRoot, normalizedPath);
|
|
35
36
|
const offset = (0, node_path_1.relative)(normalizedPath, devkit_1.workspaceRoot);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
37
|
+
const isTsSolutionSetup = (0, ts_solution_setup_1.isUsingTsSolutionSetup)();
|
|
38
|
+
const testResultOuputDir = isTsSolutionSetup
|
|
39
|
+
? 'test-output/playwright/output'
|
|
40
|
+
: (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'test-output');
|
|
41
|
+
const reporterOutputDir = isTsSolutionSetup
|
|
42
|
+
? 'test-output/playwright/report'
|
|
43
|
+
: (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'playwright-report');
|
|
38
44
|
return (0, test_1.defineConfig)({
|
|
39
45
|
testDir: options?.testDir ?? './src',
|
|
40
46
|
outputDir: testResultOuputDir,
|
package/README.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
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>
|
|
7
|
-
|
|
8
|
-
<div style="text-align: center;">
|
|
9
|
-
|
|
10
|
-
[](https://circleci.com/gh/nrwl/nx)
|
|
11
|
-
[]()
|
|
12
|
-
[](https://www.npmjs.com/@nx/workspace)
|
|
13
|
-
[]()
|
|
14
|
-
[](http://commitizen.github.io/cz-cli/)
|
|
15
|
-
[](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
16
|
-
[](https://go.nx.dev/community)
|
|
17
|
-
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<hr>
|
|
22
|
-
|
|
23
|
-
# Nx: Smart Monorepos · Fast CI
|
|
24
|
-
|
|
25
|
-
Nx is a build system, optimized for monorepos, with plugins for popular frameworks and tools and advanced CI capabilities including caching and distribution.
|
|
26
|
-
|
|
27
|
-
## Getting Started
|
|
28
|
-
|
|
29
|
-
### Creating an Nx Workspace
|
|
30
|
-
|
|
31
|
-
**Using `npx`**
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npx create-nx-workspace
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
**Using `npm init`**
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
npm init nx-workspace
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
**Using `yarn create`**
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
yarn create nx-workspace
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Adding Nx to an Existing Repository
|
|
50
|
-
|
|
51
|
-
Run:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npx nx@latest init
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Documentation & Resources
|
|
58
|
-
|
|
59
|
-
- [Nx.Dev: Documentation, Guides, Tutorials](https://nx.dev)
|
|
60
|
-
- [Intro to Nx](https://nx.dev/getting-started/intro)
|
|
61
|
-
- [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
|
|
62
|
-
- [Blog Posts About Nx](https://nx.dev/blog)
|
|
63
|
-
|
|
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"
|
|
65
|
-
width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
|
|
66
|
-
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = update;
|
|
4
|
-
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const ts = require("typescript");
|
|
6
|
-
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
7
|
-
async function update(tree) {
|
|
8
|
-
const projects = (0, devkit_1.getProjects)(tree);
|
|
9
|
-
projects.forEach((project) => {
|
|
10
|
-
// Check if the project contains playwright config
|
|
11
|
-
const configPath = (0, devkit_1.joinPathFragments)(project.root, 'playwright.config.ts');
|
|
12
|
-
if (tree.exists(configPath)) {
|
|
13
|
-
addProjectIfExists(tree, (0, devkit_1.joinPathFragments)(configPath));
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
}
|
|
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
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = default_1;
|
|
4
|
-
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
6
|
-
async function default_1(tree) {
|
|
7
|
-
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/playwright:playwright', (options, projectName, targetName, configurationName) => {
|
|
8
|
-
if (options?.['baseUrl']) {
|
|
9
|
-
const project = (0, devkit_1.readProjectConfiguration)(tree, projectName);
|
|
10
|
-
if (configurationName) {
|
|
11
|
-
delete project.targets[targetName].configurations[configurationName]['baseUrl'];
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
delete project.targets[targetName].options['baseUrl'];
|
|
15
|
-
}
|
|
16
|
-
(0, devkit_1.updateProjectConfiguration)(tree, projectName, project);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
20
|
-
for (const [targetNameOrExecutor, target] of Object.entries(nxJson.targetDefaults)) {
|
|
21
|
-
if (targetNameOrExecutor === '@nx/playwright:playwright' ||
|
|
22
|
-
(target.executor && target.executor === '@nx/playwright:playwright')) {
|
|
23
|
-
let updated = false;
|
|
24
|
-
if (target.options?.['baseUrl']) {
|
|
25
|
-
delete nxJson.targetDefaults[targetNameOrExecutor].options['baseUrl'];
|
|
26
|
-
updated = true;
|
|
27
|
-
}
|
|
28
|
-
if (target.configurations) {
|
|
29
|
-
for (const [configurationName, configuration] of Object.entries(target.configurations)) {
|
|
30
|
-
if (configuration['baseUrl']) {
|
|
31
|
-
delete nxJson.targetDefaults[targetNameOrExecutor].configurations[configurationName]['baseUrl'];
|
|
32
|
-
updated = true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (updated) {
|
|
37
|
-
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
await (0, devkit_1.formatFiles)(tree);
|
|
42
|
-
}
|