@lvce-editor/test-with-playwright-worker 22.39.1 → 22.39.3
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/coverageWorkerMain.js +32 -3
- package/dist/workerMain.js +292 -9
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import IstanbulCoverage from 'istanbul-lib-coverage';
|
|
1
2
|
import IstanbulReport from 'istanbul-lib-report';
|
|
2
3
|
import IstanbulReports from 'istanbul-reports';
|
|
3
4
|
import { rm, mkdir, readFile } from 'node:fs/promises';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
|
-
import IstanbulCoverage from 'istanbul-lib-coverage';
|
|
6
6
|
import v8ToIstanbul from 'v8-to-istanbul';
|
|
7
7
|
|
|
8
8
|
const normalizeLine = line => {
|
|
@@ -932,8 +932,29 @@ const executeReport = (coverageMap, directory, name) => {
|
|
|
932
932
|
});
|
|
933
933
|
IstanbulReports.create(name).execute(context);
|
|
934
934
|
};
|
|
935
|
-
const writeJavascriptCoverage = async (entries, directory) => {
|
|
936
|
-
|
|
935
|
+
const writeJavascriptCoverage = async (entries, directory, threshold, include) => {
|
|
936
|
+
let coverageMap = await createJavascriptCoverage(entries);
|
|
937
|
+
if (include) {
|
|
938
|
+
const includedCoverage = IstanbulCoverage.createCoverageMap();
|
|
939
|
+
for (const path of coverageMap.files()) {
|
|
940
|
+
if (path.includes(include)) {
|
|
941
|
+
includedCoverage.addFileCoverage(coverageMap.fileCoverageFor(path));
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
coverageMap = includedCoverage;
|
|
945
|
+
if (coverageMap.files().length === 0) {
|
|
946
|
+
throw new Error(`[test-with-playwright] worker coverage target produced no files matching ${include}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (threshold !== undefined) {
|
|
950
|
+
if (coverageMap.files().length === 0) {
|
|
951
|
+
throw new Error('[test-with-playwright] worker coverage target produced no reportable JavaScript');
|
|
952
|
+
}
|
|
953
|
+
const {
|
|
954
|
+
lines
|
|
955
|
+
} = coverageMap.getCoverageSummary().toJSON();
|
|
956
|
+
console.info(`[test-with-playwright] worker line coverage: ${lines.pct}% (${lines.covered}/${lines.total})`);
|
|
957
|
+
}
|
|
937
958
|
await rm(directory, {
|
|
938
959
|
force: true,
|
|
939
960
|
recursive: true
|
|
@@ -954,6 +975,14 @@ const writeJavascriptCoverage = async (entries, directory) => {
|
|
|
954
975
|
const summaryText = await readFile(join(directory, 'coverage.txt'), 'utf8');
|
|
955
976
|
const summary = summaryText.trimEnd();
|
|
956
977
|
console.info(`[test-with-playwright] JavaScript coverage written to ${directory}\n${summary}`);
|
|
978
|
+
if (threshold !== undefined) {
|
|
979
|
+
const {
|
|
980
|
+
lines
|
|
981
|
+
} = coverageMap.getCoverageSummary().toJSON();
|
|
982
|
+
if (lines.total === 0 || lines.pct < threshold) {
|
|
983
|
+
throw new Error(`[test-with-playwright] worker line coverage ${lines.pct}% is below threshold ${threshold}%`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
957
986
|
};
|
|
958
987
|
|
|
959
988
|
const commandMap = {
|
package/dist/workerMain.js
CHANGED
|
@@ -2099,37 +2099,290 @@ const getCoverageWorkerUrl = (moduleUrl = import.meta.url) => {
|
|
|
2099
2099
|
return new URL('../../../../test-with-playwright-coverage-worker/src/workerMain.ts', moduleUrl).href;
|
|
2100
2100
|
};
|
|
2101
2101
|
|
|
2102
|
-
const writeJavascriptCoverage = async (entries, directory) => {
|
|
2102
|
+
const writeJavascriptCoverage = async (entries, directory, threshold, include) => {
|
|
2103
2103
|
const rpc = create$1({
|
|
2104
2104
|
commandMap: {},
|
|
2105
2105
|
path: fileURLToPath(getCoverageWorkerUrl()),
|
|
2106
2106
|
stdio: 'inherit'
|
|
2107
2107
|
});
|
|
2108
2108
|
try {
|
|
2109
|
-
await rpc.invoke(WriteJavascriptCoverage, entries, directory);
|
|
2109
|
+
await rpc.invoke(WriteJavascriptCoverage, entries, directory, threshold, include);
|
|
2110
2110
|
} finally {
|
|
2111
2111
|
await rpc.dispose();
|
|
2112
2112
|
}
|
|
2113
2113
|
};
|
|
2114
2114
|
|
|
2115
|
+
const trackPromise = (promises, promise, onError) => {
|
|
2116
|
+
let trackedPromise = Promise.resolve();
|
|
2117
|
+
trackedPromise = (async () => {
|
|
2118
|
+
try {
|
|
2119
|
+
await promise;
|
|
2120
|
+
} catch (error) {
|
|
2121
|
+
await onError(error);
|
|
2122
|
+
} finally {
|
|
2123
|
+
promises.delete(trackedPromise);
|
|
2124
|
+
}
|
|
2125
|
+
})();
|
|
2126
|
+
promises.add(trackedPromise);
|
|
2127
|
+
};
|
|
2128
|
+
const startWorkerJavascriptCoverage = async (page, targetScript) => {
|
|
2129
|
+
const session = await page.context().newCDPSession(page);
|
|
2130
|
+
const sessions = new Set();
|
|
2131
|
+
const tasks = new Set();
|
|
2132
|
+
const entries = new Map();
|
|
2133
|
+
const pendingEntries = new Set();
|
|
2134
|
+
const timers = new Map();
|
|
2135
|
+
const pendingCommands = new Map();
|
|
2136
|
+
let commandId = 0;
|
|
2137
|
+
let collectionError;
|
|
2138
|
+
let stopped = false;
|
|
2139
|
+
const recordCollectionError = error => {
|
|
2140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2141
|
+
if (!message.includes('No session with given id') && !message.includes('Target closed')) {
|
|
2142
|
+
collectionError = error instanceof Error ? error : new Error(message);
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
const getCollectionError = () => collectionError;
|
|
2146
|
+
const onReceivedMessage = ({
|
|
2147
|
+
message
|
|
2148
|
+
}) => {
|
|
2149
|
+
const response = JSON.parse(message);
|
|
2150
|
+
if (response.id === undefined) {
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
const pending = pendingCommands.get(response.id);
|
|
2154
|
+
if (!pending) {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
pendingCommands.delete(response.id);
|
|
2158
|
+
if (response.error) {
|
|
2159
|
+
pending.reject(new Error(response.error.message));
|
|
2160
|
+
} else {
|
|
2161
|
+
pending.resolve(response.result);
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
const sendToTarget = (sessionId, method, params = {}) => {
|
|
2165
|
+
const id = ++commandId;
|
|
2166
|
+
const command = new Promise((resolve, reject) => {
|
|
2167
|
+
pendingCommands.set(id, {
|
|
2168
|
+
reject,
|
|
2169
|
+
resolve
|
|
2170
|
+
});
|
|
2171
|
+
});
|
|
2172
|
+
void session.send('Target.sendMessageToTarget', {
|
|
2173
|
+
message: JSON.stringify({
|
|
2174
|
+
id,
|
|
2175
|
+
method,
|
|
2176
|
+
params
|
|
2177
|
+
}),
|
|
2178
|
+
sessionId
|
|
2179
|
+
}).catch(error => {
|
|
2180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2181
|
+
const pending = pendingCommands.get(id);
|
|
2182
|
+
pendingCommands.delete(id);
|
|
2183
|
+
pending?.reject(new Error(message));
|
|
2184
|
+
});
|
|
2185
|
+
return command;
|
|
2186
|
+
};
|
|
2187
|
+
const handleAttachedTarget = async ({
|
|
2188
|
+
sessionId,
|
|
2189
|
+
targetInfo
|
|
2190
|
+
}) => {
|
|
2191
|
+
const matches = targetInfo.type === 'worker' && targetInfo.url.includes(targetScript);
|
|
2192
|
+
if (matches) {
|
|
2193
|
+
sessions.add(sessionId);
|
|
2194
|
+
await sendToTarget(sessionId, 'Debugger.enable');
|
|
2195
|
+
await sendToTarget(sessionId, 'Profiler.enable');
|
|
2196
|
+
await sendToTarget(sessionId, 'Profiler.startPreciseCoverage', {
|
|
2197
|
+
callCount: true,
|
|
2198
|
+
detailed: true
|
|
2199
|
+
});
|
|
2200
|
+
const timer = setInterval(() => {
|
|
2201
|
+
trackPromise(pendingEntries, readTargetCoverage(sendToTarget, sessionId, entries), error => {
|
|
2202
|
+
recordCollectionError(error);
|
|
2203
|
+
});
|
|
2204
|
+
}, 50);
|
|
2205
|
+
timers.set(sessionId, timer);
|
|
2206
|
+
}
|
|
2207
|
+
await sendToTarget(sessionId, 'Runtime.runIfWaitingForDebugger');
|
|
2208
|
+
if (matches) {
|
|
2209
|
+
await readTargetCoverage(sendToTarget, sessionId, entries);
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
const onAttached = target => {
|
|
2213
|
+
trackPromise(tasks, handleAttachedTarget(target), async error => {
|
|
2214
|
+
recordCollectionError(error);
|
|
2215
|
+
try {
|
|
2216
|
+
await sendToTarget(target.sessionId, 'Runtime.runIfWaitingForDebugger');
|
|
2217
|
+
} catch {
|
|
2218
|
+
// The target may have closed before it could be resumed.
|
|
2219
|
+
}
|
|
2220
|
+
});
|
|
2221
|
+
};
|
|
2222
|
+
const onDetached = ({
|
|
2223
|
+
sessionId
|
|
2224
|
+
}) => {
|
|
2225
|
+
if (!sessions.has(sessionId)) {
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
sessions.delete(sessionId);
|
|
2229
|
+
const timer = timers.get(sessionId);
|
|
2230
|
+
if (timer) {
|
|
2231
|
+
clearInterval(timer);
|
|
2232
|
+
timers.delete(sessionId);
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
session.on('Target.attachedToTarget', onAttached);
|
|
2236
|
+
session.on('Target.detachedFromTarget', onDetached);
|
|
2237
|
+
session.on('Target.receivedMessageFromTarget', onReceivedMessage);
|
|
2238
|
+
await session.send('Target.setAutoAttach', {
|
|
2239
|
+
autoAttach: true,
|
|
2240
|
+
flatten: false,
|
|
2241
|
+
waitForDebuggerOnStart: true
|
|
2242
|
+
});
|
|
2243
|
+
return {
|
|
2244
|
+
async stop() {
|
|
2245
|
+
if (stopped) {
|
|
2246
|
+
return entries.values().toArray();
|
|
2247
|
+
}
|
|
2248
|
+
stopped = true;
|
|
2249
|
+
session.off('Target.attachedToTarget', onAttached);
|
|
2250
|
+
session.off('Target.detachedFromTarget', onDetached);
|
|
2251
|
+
try {
|
|
2252
|
+
for (const timer of timers.values()) {
|
|
2253
|
+
clearInterval(timer);
|
|
2254
|
+
}
|
|
2255
|
+
timers.clear();
|
|
2256
|
+
await session.send('Target.setAutoAttach', {
|
|
2257
|
+
autoAttach: false,
|
|
2258
|
+
flatten: false,
|
|
2259
|
+
waitForDebuggerOnStart: false
|
|
2260
|
+
});
|
|
2261
|
+
while (tasks.size > 0) {
|
|
2262
|
+
await Promise.all(tasks);
|
|
2263
|
+
}
|
|
2264
|
+
await Promise.all(pendingEntries);
|
|
2265
|
+
const firstCollectionError = getCollectionError();
|
|
2266
|
+
if (firstCollectionError) {
|
|
2267
|
+
throw new Error(firstCollectionError.message, {
|
|
2268
|
+
cause: firstCollectionError
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
for (const sessionId of sessions) {
|
|
2272
|
+
trackPromise(pendingEntries, readTargetCoverage(sendToTarget, sessionId, entries), error => {
|
|
2273
|
+
recordCollectionError(error);
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
await Promise.all(pendingEntries);
|
|
2277
|
+
const finalCollectionError = getCollectionError();
|
|
2278
|
+
if (finalCollectionError) {
|
|
2279
|
+
throw new Error(finalCollectionError.message, {
|
|
2280
|
+
cause: finalCollectionError
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
for (const sessionId of sessions) {
|
|
2284
|
+
try {
|
|
2285
|
+
await sendToTarget(sessionId, 'Profiler.stopPreciseCoverage');
|
|
2286
|
+
} catch {
|
|
2287
|
+
// A target can exit after its last coverage snapshot.
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
const result = entries.values().toArray();
|
|
2291
|
+
if (result.length === 0) {
|
|
2292
|
+
throw new Error(`[test-with-playwright] no JavaScript coverage was collected for worker target ${targetScript}`);
|
|
2293
|
+
}
|
|
2294
|
+
return result;
|
|
2295
|
+
} finally {
|
|
2296
|
+
session.off('Target.receivedMessageFromTarget', onReceivedMessage);
|
|
2297
|
+
await session.detach();
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
};
|
|
2302
|
+
const readTargetCoverage = async (sendToTarget, sessionId, entries) => {
|
|
2303
|
+
try {
|
|
2304
|
+
const {
|
|
2305
|
+
result
|
|
2306
|
+
} = await sendToTarget(sessionId, 'Profiler.takePreciseCoverage');
|
|
2307
|
+
for (const script of result) {
|
|
2308
|
+
if (!script.url) {
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
const {
|
|
2312
|
+
scriptSource
|
|
2313
|
+
} = await sendToTarget(sessionId, 'Debugger.getScriptSource', {
|
|
2314
|
+
scriptId: script.scriptId
|
|
2315
|
+
});
|
|
2316
|
+
const key = `${sessionId}:${script.scriptId}`;
|
|
2317
|
+
const previous = entries.get(key);
|
|
2318
|
+
const functions = script.functions.map((fn, index) => {
|
|
2319
|
+
const previousFunction = previous?.functions[index];
|
|
2320
|
+
return {
|
|
2321
|
+
functionName: fn.functionName,
|
|
2322
|
+
isBlockCoverage: fn.isBlockCoverage,
|
|
2323
|
+
ranges: fn.ranges.map((range, rangeIndex) => {
|
|
2324
|
+
const previousRange = previousFunction?.ranges[rangeIndex];
|
|
2325
|
+
return {
|
|
2326
|
+
count: range.count + (previousRange?.count ?? 0),
|
|
2327
|
+
endOffset: range.endOffset,
|
|
2328
|
+
startOffset: range.startOffset
|
|
2329
|
+
};
|
|
2330
|
+
})
|
|
2331
|
+
};
|
|
2332
|
+
});
|
|
2333
|
+
entries.set(key, {
|
|
2334
|
+
functions,
|
|
2335
|
+
scriptId: script.scriptId,
|
|
2336
|
+
source: previous?.source ?? scriptSource,
|
|
2337
|
+
url: script.url
|
|
2338
|
+
});
|
|
2339
|
+
}
|
|
2340
|
+
} catch (error) {
|
|
2341
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2342
|
+
if (!message.includes('No session with given id') && !message.includes('Target closed')) {
|
|
2343
|
+
throw error;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
|
|
2115
2348
|
const runWithJavascriptCoverage = async ({
|
|
2116
2349
|
coverage,
|
|
2350
|
+
coverageInclude,
|
|
2351
|
+
coverageTarget,
|
|
2352
|
+
coverageThreshold,
|
|
2117
2353
|
cwd,
|
|
2118
2354
|
page,
|
|
2119
2355
|
run
|
|
2120
2356
|
}) => {
|
|
2121
|
-
if (!coverage) {
|
|
2357
|
+
if (!coverage && !coverageTarget) {
|
|
2122
2358
|
await run();
|
|
2123
2359
|
return;
|
|
2124
2360
|
}
|
|
2125
|
-
await page
|
|
2126
|
-
|
|
2127
|
-
});
|
|
2361
|
+
const workerCoverage = coverageTarget ? await startWorkerJavascriptCoverage(page, coverageTarget) : undefined;
|
|
2362
|
+
let pageCoverageStarted = false;
|
|
2128
2363
|
try {
|
|
2364
|
+
if (coverage) {
|
|
2365
|
+
await page.coverage.startJSCoverage({
|
|
2366
|
+
resetOnNavigation: false
|
|
2367
|
+
});
|
|
2368
|
+
pageCoverageStarted = true;
|
|
2369
|
+
}
|
|
2129
2370
|
await run();
|
|
2130
2371
|
} finally {
|
|
2131
|
-
|
|
2132
|
-
|
|
2372
|
+
let workerEntries;
|
|
2373
|
+
if (pageCoverageStarted) {
|
|
2374
|
+
try {
|
|
2375
|
+
const entries = await page.coverage.stopJSCoverage();
|
|
2376
|
+
await writeJavascriptCoverage(entries, join(cwd, 'coverage'));
|
|
2377
|
+
} finally {
|
|
2378
|
+
workerEntries = await workerCoverage?.stop();
|
|
2379
|
+
}
|
|
2380
|
+
} else if (workerCoverage) {
|
|
2381
|
+
workerEntries = await workerCoverage.stop();
|
|
2382
|
+
}
|
|
2383
|
+
if (workerEntries) {
|
|
2384
|
+
await writeJavascriptCoverage(workerEntries, join(cwd, 'coverage', 'worker'), coverageThreshold, coverageInclude);
|
|
2385
|
+
}
|
|
2133
2386
|
}
|
|
2134
2387
|
};
|
|
2135
2388
|
|
|
@@ -2628,6 +2881,17 @@ const tearDownTests = async ({
|
|
|
2628
2881
|
}
|
|
2629
2882
|
};
|
|
2630
2883
|
|
|
2884
|
+
const assertCoverageOptions = (coverageTarget, coverageInclude, coverageThreshold) => {
|
|
2885
|
+
if (coverageTarget !== undefined) {
|
|
2886
|
+
string(coverageTarget);
|
|
2887
|
+
}
|
|
2888
|
+
if (coverageInclude !== undefined) {
|
|
2889
|
+
string(coverageInclude);
|
|
2890
|
+
}
|
|
2891
|
+
if (coverageThreshold !== undefined) {
|
|
2892
|
+
number(coverageThreshold);
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2631
2895
|
/**
|
|
2632
2896
|
* @param {string} extensionPath
|
|
2633
2897
|
* @param {string} testPath
|
|
@@ -2642,7 +2906,7 @@ const tearDownTests = async ({
|
|
|
2642
2906
|
* @param {boolean} coverage
|
|
2643
2907
|
* @param {boolean} traceRendererWorker
|
|
2644
2908
|
*/
|
|
2645
|
-
const runAllTests = async (extensionPath, testPath, cwd, browser, headless, timeout, runtimeOptions, traceFocus, filter, reusePage, svgScreenshotOptions, coverage, traceRendererWorker) => {
|
|
2909
|
+
const runAllTests = async (extensionPath, testPath, cwd, browser, headless, timeout, runtimeOptions, traceFocus, filter, reusePage, svgScreenshotOptions, coverage, coverageInclude, coverageTarget, coverageThreshold, traceRendererWorker) => {
|
|
2646
2910
|
string(extensionPath);
|
|
2647
2911
|
string(testPath);
|
|
2648
2912
|
string(cwd);
|
|
@@ -2652,6 +2916,7 @@ const runAllTests = async (extensionPath, testPath, cwd, browser, headless, time
|
|
|
2652
2916
|
object(runtimeOptions);
|
|
2653
2917
|
boolean(reusePage);
|
|
2654
2918
|
boolean(coverage);
|
|
2919
|
+
assertCoverageOptions(coverageTarget, coverageInclude, coverageThreshold);
|
|
2655
2920
|
boolean(traceRendererWorker);
|
|
2656
2921
|
if (svgScreenshotOptions !== undefined) {
|
|
2657
2922
|
object(svgScreenshotOptions);
|
|
@@ -2683,6 +2948,15 @@ const runAllTests = async (extensionPath, testPath, cwd, browser, headless, time
|
|
|
2683
2948
|
});
|
|
2684
2949
|
await runWithJavascriptCoverage({
|
|
2685
2950
|
coverage,
|
|
2951
|
+
...(coverageTarget !== undefined && {
|
|
2952
|
+
coverageTarget
|
|
2953
|
+
}),
|
|
2954
|
+
...(coverageInclude !== undefined && {
|
|
2955
|
+
coverageInclude
|
|
2956
|
+
}),
|
|
2957
|
+
...(coverageThreshold !== undefined && {
|
|
2958
|
+
coverageThreshold
|
|
2959
|
+
}),
|
|
2686
2960
|
cwd,
|
|
2687
2961
|
page: electron.page,
|
|
2688
2962
|
run: async () => {
|
|
@@ -2728,6 +3002,15 @@ const runAllTests = async (extensionPath, testPath, cwd, browser, headless, time
|
|
|
2728
3002
|
if (reusePage) {
|
|
2729
3003
|
await runWithJavascriptCoverage({
|
|
2730
3004
|
coverage,
|
|
3005
|
+
...(coverageTarget !== undefined && {
|
|
3006
|
+
coverageTarget
|
|
3007
|
+
}),
|
|
3008
|
+
...(coverageInclude !== undefined && {
|
|
3009
|
+
coverageInclude
|
|
3010
|
+
}),
|
|
3011
|
+
...(coverageThreshold !== undefined && {
|
|
3012
|
+
coverageThreshold
|
|
3013
|
+
}),
|
|
2731
3014
|
cwd,
|
|
2732
3015
|
page,
|
|
2733
3016
|
run: async () => {
|