ui5-test-runner 6.0.3 → 6.0.5
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/dist/Npm.js
CHANGED
|
@@ -19,7 +19,7 @@ const npm = async (...arguments_) => {
|
|
|
19
19
|
detached: true
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
|
-
const validateRoot = async (proc, label) => {
|
|
22
|
+
const validateRoot = async (proc, label, requiresExistence) => {
|
|
23
23
|
if (proc.code !== 0) {
|
|
24
24
|
logger.fatal({ source: 'npm', message: `npm root ${label} failed with code ${proc.code}` });
|
|
25
25
|
}
|
|
@@ -30,8 +30,11 @@ const validateRoot = async (proc, label) => {
|
|
|
30
30
|
try {
|
|
31
31
|
await FileSystem.access(value, FileSystem.constants.R_OK);
|
|
32
32
|
}
|
|
33
|
-
catch {
|
|
34
|
-
|
|
33
|
+
catch (error) {
|
|
34
|
+
const code = error.code;
|
|
35
|
+
if (requiresExistence || code !== 'ENOENT') {
|
|
36
|
+
logger.fatal({ source: 'npm', message: `npm root ${label} directory is not accessible: "${value}"` });
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
39
|
return value;
|
|
37
40
|
};
|
|
@@ -39,8 +42,8 @@ const getRoots = memoize(async () => {
|
|
|
39
42
|
const localRootProcess = await npm('root');
|
|
40
43
|
const globalRootProcess = await npm('root', '--global');
|
|
41
44
|
await Promise.all([localRootProcess.closed, globalRootProcess.closed]);
|
|
42
|
-
const local = await validateRoot(localRootProcess, '(local)');
|
|
43
|
-
const global = await validateRoot(globalRootProcess, '(global)');
|
|
45
|
+
const local = await validateRoot(localRootProcess, '(local)', false);
|
|
46
|
+
const global = await validateRoot(globalRootProcess, '(global)', true);
|
|
44
47
|
logger.debug({ source: 'npm', message: 'roots', data: { local, global } });
|
|
45
48
|
return { local, global };
|
|
46
49
|
});
|
|
@@ -63,6 +66,17 @@ export class Npm {
|
|
|
63
66
|
static dynamicImport(specifier) {
|
|
64
67
|
return import(specifier);
|
|
65
68
|
}
|
|
69
|
+
static resolveSpecifier(specifier) {
|
|
70
|
+
return import.meta.resolve(specifier);
|
|
71
|
+
}
|
|
72
|
+
static tryResolveSpecifier(specifier) {
|
|
73
|
+
try {
|
|
74
|
+
return this.resolveSpecifier(specifier);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return 'unknown location';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
66
80
|
static tryResolvePackageDir(moduleName, nodeModulesPath) {
|
|
67
81
|
try {
|
|
68
82
|
const packageJsonPath = Module.findPackageJSON(moduleName, `${nodeModulesPath}/`);
|
|
@@ -73,10 +87,13 @@ export class Npm {
|
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
static async tryImportFromPath(configuration, moduleName, nodeModulesPath) {
|
|
90
|
+
if (this.tryResolvePackageDir(this.packageNameOf(moduleName), nodeModulesPath) === undefined) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
76
93
|
try {
|
|
77
94
|
const require = Module.createRequire(`${Url.pathToFileURL(configuration.cwd).href}/`);
|
|
78
95
|
const resolved = require.resolve(moduleName, { paths: [nodeModulesPath] });
|
|
79
|
-
return await this.dynamicImport(Url.pathToFileURL(resolved).href);
|
|
96
|
+
return { module: await this.dynamicImport(Url.pathToFileURL(resolved).href), resolved };
|
|
80
97
|
}
|
|
81
98
|
catch {
|
|
82
99
|
return undefined;
|
|
@@ -86,19 +103,24 @@ export class Npm {
|
|
|
86
103
|
for (const { path, label, checkVersion } of roots) {
|
|
87
104
|
const result = await this.tryImportFromPath(configuration, moduleName, path);
|
|
88
105
|
if (result !== undefined) {
|
|
89
|
-
logger.debug({ source: 'npm', message: `Module ${moduleName} ${label}` });
|
|
106
|
+
logger.debug({ source: 'npm', message: `Module ${moduleName} ${label} (${result.resolved})` });
|
|
90
107
|
if (checkVersion)
|
|
91
108
|
void this.checkIfLatestVersion(configuration, moduleName);
|
|
92
|
-
return result;
|
|
109
|
+
return result.module;
|
|
93
110
|
}
|
|
94
111
|
}
|
|
95
112
|
return undefined;
|
|
96
113
|
}
|
|
97
|
-
static async
|
|
98
|
-
|
|
114
|
+
static async tryImportLocally(configuration, moduleName) {
|
|
115
|
+
if (configuration.debugNpmNoLocal) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
99
118
|
try {
|
|
100
119
|
const module = await this.dynamicImport(moduleName);
|
|
101
|
-
logger.debug({
|
|
120
|
+
logger.debug({
|
|
121
|
+
source: 'npm',
|
|
122
|
+
message: `Module ${moduleName} found locally (${this.tryResolveSpecifier(moduleName)})`
|
|
123
|
+
});
|
|
102
124
|
void this.checkIfLatestVersion(configuration, moduleName);
|
|
103
125
|
return module;
|
|
104
126
|
}
|
|
@@ -110,7 +132,14 @@ export class Npm {
|
|
|
110
132
|
if (moduleName === this.packageNameOf(moduleName)) {
|
|
111
133
|
logger.warn({ source: 'npm', message: `Module ${moduleName} not found locally` });
|
|
112
134
|
}
|
|
135
|
+
return undefined;
|
|
113
136
|
}
|
|
137
|
+
}
|
|
138
|
+
static async import(configuration, moduleName) {
|
|
139
|
+
logger.debug({ source: 'npm', message: `Importing module: ${moduleName}` });
|
|
140
|
+
const local = await this.tryImportLocally(configuration, moduleName);
|
|
141
|
+
if (local !== undefined)
|
|
142
|
+
return local;
|
|
114
143
|
const { local: localRoot, global: globalRoot } = await getRoots();
|
|
115
144
|
const extraRoots = [
|
|
116
145
|
...(configuration.alternateNpmPath
|
|
@@ -121,7 +150,9 @@ export class Npm {
|
|
|
121
150
|
: [])
|
|
122
151
|
];
|
|
123
152
|
const found = await this.tryImportFromRoots(configuration, moduleName, [
|
|
124
|
-
|
|
153
|
+
...(configuration.debugNpmNoLocal
|
|
154
|
+
? []
|
|
155
|
+
: [{ path: localRoot, label: 'found in local node_modules', checkVersion: true }]),
|
|
125
156
|
{ path: globalRoot, label: 'found globally', checkVersion: true },
|
|
126
157
|
...extraRoots
|
|
127
158
|
]);
|
|
@@ -144,18 +175,20 @@ export class Npm {
|
|
|
144
175
|
if (reimportPath !== undefined) {
|
|
145
176
|
const result = await this.tryImportFromPath(configuration, moduleName, reimportPath);
|
|
146
177
|
if (result === undefined) {
|
|
147
|
-
|
|
148
|
-
logger.fatal({ source: 'npm', message });
|
|
178
|
+
return logger.fatal({ source: 'npm', message: `Module ${moduleName} could not be loaded after install` });
|
|
149
179
|
}
|
|
150
|
-
|
|
180
|
+
logger.debug({ source: 'npm', message: `Module ${moduleName} loaded after install (${result.resolved})` });
|
|
181
|
+
return result.module;
|
|
151
182
|
}
|
|
152
183
|
return await this.dynamicImport(moduleName);
|
|
153
184
|
}
|
|
154
185
|
static async resolvePackageDir(configuration, moduleName) {
|
|
155
186
|
const { local, global: globalRoot } = await getRoots();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
187
|
+
if (!configuration.debugNpmNoLocal) {
|
|
188
|
+
const fromLocal = this.tryResolvePackageDir(moduleName, local);
|
|
189
|
+
if (fromLocal)
|
|
190
|
+
return fromLocal;
|
|
191
|
+
}
|
|
159
192
|
const fromGlobal = this.tryResolvePackageDir(moduleName, globalRoot);
|
|
160
193
|
if (fromGlobal)
|
|
161
194
|
return fromGlobal;
|
|
@@ -171,6 +204,17 @@ export class Npm {
|
|
|
171
204
|
}
|
|
172
205
|
throw new Error(`Cannot locate package directory for ${moduleName}`);
|
|
173
206
|
}
|
|
207
|
+
static async resolvePackageBinCommand(configuration, moduleName, command) {
|
|
208
|
+
const packageDirectory = await this.resolvePackageDir(configuration, moduleName);
|
|
209
|
+
const { bin } = JSON.parse(await FileSystem.readFile(Path.join(packageDirectory, 'package.json'), 'utf8'));
|
|
210
|
+
const binRelativePath = typeof bin === 'string' ? bin : bin?.[command];
|
|
211
|
+
return binRelativePath
|
|
212
|
+
? Path.join(packageDirectory, binRelativePath)
|
|
213
|
+
: logger.fatal({
|
|
214
|
+
source: 'npm',
|
|
215
|
+
message: `Unable to locate command "${command}" in ${moduleName} package.json "bin"`
|
|
216
|
+
});
|
|
217
|
+
}
|
|
174
218
|
static packageNameOf(moduleName) {
|
|
175
219
|
if (moduleName.startsWith('@')) {
|
|
176
220
|
const [scope, package_] = moduleName.split('/', 2);
|
|
@@ -35,8 +35,8 @@ export const factory = async (configuration, signal) => {
|
|
|
35
35
|
max: 0
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
|
-
await
|
|
39
|
-
|
|
38
|
+
const cliPath = await Npm.resolvePackageBinCommand(configuration, 'playwright', 'playwright');
|
|
39
|
+
await Process.spawn('node', [cliPath, 'install', 'chromium'], {
|
|
40
40
|
signal
|
|
41
41
|
}).closed;
|
|
42
42
|
browser = await chromium.launch(launchOptions);
|
|
@@ -45,8 +45,8 @@ export const factory = async (configuration, signal) => {
|
|
|
45
45
|
max: 0
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
|
-
await
|
|
49
|
-
|
|
48
|
+
const cliPath = await Npm.resolvePackageBinCommand(configuration, 'puppeteer', 'puppeteer');
|
|
49
|
+
await Process.spawn('node', [cliPath, 'browsers', 'install', target], {
|
|
50
50
|
signal
|
|
51
51
|
}).closed;
|
|
52
52
|
browser = await launch(launchOptions);
|
|
@@ -91,6 +91,17 @@ const extractConfigEntries = (parsed, configDirectory) => {
|
|
|
91
91
|
}
|
|
92
92
|
return { forcedKeys, configFileKeys, configFileObject };
|
|
93
93
|
};
|
|
94
|
+
const substitutePlaceholder = async (merged, searchToken, valueToReplace) => {
|
|
95
|
+
for (const option of options) {
|
|
96
|
+
if (!(option.type === 'fs-entry' &&
|
|
97
|
+
typeof merged[option.name] === 'string' &&
|
|
98
|
+
merged[option.name].includes(searchToken))) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const substituted = Path.join(valueToReplace, merged[option.name].split(searchToken, 2)[1]);
|
|
102
|
+
Object.assign(merged, { [option.name]: await validateValue(option, { ...merged, [option.name]: substituted }) });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
94
105
|
const finalizeConfiguration = async (merged) => {
|
|
95
106
|
const hasExplicitReportDirectory = Object.hasOwn(merged, 'reportDir');
|
|
96
107
|
for (const option of options) {
|
|
@@ -108,15 +119,8 @@ const finalizeConfiguration = async (merged) => {
|
|
|
108
119
|
reportDir: await validateValue(reportDirectoryOption, { ...merged, reportDir: batchReportDirectory })
|
|
109
120
|
});
|
|
110
121
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
typeof merged[option.name] === 'string' &&
|
|
114
|
-
merged[option.name].includes('{{reportDir}}'))) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const substituted = Path.join(merged.reportDir, merged[option.name].split('{{reportDir}}', 2)[1]);
|
|
118
|
-
Object.assign(merged, { [option.name]: await validateValue(option, { ...merged, [option.name]: substituted }) });
|
|
119
|
-
}
|
|
122
|
+
await substitutePlaceholder(merged, '{{reportDir}}', merged.reportDir);
|
|
123
|
+
await substitutePlaceholder(merged, '{{tmpDir}}', Host.tmpdir());
|
|
120
124
|
for (const derive of derivations) {
|
|
121
125
|
derive(merged);
|
|
122
126
|
}
|
|
@@ -249,6 +249,11 @@ export const options = [
|
|
|
249
249
|
type: 'boolean',
|
|
250
250
|
description: 'use local docs/ directory instead of fetching from GitHub (development only)'
|
|
251
251
|
},
|
|
252
|
+
{
|
|
253
|
+
name: 'debugNpmNoLocal',
|
|
254
|
+
type: 'boolean',
|
|
255
|
+
description: 'Simulates a locally-absent package so the runner falls back to global or prefix resolution (development/testing only)'
|
|
256
|
+
},
|
|
252
257
|
{
|
|
253
258
|
name: 'driver',
|
|
254
259
|
short: 'd',
|
|
@@ -378,7 +383,8 @@ export const options = [
|
|
|
378
383
|
{
|
|
379
384
|
name: 'npmInstallPrefix',
|
|
380
385
|
type: 'fs-entry',
|
|
381
|
-
|
|
386
|
+
typeModifiers: new Set(['overwrite']),
|
|
387
|
+
description: 'Directory used as the npm --prefix install target when --npm-install is set to prefix'
|
|
382
388
|
},
|
|
383
389
|
{
|
|
384
390
|
name: 'outputInterval',
|