@rstest/browser 0.11.5 → 0.11.7
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/browser-container/container-static/css/index.43daaf6c93.css +1 -0
- package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
- package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browserExecutor.d.ts +7 -5
- package/dist/browserRsbuild.d.ts +139 -0
- package/dist/browserV8Coverage.d.ts +15 -0
- package/dist/client/240~1.js +150 -0
- package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
- package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
- package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
- package/dist/client/client/index.d.ts +2 -0
- package/dist/client/client/runIdentity.d.ts +12 -0
- package/dist/client/client/runner.d.ts +4 -0
- package/dist/client/client/snapshot.d.ts +27 -0
- package/dist/client/client/sourceMapSupport.d.ts +36 -0
- package/dist/{browser.js → client/index.js} +3 -102
- package/dist/client/protocol.d.ts +225 -0
- package/dist/client/rpcProtocol.d.ts +138 -0
- package/dist/client/runner.js +675 -0
- package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
- package/dist/containerRpc.d.ts +60 -0
- package/dist/dispatchCapabilities.d.ts +1 -2
- package/dist/headedRunRegistry.d.ts +51 -0
- package/dist/headedScheduler.d.ts +58 -0
- package/dist/headlessScheduler.d.ts +38 -0
- package/dist/headlessTransport.d.ts +4 -2
- package/dist/hostController.d.ts +12 -47
- package/dist/hostPayloads.d.ts +24 -0
- package/dist/index.js +2019 -1516
- package/dist/protocol.d.ts +48 -8
- package/dist/providers/index.d.ts +21 -0
- package/dist/providers/playwright/v8Coverage.d.ts +2 -0
- package/dist/rpcProtocol.d.ts +1 -2
- package/dist/schedulerSeam.d.ts +42 -0
- package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
- package/dist/watchRerunPlanner.d.ts +20 -4
- package/dist/watchRuntime.d.ts +21 -0
- package/dist/watchSignals.d.ts +29 -0
- package/package.json +12 -12
- package/dist/33.js +0 -7
- package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
- package/dist/browser-container/container-static/js/506.b2f920fa67.js +0 -194
- package/dist/browser-container/container-static/js/index.d27fd63f5e.js +0 -1
- package/dist/browser.d.ts +0 -2
- package/dist/headlessLatestRerunScheduler.d.ts +0 -18
- package/src/augmentExpect.ts +0 -62
- package/src/browser.ts +0 -3
- package/src/browserExecutor.ts +0 -140
- package/src/browserRpcRegistry.ts +0 -57
- package/src/client/api.ts +0 -213
- package/src/client/browserRpc.ts +0 -86
- package/src/client/dispatchTransport.ts +0 -240
- package/src/client/entry.ts +0 -807
- package/src/client/locator.ts +0 -448
- package/src/client/snapshot.ts +0 -113
- package/src/client/sourceMapSupport.ts +0 -159
- package/src/concurrency.ts +0 -65
- package/src/configValidation.ts +0 -263
- package/src/dispatchCapabilities.ts +0 -182
- package/src/dispatchRouter.ts +0 -82
- package/src/env.d.ts +0 -54
- package/src/headedSerialTaskQueue.ts +0 -23
- package/src/headlessLatestRerunScheduler.ts +0 -76
- package/src/headlessTransport.ts +0 -28
- package/src/hostController.ts +0 -4633
- package/src/index.ts +0 -51
- package/src/manifest.d.ts +0 -41
- package/src/protocol.ts +0 -225
- package/src/providers/index.ts +0 -126
- package/src/providers/playwright/compileLocator.ts +0 -130
- package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
- package/src/providers/playwright/expectUtils.ts +0 -57
- package/src/providers/playwright/implementation.ts +0 -35
- package/src/providers/playwright/index.ts +0 -1
- package/src/providers/playwright/runtime.ts +0 -159
- package/src/providers/playwright/textMatcher.ts +0 -10
- package/src/rpcProtocol.ts +0 -218
- package/src/runSession.ts +0 -110
- package/src/sessionRegistry.ts +0 -89
- package/src/sourceMap/sourceMapLoader.ts +0 -96
- package/src/viewportPresets.ts +0 -69
- package/src/watchRerunPlanner.ts +0 -77
- /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
- /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
- /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import { projectSetupLoaders, projectTestContexts, projects } from "__rstest_virtual_browser_manifest__";
|
|
2
|
+
import { RSTEST_API_GLOBAL_KEY, RSTEST_ENV_SYMBOL_KEY, RSTEST_IMPORT_META_GLOBAL_KEY, SNAPSHOT_HEADER, createBrowserTaskContext, createRstestRuntime, formatConsoleArgs, globalApis, setRealTimers, unwrapRegex } from "@rstest/core/internal/browser-runtime";
|
|
3
|
+
import { normalize } from "pathe";
|
|
4
|
+
import { AnyMap, TraceMap, encodedMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
5
|
+
import convert_source_map from "convert-source-map";
|
|
6
|
+
import { createRequestId, createRunnerLifecycleRequest, dispatchRpc, setRpcPhase, disposeDispatchTransport, DISPATCH_NAMESPACE_FILE_CLEANUP, sendDispatchRequest, RSTEST_BROWSER_CACHE_CLEANERS_KEY, RSTEST_CONFIG_MESSAGE_TYPE, DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_SNAPSHOT, getRunIdentity, adoptRunIdentity, getRpcTimeout } from "./240~1.js";
|
|
7
|
+
const flattenSourceMap = (sourceMap, sourceMapUrl)=>encodedMap(new AnyMap(sourceMap, sourceMapUrl));
|
|
8
|
+
const normalizeJavaScriptUrl = (value, options)=>{
|
|
9
|
+
try {
|
|
10
|
+
const url = options?.origin ? new URL(value, options.origin) : new URL(value);
|
|
11
|
+
if ('http:' !== url.protocol && 'https:' !== url.protocol) return null;
|
|
12
|
+
url.hash = '';
|
|
13
|
+
return url.toString();
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const resolveInlineSourceMap = (code, sourceMapUrl)=>{
|
|
19
|
+
try {
|
|
20
|
+
const converter = convert_source_map.fromSource(code);
|
|
21
|
+
return converter ? flattenSourceMap(converter.toObject(), sourceMapUrl) : null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const fetchSourceMap = async (jsUrl, fetcher)=>{
|
|
27
|
+
const jsResponse = await fetcher(jsUrl);
|
|
28
|
+
if (!jsResponse.ok) return null;
|
|
29
|
+
const code = await jsResponse.text();
|
|
30
|
+
const inlineMap = resolveInlineSourceMap(code, jsUrl);
|
|
31
|
+
if (inlineMap) return inlineMap;
|
|
32
|
+
const mapUrl = new URL(jsUrl);
|
|
33
|
+
mapUrl.pathname += '.map';
|
|
34
|
+
const mapResponse = await fetcher(mapUrl.href);
|
|
35
|
+
if (!mapResponse.ok) return null;
|
|
36
|
+
return flattenSourceMap(await mapResponse.json(), mapUrl.href);
|
|
37
|
+
};
|
|
38
|
+
const loadSourceMapWithCache = async ({ jsUrl, cache, force = false, origin, fetcher = fetch })=>{
|
|
39
|
+
const normalizedUrl = normalizeJavaScriptUrl(jsUrl, {
|
|
40
|
+
origin
|
|
41
|
+
});
|
|
42
|
+
if (!normalizedUrl) return null;
|
|
43
|
+
if (!force && cache.has(normalizedUrl)) return cache.get(normalizedUrl) ?? null;
|
|
44
|
+
try {
|
|
45
|
+
const sourceMap = await fetchSourceMap(normalizedUrl, fetcher);
|
|
46
|
+
cache.set(normalizedUrl, sourceMap);
|
|
47
|
+
return sourceMap;
|
|
48
|
+
} catch {
|
|
49
|
+
cache.set(normalizedUrl, null);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const sourceMapCache = new Map();
|
|
54
|
+
const sourceMapPayloadCache = new Map();
|
|
55
|
+
const getSourceMap = (url)=>sourceMapCache.get(url) ?? null;
|
|
56
|
+
const preloadSourceMap = async (jsUrl, force = false)=>{
|
|
57
|
+
const normalizedUrl = normalizeJavaScriptUrl(jsUrl, {
|
|
58
|
+
origin: window.location.origin
|
|
59
|
+
});
|
|
60
|
+
if (!normalizedUrl) return;
|
|
61
|
+
if (!force && sourceMapCache.has(normalizedUrl)) return;
|
|
62
|
+
const sourceMap = await loadSourceMapWithCache({
|
|
63
|
+
jsUrl: normalizedUrl,
|
|
64
|
+
cache: sourceMapPayloadCache,
|
|
65
|
+
force,
|
|
66
|
+
origin: window.location.origin
|
|
67
|
+
});
|
|
68
|
+
sourceMapCache.set(normalizedUrl, sourceMap ? new TraceMap(sourceMap) : null);
|
|
69
|
+
};
|
|
70
|
+
const getScriptUrls = ()=>{
|
|
71
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
72
|
+
const urls = new Set();
|
|
73
|
+
for (const script of scripts){
|
|
74
|
+
const src = script.getAttribute('src');
|
|
75
|
+
if (src) {
|
|
76
|
+
const fullUrl = src.startsWith('http') ? src : `${window.location.origin}${src.startsWith('/') ? '' : '/'}${src}`;
|
|
77
|
+
urls.add(fullUrl);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return urls;
|
|
81
|
+
};
|
|
82
|
+
const findNewScriptUrl = (beforeUrls, afterUrls)=>{
|
|
83
|
+
for (const url of afterUrls)if (!beforeUrls.has(url)) return url;
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
const preloadTestFileSourceMap = async (chunkUrl)=>{
|
|
87
|
+
await preloadSourceMap(chunkUrl, true);
|
|
88
|
+
};
|
|
89
|
+
const preloadRunnerSourceMap = async ()=>{
|
|
90
|
+
const runnerUrl = `${window.location.origin}/static/js/runner.js`;
|
|
91
|
+
await preloadSourceMap(runnerUrl);
|
|
92
|
+
};
|
|
93
|
+
const mapStackFrame = (frame)=>{
|
|
94
|
+
const { file, line, column } = frame;
|
|
95
|
+
const fullUrl = normalizeJavaScriptUrl(file, {
|
|
96
|
+
origin: window.location.origin
|
|
97
|
+
});
|
|
98
|
+
if (!fullUrl) return frame;
|
|
99
|
+
const traceMap = getSourceMap(fullUrl);
|
|
100
|
+
if (!traceMap) return frame;
|
|
101
|
+
const pos = originalPositionFor(traceMap, {
|
|
102
|
+
line,
|
|
103
|
+
column: column - 1
|
|
104
|
+
});
|
|
105
|
+
if (pos.source && null != pos.line && null != pos.column) return {
|
|
106
|
+
...frame,
|
|
107
|
+
file: pos.source,
|
|
108
|
+
line: pos.line,
|
|
109
|
+
column: pos.column + 1,
|
|
110
|
+
method: pos.name || frame.method
|
|
111
|
+
};
|
|
112
|
+
return frame;
|
|
113
|
+
};
|
|
114
|
+
const createSnapshotDispatchRequest = (requestId, call)=>({
|
|
115
|
+
requestId,
|
|
116
|
+
namespace: DISPATCH_NAMESPACE_SNAPSHOT,
|
|
117
|
+
method: call.method,
|
|
118
|
+
args: call.args
|
|
119
|
+
});
|
|
120
|
+
const sendRpcRequest = (call)=>{
|
|
121
|
+
const requestId = createRequestId('snapshot-rpc');
|
|
122
|
+
const rpcTimeout = getRpcTimeout();
|
|
123
|
+
const dispatchRequest = createSnapshotDispatchRequest(requestId, call);
|
|
124
|
+
return dispatchRpc({
|
|
125
|
+
requestId,
|
|
126
|
+
request: dispatchRequest,
|
|
127
|
+
timeoutMs: rpcTimeout,
|
|
128
|
+
staleMessage: 'Stale snapshot RPC request ignored.',
|
|
129
|
+
timeoutMessage: `Snapshot RPC timeout after ${rpcTimeout / 1000}s: ${call.method}`
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
class BrowserSnapshotEnvironment {
|
|
133
|
+
getVersion() {
|
|
134
|
+
return '1';
|
|
135
|
+
}
|
|
136
|
+
getHeader() {
|
|
137
|
+
return `${SNAPSHOT_HEADER} v${this.getVersion()}`;
|
|
138
|
+
}
|
|
139
|
+
async resolveRawPath(_testPath, rawPath) {
|
|
140
|
+
return rawPath;
|
|
141
|
+
}
|
|
142
|
+
async resolvePath(filepath) {
|
|
143
|
+
return sendRpcRequest({
|
|
144
|
+
method: 'resolveSnapshotPath',
|
|
145
|
+
args: {
|
|
146
|
+
testPath: filepath
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async prepareDirectory() {}
|
|
151
|
+
async saveSnapshotFile(filepath, snapshot) {
|
|
152
|
+
await sendRpcRequest({
|
|
153
|
+
method: 'saveSnapshotFile',
|
|
154
|
+
args: {
|
|
155
|
+
filepath,
|
|
156
|
+
content: snapshot
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async readSnapshotFile(filepath) {
|
|
161
|
+
return sendRpcRequest({
|
|
162
|
+
method: 'readSnapshotFile',
|
|
163
|
+
args: {
|
|
164
|
+
filepath
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async removeSnapshotFile(filepath) {
|
|
169
|
+
await sendRpcRequest({
|
|
170
|
+
method: 'removeSnapshotFile',
|
|
171
|
+
args: {
|
|
172
|
+
filepath
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
processStackTrace(stack) {
|
|
177
|
+
const mapped = mapStackFrame(stack);
|
|
178
|
+
return {
|
|
179
|
+
file: mapped.file,
|
|
180
|
+
line: mapped.line,
|
|
181
|
+
column: mapped.column,
|
|
182
|
+
method: mapped.method || stack.method
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const debugLog = (...args)=>{
|
|
187
|
+
if (window.__RSTEST_BROWSER_OPTIONS__?.debug) console.log(...args);
|
|
188
|
+
};
|
|
189
|
+
const RSTEST_ENV_SYMBOL = Symbol.for(RSTEST_ENV_SYMBOL_KEY);
|
|
190
|
+
const installRuntimeGlobals = (runtime, runtimeConfig)=>{
|
|
191
|
+
Object.assign(globalThis, {
|
|
192
|
+
[RSTEST_API_GLOBAL_KEY]: runtime.api,
|
|
193
|
+
[RSTEST_IMPORT_META_GLOBAL_KEY]: runtime.resolveImportMetaRstest
|
|
194
|
+
});
|
|
195
|
+
if (runtimeConfig.globals) for (const apiKey of globalApis)globalThis[apiKey] = runtime.api[apiKey];
|
|
196
|
+
};
|
|
197
|
+
const clearBrowserTestEntryCache = (testEntryPath)=>{
|
|
198
|
+
const cleaners = globalThis[RSTEST_BROWSER_CACHE_CLEANERS_KEY];
|
|
199
|
+
cleaners?.forEach((clean)=>clean(testEntryPath));
|
|
200
|
+
};
|
|
201
|
+
const restoreRuntimeConfig = (config)=>{
|
|
202
|
+
const { testNamePattern } = config;
|
|
203
|
+
return {
|
|
204
|
+
...config,
|
|
205
|
+
testNamePattern: 'string' == typeof testNamePattern ? unwrapRegex(testNamePattern) : testNamePattern
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const ensureRuntimeEnv = (env)=>{
|
|
209
|
+
const globalRef = globalThis;
|
|
210
|
+
if (!globalRef.global) globalRef.global = globalRef;
|
|
211
|
+
const existingEnv = globalRef[RSTEST_ENV_SYMBOL];
|
|
212
|
+
let runtimeEnv;
|
|
213
|
+
if (existingEnv && 'object' == typeof existingEnv) runtimeEnv = existingEnv;
|
|
214
|
+
else {
|
|
215
|
+
runtimeEnv = {};
|
|
216
|
+
globalRef[RSTEST_ENV_SYMBOL] = runtimeEnv;
|
|
217
|
+
}
|
|
218
|
+
if (env) for (const [key, value] of Object.entries(env)){
|
|
219
|
+
const normalizedValue = 'string' == typeof value ? value : null == value ? void 0 : String(value);
|
|
220
|
+
if (void 0 === normalizedValue) delete runtimeEnv[key];
|
|
221
|
+
else runtimeEnv[key] = normalizedValue;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const getFileTaskId = (testPath)=>`file:${testPath}`;
|
|
225
|
+
const interceptConsole = (projectName, getCurrentTask, printConsoleTrace)=>{
|
|
226
|
+
const originalConsole = {
|
|
227
|
+
log: console.log.bind(console),
|
|
228
|
+
warn: console.warn.bind(console),
|
|
229
|
+
error: console.error.bind(console),
|
|
230
|
+
info: console.info.bind(console),
|
|
231
|
+
debug: console.debug.bind(console)
|
|
232
|
+
};
|
|
233
|
+
const getConsoleTrace = ()=>{
|
|
234
|
+
if (!printConsoleTrace) return;
|
|
235
|
+
const stack = new Error('STACK_TRACE').stack;
|
|
236
|
+
return stack?.split('\n').slice(4).join('\n');
|
|
237
|
+
};
|
|
238
|
+
const createConsoleInterceptor = (level)=>(...args)=>{
|
|
239
|
+
originalConsole[level](...args);
|
|
240
|
+
const content = formatConsoleArgs(args);
|
|
241
|
+
const currentTask = getCurrentTask();
|
|
242
|
+
send({
|
|
243
|
+
type: 'log',
|
|
244
|
+
payload: {
|
|
245
|
+
level,
|
|
246
|
+
content,
|
|
247
|
+
projectName,
|
|
248
|
+
taskId: currentTask?.taskId,
|
|
249
|
+
taskName: currentTask?.taskName,
|
|
250
|
+
taskParentNames: currentTask?.taskParentNames,
|
|
251
|
+
taskType: currentTask?.taskType,
|
|
252
|
+
testPath: currentTask?.testPath ?? '',
|
|
253
|
+
type: 'error' === level || 'warn' === level ? 'stderr' : 'stdout',
|
|
254
|
+
trace: getConsoleTrace()
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
console.log = createConsoleInterceptor('log');
|
|
259
|
+
console.warn = createConsoleInterceptor('warn');
|
|
260
|
+
console.error = createConsoleInterceptor('error');
|
|
261
|
+
console.info = createConsoleInterceptor('info');
|
|
262
|
+
console.debug = createConsoleInterceptor('debug');
|
|
263
|
+
return ()=>{
|
|
264
|
+
console.log = originalConsole.log;
|
|
265
|
+
console.warn = originalConsole.warn;
|
|
266
|
+
console.error = originalConsole.error;
|
|
267
|
+
console.info = originalConsole.info;
|
|
268
|
+
console.debug = originalConsole.debug;
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
const send = (message)=>{
|
|
272
|
+
const envelope = {
|
|
273
|
+
runId: getRunIdentity(),
|
|
274
|
+
message
|
|
275
|
+
};
|
|
276
|
+
if (window.parent !== window) return void window.parent.postMessage({
|
|
277
|
+
type: DISPATCH_MESSAGE_TYPE,
|
|
278
|
+
payload: envelope
|
|
279
|
+
}, '*');
|
|
280
|
+
window[DISPATCH_MESSAGE_TYPE]?.(envelope);
|
|
281
|
+
};
|
|
282
|
+
const dispatchRunnerLifecycle = (method, payload)=>{
|
|
283
|
+
sendDispatchRequest(createRunnerLifecycleRequest(method, payload), (error)=>{
|
|
284
|
+
debugLog('[Runner] Failed to dispatch lifecycle method:', method, error);
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
const CONFIG_WAIT_TIMEOUT_MS = 30000;
|
|
288
|
+
const waitForConfig = ()=>{
|
|
289
|
+
if (window.parent === window || window.__RSTEST_BROWSER_OPTIONS__) return Promise.resolve();
|
|
290
|
+
return new Promise((resolve, reject)=>{
|
|
291
|
+
const handleMessage = (event)=>{
|
|
292
|
+
if (event.data?.type === RSTEST_CONFIG_MESSAGE_TYPE && 'string' == typeof event.data.payload?.runId) {
|
|
293
|
+
window.__RSTEST_BROWSER_OPTIONS__ = event.data.payload;
|
|
294
|
+
debugLog('[Runner] Received config from container:', event.data.payload);
|
|
295
|
+
window.removeEventListener('message', handleMessage);
|
|
296
|
+
resolve();
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
window.addEventListener('message', handleMessage);
|
|
300
|
+
setTimeout(()=>{
|
|
301
|
+
window.removeEventListener('message', handleMessage);
|
|
302
|
+
reject(new Error(`[Rstest] Failed to receive browser config within ${CONFIG_WAIT_TIMEOUT_MS / 1000}s. This may indicate a connection issue between the runner iframe and container.`));
|
|
303
|
+
}, CONFIG_WAIT_TIMEOUT_MS);
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
const toContextKey = (absolutePath, projectRoot)=>{
|
|
307
|
+
const normalizedAbsolute = normalize(absolutePath);
|
|
308
|
+
const normalizedRoot = normalize(projectRoot);
|
|
309
|
+
const withinRoot = normalizedAbsolute === normalizedRoot || normalizedAbsolute.startsWith(`${normalizedRoot}/`);
|
|
310
|
+
if (!withinRoot) return normalizedAbsolute;
|
|
311
|
+
const relative = normalizedAbsolute.slice(normalizedRoot.length);
|
|
312
|
+
return relative.startsWith('/') ? `.${relative}` : `./${relative}`;
|
|
313
|
+
};
|
|
314
|
+
const toAbsolutePath = (key, projectRoot)=>{
|
|
315
|
+
if (!key.startsWith('.')) return key;
|
|
316
|
+
const normalizedRoot = normalize(projectRoot).replace(/\/$/, '');
|
|
317
|
+
return normalizedRoot + key.slice(1);
|
|
318
|
+
};
|
|
319
|
+
const findProjectForTestFile = (testFile, allProjects)=>{
|
|
320
|
+
const normalizedTestFile = normalize(testFile);
|
|
321
|
+
const sorted = [
|
|
322
|
+
...allProjects
|
|
323
|
+
].sort((a, b)=>b.projectRoot.length - a.projectRoot.length);
|
|
324
|
+
for (const proj of sorted){
|
|
325
|
+
const normalizedRoot = normalize(proj.projectRoot);
|
|
326
|
+
if (normalizedTestFile.startsWith(normalizedRoot)) return proj;
|
|
327
|
+
}
|
|
328
|
+
return allProjects[0];
|
|
329
|
+
};
|
|
330
|
+
const run = async ()=>{
|
|
331
|
+
await waitForConfig();
|
|
332
|
+
let options = window.__RSTEST_BROWSER_OPTIONS__;
|
|
333
|
+
if (options?.runId) adoptRunIdentity(options.runId);
|
|
334
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
335
|
+
const urlTestFile = urlParams.get('testFile');
|
|
336
|
+
const urlTestNamePattern = urlParams.get('testNamePattern');
|
|
337
|
+
if (urlTestFile && options) options = {
|
|
338
|
+
...options,
|
|
339
|
+
testFile: urlTestFile
|
|
340
|
+
};
|
|
341
|
+
if (urlTestNamePattern && options) options = {
|
|
342
|
+
...options,
|
|
343
|
+
projects: options.projects.map((project)=>({
|
|
344
|
+
...project,
|
|
345
|
+
runtimeConfig: {
|
|
346
|
+
...project.runtimeConfig,
|
|
347
|
+
testNamePattern: urlTestNamePattern
|
|
348
|
+
}
|
|
349
|
+
}))
|
|
350
|
+
};
|
|
351
|
+
if (!options) {
|
|
352
|
+
send({
|
|
353
|
+
type: 'fatal',
|
|
354
|
+
payload: {
|
|
355
|
+
message: 'Browser test runtime is not configured.'
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
window.__RSTEST_DONE__ = true;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
send({
|
|
362
|
+
type: 'ready'
|
|
363
|
+
});
|
|
364
|
+
setRealTimers();
|
|
365
|
+
await preloadRunnerSourceMap();
|
|
366
|
+
const targetTestFile = options.testFile;
|
|
367
|
+
const currentProject = targetTestFile ? findProjectForTestFile(targetTestFile, projects) : projects["0"];
|
|
368
|
+
if (!currentProject) {
|
|
369
|
+
send({
|
|
370
|
+
type: 'fatal',
|
|
371
|
+
payload: {
|
|
372
|
+
message: 'No project found for test file'
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
window.__RSTEST_DONE__ = true;
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const projectRuntime = options.projects.find((p)=>p.name === currentProject.name);
|
|
379
|
+
if (!projectRuntime) {
|
|
380
|
+
send({
|
|
381
|
+
type: 'fatal',
|
|
382
|
+
payload: {
|
|
383
|
+
message: `Project ${currentProject.name} not found in runtime options`
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
window.__RSTEST_DONE__ = true;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const runtimeConfig = restoreRuntimeConfig(projectRuntime.runtimeConfig);
|
|
390
|
+
ensureRuntimeEnv(runtimeConfig.env);
|
|
391
|
+
const currentSetupLoaders = projectSetupLoaders[currentProject.name] || [];
|
|
392
|
+
const currentTestContext = projectTestContexts[currentProject.name];
|
|
393
|
+
if (!currentTestContext) {
|
|
394
|
+
send({
|
|
395
|
+
type: 'fatal',
|
|
396
|
+
payload: {
|
|
397
|
+
message: `Test context not found for project ${currentProject.name}`
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
window.__RSTEST_DONE__ = true;
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const loadSetupFiles = async ()=>{
|
|
404
|
+
for (const loadSetup of currentSetupLoaders)await loadSetup();
|
|
405
|
+
};
|
|
406
|
+
let testKeysToRun;
|
|
407
|
+
if (targetTestFile) {
|
|
408
|
+
const key = toContextKey(targetTestFile, currentProject.projectRoot);
|
|
409
|
+
testKeysToRun = [
|
|
410
|
+
key
|
|
411
|
+
];
|
|
412
|
+
} else testKeysToRun = currentTestContext.getTestKeys();
|
|
413
|
+
const executionMode = options.mode || 'run';
|
|
414
|
+
if ('collect' === executionMode) {
|
|
415
|
+
for (const key of testKeysToRun){
|
|
416
|
+
const testPath = toAbsolutePath(key, currentProject.projectRoot);
|
|
417
|
+
const workerState = {
|
|
418
|
+
project: projectRuntime.name,
|
|
419
|
+
projectRoot: projectRuntime.projectRoot,
|
|
420
|
+
rootPath: options.rootPath,
|
|
421
|
+
runtimeConfig,
|
|
422
|
+
taskId: 0,
|
|
423
|
+
buildId: 0,
|
|
424
|
+
outputModule: false,
|
|
425
|
+
environment: 'browser',
|
|
426
|
+
testPath,
|
|
427
|
+
distPath: testPath,
|
|
428
|
+
snapshotOptions: {
|
|
429
|
+
updateSnapshot: options.snapshot.updateSnapshot,
|
|
430
|
+
snapshotEnvironment: new BrowserSnapshotEnvironment(),
|
|
431
|
+
snapshotFormat: runtimeConfig.snapshotFormat
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
const runtime = await createRstestRuntime(workerState, {
|
|
435
|
+
taskContext: createBrowserTaskContext()
|
|
436
|
+
});
|
|
437
|
+
installRuntimeGlobals(runtime, runtimeConfig);
|
|
438
|
+
try {
|
|
439
|
+
setRpcPhase('framework');
|
|
440
|
+
await loadSetupFiles();
|
|
441
|
+
clearBrowserTestEntryCache(testPath);
|
|
442
|
+
await currentTestContext.loadTest(key);
|
|
443
|
+
const tests = await runtime.runner.collectTests();
|
|
444
|
+
send({
|
|
445
|
+
type: 'collect-result',
|
|
446
|
+
payload: {
|
|
447
|
+
testPath,
|
|
448
|
+
project: projectRuntime.name,
|
|
449
|
+
tests
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
} catch (_error) {
|
|
453
|
+
const error = _error instanceof Error ? _error : new Error(String(_error));
|
|
454
|
+
send({
|
|
455
|
+
type: 'fatal',
|
|
456
|
+
payload: {
|
|
457
|
+
message: error.message,
|
|
458
|
+
stack: error.stack
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
window.__RSTEST_DONE__ = true;
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
send({
|
|
466
|
+
type: 'collect-complete'
|
|
467
|
+
});
|
|
468
|
+
window.__RSTEST_DONE__ = true;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
let activeUnhandledErrors;
|
|
472
|
+
const onWindowError = (event)=>{
|
|
473
|
+
activeUnhandledErrors?.push(event.error instanceof Error ? event.error : new Error(event.message || String(event.error)));
|
|
474
|
+
};
|
|
475
|
+
const onUnhandledRejection = (event)=>{
|
|
476
|
+
const { reason } = event;
|
|
477
|
+
activeUnhandledErrors?.push(reason instanceof Error ? reason : new Error(String(reason)));
|
|
478
|
+
};
|
|
479
|
+
window.addEventListener('error', onWindowError);
|
|
480
|
+
window.addEventListener('unhandledrejection', onUnhandledRejection);
|
|
481
|
+
for (const key of testKeysToRun){
|
|
482
|
+
const testPath = toAbsolutePath(key, currentProject.projectRoot);
|
|
483
|
+
const taskStack = [
|
|
484
|
+
{
|
|
485
|
+
taskId: getFileTaskId(testPath),
|
|
486
|
+
taskType: 'file',
|
|
487
|
+
testPath
|
|
488
|
+
}
|
|
489
|
+
];
|
|
490
|
+
const taskContext = createBrowserTaskContext();
|
|
491
|
+
const shouldInterceptConsole = !runtimeConfig.disableConsoleIntercept || true === runtimeConfig.silent || 'passed-only' === runtimeConfig.silent;
|
|
492
|
+
const restoreConsole = shouldInterceptConsole ? interceptConsole(projectRuntime.name, ()=>taskContext.getCurrent() ?? taskStack[taskStack.length - 1], runtimeConfig.disableConsoleIntercept ? false : runtimeConfig.printConsoleTrace ?? false) : ()=>{};
|
|
493
|
+
const workerState = {
|
|
494
|
+
project: projectRuntime.name,
|
|
495
|
+
projectRoot: projectRuntime.projectRoot,
|
|
496
|
+
rootPath: options.rootPath,
|
|
497
|
+
runtimeConfig,
|
|
498
|
+
taskId: 0,
|
|
499
|
+
buildId: 0,
|
|
500
|
+
outputModule: false,
|
|
501
|
+
environment: 'browser',
|
|
502
|
+
currentTask: taskStack[0],
|
|
503
|
+
testPath,
|
|
504
|
+
distPath: testPath,
|
|
505
|
+
snapshotOptions: {
|
|
506
|
+
updateSnapshot: options.snapshot.updateSnapshot,
|
|
507
|
+
snapshotEnvironment: new BrowserSnapshotEnvironment(),
|
|
508
|
+
snapshotFormat: runtimeConfig.snapshotFormat
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
const syncCurrentTask = ()=>{
|
|
512
|
+
workerState.currentTask = taskStack[taskStack.length - 1];
|
|
513
|
+
};
|
|
514
|
+
const removeTaskFromStack = (taskId)=>{
|
|
515
|
+
const taskIndex = taskStack.findLastIndex((task)=>task.taskId === taskId);
|
|
516
|
+
if (taskIndex < 0) return;
|
|
517
|
+
taskStack.splice(taskIndex, 1);
|
|
518
|
+
syncCurrentTask();
|
|
519
|
+
};
|
|
520
|
+
const runtime = await createRstestRuntime(workerState, {
|
|
521
|
+
taskContext
|
|
522
|
+
});
|
|
523
|
+
installRuntimeGlobals(runtime, runtimeConfig);
|
|
524
|
+
let failedTestsCount = 0;
|
|
525
|
+
const dispatchFileCleanup = async (method, result, waitForAcknowledgement = true)=>{
|
|
526
|
+
const requestId = createRequestId(`file-cleanup-${method}`);
|
|
527
|
+
const request = {
|
|
528
|
+
requestId,
|
|
529
|
+
namespace: DISPATCH_NAMESPACE_FILE_CLEANUP,
|
|
530
|
+
method,
|
|
531
|
+
args: {
|
|
532
|
+
projectName: projectRuntime.name,
|
|
533
|
+
result,
|
|
534
|
+
runId: getRunIdentity(),
|
|
535
|
+
testPath
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
if (!waitForAcknowledgement) return void sendDispatchRequest(request);
|
|
539
|
+
await dispatchRpc({
|
|
540
|
+
requestId,
|
|
541
|
+
request,
|
|
542
|
+
timeoutMs: getRpcTimeout('framework'),
|
|
543
|
+
timeoutMessage: `File cleanup ${method} acknowledgement timed out for ${testPath}.`,
|
|
544
|
+
staleMessage: `File cleanup ${method} became stale for ${testPath}.`
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
const runnerHooks = {
|
|
548
|
+
onFileCleanupStart: async (result)=>{
|
|
549
|
+
if (result && globalThis.__coverage__) result.coverage = globalThis.__coverage__;
|
|
550
|
+
await dispatchFileCleanup('start', result, window.parent !== window);
|
|
551
|
+
},
|
|
552
|
+
onFileCleanupEnd: ()=>dispatchFileCleanup('end', void 0, window.parent !== window),
|
|
553
|
+
onSnapshotSetupStart: async ()=>{
|
|
554
|
+
setRpcPhase('framework');
|
|
555
|
+
},
|
|
556
|
+
onSnapshotSetupEnd: async ()=>{
|
|
557
|
+
setRpcPhase('test');
|
|
558
|
+
},
|
|
559
|
+
onSnapshotFinishStart: async ()=>{
|
|
560
|
+
setRpcPhase('framework');
|
|
561
|
+
},
|
|
562
|
+
onSnapshotFinishEnd: async ()=>{
|
|
563
|
+
setRpcPhase('test');
|
|
564
|
+
},
|
|
565
|
+
onTestFileReady: async (test)=>{
|
|
566
|
+
dispatchRunnerLifecycle('file-ready', test);
|
|
567
|
+
},
|
|
568
|
+
onTestSuiteStart: async (test)=>{
|
|
569
|
+
taskStack.push({
|
|
570
|
+
taskId: test.testId,
|
|
571
|
+
taskName: test.name,
|
|
572
|
+
taskParentNames: test.parentNames,
|
|
573
|
+
taskType: 'suite',
|
|
574
|
+
testPath: test.testPath
|
|
575
|
+
});
|
|
576
|
+
syncCurrentTask();
|
|
577
|
+
dispatchRunnerLifecycle('suite-start', test);
|
|
578
|
+
},
|
|
579
|
+
onTestSuiteResult: async (result)=>{
|
|
580
|
+
removeTaskFromStack(result.testId);
|
|
581
|
+
dispatchRunnerLifecycle('suite-result', result);
|
|
582
|
+
},
|
|
583
|
+
onTestCaseStart: async (test)=>{
|
|
584
|
+
taskStack.push({
|
|
585
|
+
taskId: test.testId,
|
|
586
|
+
taskName: test.name,
|
|
587
|
+
taskParentNames: test.parentNames,
|
|
588
|
+
taskType: 'case',
|
|
589
|
+
testPath: test.testPath
|
|
590
|
+
});
|
|
591
|
+
syncCurrentTask();
|
|
592
|
+
dispatchRunnerLifecycle('case-start', test);
|
|
593
|
+
},
|
|
594
|
+
onTestCaseResult: async (result)=>{
|
|
595
|
+
removeTaskFromStack(result.testId);
|
|
596
|
+
if ('fail' === result.status) failedTestsCount++;
|
|
597
|
+
send({
|
|
598
|
+
type: 'case-result',
|
|
599
|
+
payload: result
|
|
600
|
+
});
|
|
601
|
+
},
|
|
602
|
+
getCountOfFailedTests: async ()=>failedTestsCount
|
|
603
|
+
};
|
|
604
|
+
send({
|
|
605
|
+
type: 'file-start',
|
|
606
|
+
payload: {
|
|
607
|
+
testPath,
|
|
608
|
+
projectName: projectRuntime.name
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
const unhandledErrors = [];
|
|
612
|
+
activeUnhandledErrors = unhandledErrors;
|
|
613
|
+
try {
|
|
614
|
+
setRpcPhase('framework');
|
|
615
|
+
await loadSetupFiles();
|
|
616
|
+
const beforeScripts = getScriptUrls();
|
|
617
|
+
await currentTestContext.loadTest(key);
|
|
618
|
+
const afterScripts = getScriptUrls();
|
|
619
|
+
const chunkUrl = findNewScriptUrl(beforeScripts, afterScripts);
|
|
620
|
+
if (chunkUrl) await preloadTestFileSourceMap(chunkUrl);
|
|
621
|
+
const result = await runtime.runner.runTests(testPath, runnerHooks, runtime.api);
|
|
622
|
+
for(let i = 0; i < 2; i++)await new Promise((resolve)=>{
|
|
623
|
+
setTimeout(resolve, 0);
|
|
624
|
+
});
|
|
625
|
+
if (unhandledErrors.length > 0) {
|
|
626
|
+
result.status = 'fail';
|
|
627
|
+
result.errors = [
|
|
628
|
+
...result.errors ?? [],
|
|
629
|
+
...unhandledErrors.map((error)=>({
|
|
630
|
+
name: error.name,
|
|
631
|
+
message: error.message,
|
|
632
|
+
stack: error.stack
|
|
633
|
+
}))
|
|
634
|
+
];
|
|
635
|
+
}
|
|
636
|
+
send({
|
|
637
|
+
type: 'file-complete',
|
|
638
|
+
payload: result
|
|
639
|
+
});
|
|
640
|
+
} catch (_error) {
|
|
641
|
+
const error = _error instanceof Error ? _error : new Error(String(_error));
|
|
642
|
+
send({
|
|
643
|
+
type: 'fatal',
|
|
644
|
+
payload: {
|
|
645
|
+
message: error.message,
|
|
646
|
+
stack: error.stack
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
window.__RSTEST_DONE__ = true;
|
|
650
|
+
return;
|
|
651
|
+
} finally{
|
|
652
|
+
restoreConsole();
|
|
653
|
+
activeUnhandledErrors = void 0;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
window.removeEventListener('error', onWindowError);
|
|
657
|
+
window.removeEventListener('unhandledrejection', onUnhandledRejection);
|
|
658
|
+
send({
|
|
659
|
+
type: 'complete'
|
|
660
|
+
});
|
|
661
|
+
window.__RSTEST_DONE__ = true;
|
|
662
|
+
};
|
|
663
|
+
run().catch((error)=>{
|
|
664
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
665
|
+
send({
|
|
666
|
+
type: 'fatal',
|
|
667
|
+
payload: {
|
|
668
|
+
message: err.message,
|
|
669
|
+
stack: err.stack
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
window.__RSTEST_DONE__ = true;
|
|
673
|
+
}).finally(()=>{
|
|
674
|
+
disposeDispatchTransport();
|
|
675
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DecodedSourceMapXInput, type EncodedSourceMapXInput } from '@jridgewell/trace-mapping';
|
|
2
|
+
export type SourceMapPayload = EncodedSourceMapXInput | DecodedSourceMapXInput;
|
|
3
|
+
export type LoadedSourceMap = {
|
|
4
|
+
sourceMap: SourceMapPayload;
|
|
5
|
+
sourceMapUrl: string;
|
|
6
|
+
};
|
|
7
|
+
type Fetcher = typeof fetch;
|
|
8
|
+
export declare const normalizeJavaScriptUrl: (value: string, options?: {
|
|
9
|
+
origin?: string;
|
|
10
|
+
}) => string | null;
|
|
11
|
+
export declare const resolveInlineSourceMap: (code: string, sourceMapUrl?: string) => SourceMapPayload | null;
|
|
12
|
+
export declare const loadSourceMapForSource: ({ jsUrl, signal, source, fetcher }: {
|
|
13
|
+
jsUrl: string;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
source: string;
|
|
16
|
+
fetcher?: Fetcher;
|
|
17
|
+
}) => Promise<LoadedSourceMap | null>;
|
|
18
|
+
export declare const loadSourceMapWithCache: ({ jsUrl, cache, force, origin, fetcher }: {
|
|
19
|
+
jsUrl: string;
|
|
20
|
+
cache: Map<string, SourceMapPayload | null>;
|
|
21
|
+
force?: boolean;
|
|
22
|
+
origin?: string;
|
|
23
|
+
fetcher?: Fetcher;
|
|
24
|
+
}) => Promise<SourceMapPayload | null>;
|
|
25
|
+
export { };
|