@nx/playwright 18.0.0-canary.20240130-bb3cf3a → 18.0.0-canary.20240202-ea5befb

Sign up to get free protection for your applications and to get access to all the features.
package/migrations.json CHANGED
@@ -1,3 +1,10 @@
1
1
  {
2
- "generators": {}
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
+ }
3
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/playwright",
3
- "version": "18.0.0-canary.20240130-bb3cf3a",
3
+ "version": "18.0.0-canary.20240202-ea5befb",
4
4
  "type": "commonjs",
5
5
  "homepage": "https://nx.dev",
6
6
  "private": false,
@@ -32,9 +32,10 @@
32
32
  "directory": "packages/playwright"
33
33
  },
34
34
  "dependencies": {
35
- "@nx/devkit": "18.0.0-canary.20240130-bb3cf3a",
36
- "@nx/eslint": "18.0.0-canary.20240130-bb3cf3a",
37
- "@nx/js": "18.0.0-canary.20240130-bb3cf3a",
35
+ "@phenomnomnominal/tsquery": "~5.0.1",
36
+ "@nx/devkit": "18.0.0-canary.20240202-ea5befb",
37
+ "@nx/eslint": "18.0.0-canary.20240202-ea5befb",
38
+ "@nx/js": "18.0.0-canary.20240202-ea5befb",
38
39
  "tslib": "^2.3.0",
39
40
  "minimatch": "9.0.3"
40
41
  },
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/schema",
2
+ "$schema": "https://json-schema.org/schema",
3
3
  "version": 2,
4
4
  "title": "Playwright executor",
5
5
  "description": "Run Playwright tests.",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/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",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/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.",
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<void>;
@@ -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
+ }
@@ -8,44 +8,27 @@ exports.dynamicImport = new Function('modulePath', 'return import(modulePath);')
8
8
  async function loadPlaywrightConfig(configFilePath) {
9
9
  {
10
10
  let module;
11
+ const configPathWithTimestamp = `${configFilePath}?t=${Date.now()}`;
11
12
  if ((0, path_1.extname)(configFilePath) === '.ts') {
12
13
  const tsConfigPath = (0, js_1.getRootTsConfigPath)();
13
14
  if (tsConfigPath) {
14
15
  const unregisterTsProject = (0, internal_1.registerTsProject)(tsConfigPath);
15
16
  try {
16
- // Require's cache doesn't notice when the file is updated, and
17
- // this function is ran during daemon operation. If the config file
18
- // is updated, we need to read its new contents, so we need to clear the cache.
19
- // We can't just delete the cache entry for the config file, because
20
- // it might have imports that need to be updated as well.
21
- clearRequireCache();
22
- // ts-node doesn't support dynamic import, so we need to use require
23
- module = require(configFilePath);
17
+ module = await (0, exports.dynamicImport)(configPathWithTimestamp);
24
18
  }
25
19
  finally {
26
20
  unregisterTsProject();
27
21
  }
28
22
  }
29
23
  else {
30
- module = await (0, exports.dynamicImport)(configFilePath);
24
+ module = await (0, exports.dynamicImport)(configPathWithTimestamp);
31
25
  }
32
26
  }
33
27
  else {
34
- module = await (0, exports.dynamicImport)(configFilePath);
28
+ module = await (0, exports.dynamicImport)(configPathWithTimestamp);
35
29
  }
36
30
  return module.default ?? module;
37
31
  }
38
32
  }
39
33
  exports.loadPlaywrightConfig = loadPlaywrightConfig;
40
34
  const packageInstallationDirectories = ['node_modules', '.yarn'];
41
- function clearRequireCache() {
42
- Object.keys(require.cache).forEach((key) => {
43
- // We don't want to clear the require cache of installed packages.
44
- // Clearing them can cause some issues when running Nx without the daemon
45
- // and may cause issues for other packages that use the module state
46
- // in some to store cached information.
47
- if (!packageInstallationDirectories.some((dir) => key.includes(dir))) {
48
- delete require.cache[key];
49
- }
50
- });
51
- }