@noego/wood 0.4.1 → 0.4.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/README.md +100 -2
- package/dist/client/global_polyfill.d.cts +1 -2
- package/dist/client/global_polyfill.d.ts +1 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +0 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/dist/testing/browser.cjs +253 -36
- package/dist/testing/browser.cjs.map +1 -1
- package/dist/testing/browser.d.cts +6 -2
- package/dist/testing/browser.d.ts +6 -2
- package/dist/testing/browser.js +253 -37
- package/dist/testing/browser.js.map +1 -1
- package/dist/testing/create_controller_harness.cjs +3 -1
- package/dist/testing/create_controller_harness.cjs.map +1 -1
- package/dist/testing/create_controller_harness.d.cts +4 -2
- package/dist/testing/create_controller_harness.d.ts +4 -2
- package/dist/testing/create_controller_harness.js +3 -1
- package/dist/testing/create_controller_harness.js.map +1 -1
- package/dist/testing/index.cjs +3 -3
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +5 -3
- package/dist/testing/index.d.ts +5 -3
- package/dist/testing/index.js +2 -2
- package/dist/testing/index.js.map +1 -1
- package/dist/trace-testing/index.cjs +3 -3
- package/dist/trace-testing/index.cjs.map +1 -1
- package/dist/trace-testing/index.d.cts +2 -3
- package/dist/trace-testing/index.d.ts +2 -3
- package/dist/trace-testing/index.js +3 -3
- package/dist/trace-testing/index.js.map +1 -1
- package/dist/trace-testing/trace_assertions.cjs +2 -46
- package/dist/trace-testing/trace_assertions.cjs.map +1 -1
- package/dist/trace-testing/trace_assertions.d.cts +1 -15
- package/dist/trace-testing/trace_assertions.d.ts +1 -15
- package/dist/trace-testing/trace_assertions.js +3 -45
- package/dist/trace-testing/trace_assertions.js.map +1 -1
- package/dist/trace-testing/trace_query.cjs +4 -115
- package/dist/trace-testing/trace_query.cjs.map +1 -1
- package/dist/trace-testing/trace_query.d.cts +1 -24
- package/dist/trace-testing/trace_query.d.ts +1 -24
- package/dist/trace-testing/trace_query.js +5 -112
- package/dist/trace-testing/trace_query.js.map +1 -1
- package/dist/trace-testing/trace_recorder.cjs +50 -0
- package/dist/trace-testing/trace_recorder.cjs.map +1 -0
- package/dist/trace-testing/trace_recorder.d.cts +12 -0
- package/dist/trace-testing/trace_recorder.d.ts +12 -0
- package/dist/trace-testing/trace_recorder.js +32 -0
- package/dist/trace-testing/trace_recorder.js.map +1 -0
- package/dist/tracing/index.cjs +0 -4
- package/dist/tracing/index.cjs.map +1 -1
- package/dist/tracing/index.d.cts +1 -1
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.js +1 -5
- package/dist/tracing/index.js.map +1 -1
- package/dist/tracing/trace_hub.cjs +0 -84
- package/dist/tracing/trace_hub.cjs.map +1 -1
- package/dist/tracing/trace_hub.d.cts +1 -16
- package/dist/tracing/trace_hub.d.ts +1 -16
- package/dist/tracing/trace_hub.js +0 -72
- package/dist/tracing/trace_hub.js.map +1 -1
- package/dist/tracing/trace_provider.cjs +0 -6
- package/dist/tracing/trace_provider.cjs.map +1 -1
- package/dist/tracing/trace_provider.d.cts +1 -3
- package/dist/tracing/trace_provider.d.ts +1 -3
- package/dist/tracing/trace_provider.js +0 -7
- package/dist/tracing/trace_provider.js.map +1 -1
- package/docs/browser-testing.md +84 -0
- package/package.json +2 -1
- package/dist/trace-testing/trace_scenario.cjs +0 -61
- package/dist/trace-testing/trace_scenario.cjs.map +0 -1
- package/dist/trace-testing/trace_scenario.d.cts +0 -27
- package/dist/trace-testing/trace_scenario.d.ts +0 -27
- package/dist/trace-testing/trace_scenario.js +0 -37
- package/dist/trace-testing/trace_scenario.js.map +0 -1
package/dist/testing/browser.js
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
5
6
|
import {
|
|
6
|
-
createTraceCapture,
|
|
7
7
|
dispatchRendererTracePayload
|
|
8
8
|
} from "../tracing/index.js";
|
|
9
|
+
import { TraceRecorder } from "../trace-testing/index.js";
|
|
9
10
|
const DEFAULT_VIEWPORT = {
|
|
10
11
|
width: 1280,
|
|
11
12
|
height: 800
|
|
12
13
|
};
|
|
14
|
+
const DEFAULT_EXPECT_TIMEOUT_MS = 1e4;
|
|
15
|
+
const DEFAULT_RESOURCE_CLOSE_TIMEOUT_MS = 5e3;
|
|
16
|
+
const WORKER_RUNTIME_IDLE_CLOSE_MS = 5e3;
|
|
17
|
+
const browserWorkerRuntimes = /* @__PURE__ */ new Map();
|
|
18
|
+
const browserWorkerCacheDirs = /* @__PURE__ */ new Set();
|
|
19
|
+
process.on("exit", () => {
|
|
20
|
+
for (const cacheDir of browserWorkerCacheDirs) {
|
|
21
|
+
try {
|
|
22
|
+
fs.rmSync(cacheDir, { recursive: true, force: true });
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
browserWorkerCacheDirs.clear();
|
|
27
|
+
browserWorkerRuntimes.clear();
|
|
28
|
+
});
|
|
13
29
|
function normalizeRoot(rootDir) {
|
|
14
30
|
return path.resolve(rootDir ?? process.cwd());
|
|
15
31
|
}
|
|
@@ -32,6 +48,18 @@ function mergeBridge(base, override) {
|
|
|
32
48
|
record: override?.record ?? base?.record
|
|
33
49
|
};
|
|
34
50
|
}
|
|
51
|
+
async function waitForBoundedClose(promise, timeoutMs = DEFAULT_RESOURCE_CLOSE_TIMEOUT_MS) {
|
|
52
|
+
let timeout;
|
|
53
|
+
await Promise.race([
|
|
54
|
+
promise.catch(() => void 0),
|
|
55
|
+
new Promise((resolve) => {
|
|
56
|
+
timeout = setTimeout(resolve, timeoutMs);
|
|
57
|
+
})
|
|
58
|
+
]);
|
|
59
|
+
if (timeout) {
|
|
60
|
+
clearTimeout(timeout);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
35
63
|
function channelToManifestEntry(channel, handler) {
|
|
36
64
|
const pathSegments = channel.split(".").filter(Boolean);
|
|
37
65
|
const action = pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : channel;
|
|
@@ -133,6 +161,51 @@ async function loadPlaywright(browserName) {
|
|
|
133
161
|
async function loadVite() {
|
|
134
162
|
return await import("vite");
|
|
135
163
|
}
|
|
164
|
+
function createPackageRequire(rootDir) {
|
|
165
|
+
return createRequire(path.join(rootDir, "package.json"));
|
|
166
|
+
}
|
|
167
|
+
function resolvePackageModule(rootDir, source) {
|
|
168
|
+
const rootRequire = createPackageRequire(rootDir);
|
|
169
|
+
const processRequire = createRequire(path.join(process.cwd(), "package.json"));
|
|
170
|
+
try {
|
|
171
|
+
return rootRequire.resolve(source);
|
|
172
|
+
} catch {
|
|
173
|
+
try {
|
|
174
|
+
return processRequire.resolve(source);
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function resolvePackageDir(rootDir, packageName) {
|
|
181
|
+
const packageJson = resolvePackageModule(rootDir, `${packageName}/package.json`);
|
|
182
|
+
return packageJson ? path.dirname(packageJson) : null;
|
|
183
|
+
}
|
|
184
|
+
function createWoodBrowserResolver(rootDir) {
|
|
185
|
+
const sveltePackageDir = resolvePackageDir(rootDir, "svelte");
|
|
186
|
+
const svelteClientEntry = sveltePackageDir ? path.join(sveltePackageDir, "src", "index-client.js") : null;
|
|
187
|
+
const svelteLegacyClientEntry = sveltePackageDir ? path.join(sveltePackageDir, "src", "legacy", "legacy-client.js") : null;
|
|
188
|
+
const svelteReactivityClientEntry = sveltePackageDir ? path.join(sveltePackageDir, "src", "reactivity", "index-client.js") : null;
|
|
189
|
+
return {
|
|
190
|
+
name: "wood-browser-resolver",
|
|
191
|
+
enforce: "pre",
|
|
192
|
+
resolveId(source) {
|
|
193
|
+
if (source === "svelte") {
|
|
194
|
+
return svelteClientEntry;
|
|
195
|
+
}
|
|
196
|
+
if (source === "svelte/legacy") {
|
|
197
|
+
return svelteLegacyClientEntry;
|
|
198
|
+
}
|
|
199
|
+
if (source === "svelte/reactivity") {
|
|
200
|
+
return svelteReactivityClientEntry;
|
|
201
|
+
}
|
|
202
|
+
if (source.startsWith("svelte/")) {
|
|
203
|
+
return resolvePackageModule(rootDir, source);
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
136
209
|
async function loadSveltePlugin(rootDir) {
|
|
137
210
|
try {
|
|
138
211
|
const rootPluginPath = path.join(
|
|
@@ -163,22 +236,115 @@ async function loadViews(viewsPath) {
|
|
|
163
236
|
const parser = await import("../parser/views_parser.js");
|
|
164
237
|
return parser.parseViewsFile(viewsPath);
|
|
165
238
|
}
|
|
166
|
-
async function createViteServer(rootDir) {
|
|
239
|
+
async function createViteServer(rootDir, cacheDir) {
|
|
167
240
|
const vite = await loadVite();
|
|
168
241
|
const svelte = await loadSveltePlugin(rootDir);
|
|
169
|
-
const plugins =
|
|
242
|
+
const plugins = [
|
|
243
|
+
createWoodBrowserResolver(rootDir),
|
|
244
|
+
...svelte ? [svelte()] : []
|
|
245
|
+
];
|
|
170
246
|
const server = await vite.createServer({
|
|
171
247
|
root: rootDir,
|
|
248
|
+
cacheDir,
|
|
172
249
|
logLevel: process.env.WOOD_BROWSER_TEST_DEBUG ? "info" : "silent",
|
|
173
250
|
plugins,
|
|
174
251
|
resolve: {
|
|
175
252
|
dedupe: ["svelte"]
|
|
176
253
|
},
|
|
177
|
-
server: { port: 0 }
|
|
254
|
+
server: { port: 0, fs: { allow: [rootDir, os.tmpdir()] } }
|
|
178
255
|
});
|
|
179
256
|
await server.listen();
|
|
180
257
|
return server;
|
|
181
258
|
}
|
|
259
|
+
function workerRuntimeKey(input) {
|
|
260
|
+
return [
|
|
261
|
+
input.rootDir,
|
|
262
|
+
input.browserName,
|
|
263
|
+
input.headless ? "headless" : "headed"
|
|
264
|
+
].join("::");
|
|
265
|
+
}
|
|
266
|
+
async function createBrowserWorkerRuntime(input) {
|
|
267
|
+
const cacheRoot = fs.mkdtempSync(path.join(os.tmpdir(), "wood-browser-vite-"));
|
|
268
|
+
browserWorkerCacheDirs.add(cacheRoot);
|
|
269
|
+
let browser2 = null;
|
|
270
|
+
try {
|
|
271
|
+
const playwrightBrowser = await loadPlaywright(input.browserName);
|
|
272
|
+
browser2 = await playwrightBrowser.launch({ headless: input.headless });
|
|
273
|
+
const server = await createViteServer(input.rootDir, path.join(cacheRoot, ".vite"));
|
|
274
|
+
return {
|
|
275
|
+
browser: browser2,
|
|
276
|
+
server,
|
|
277
|
+
cacheDir: cacheRoot,
|
|
278
|
+
rootDir: input.rootDir,
|
|
279
|
+
key: input.key,
|
|
280
|
+
activeHarnesses: 0,
|
|
281
|
+
closing: false,
|
|
282
|
+
idleCloseTimer: null
|
|
283
|
+
};
|
|
284
|
+
} catch (error) {
|
|
285
|
+
await browser2?.close().catch(() => void 0);
|
|
286
|
+
fs.rmSync(cacheRoot, { recursive: true, force: true });
|
|
287
|
+
browserWorkerCacheDirs.delete(cacheRoot);
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function acquireBrowserWorkerRuntime(input) {
|
|
292
|
+
const key = workerRuntimeKey(input);
|
|
293
|
+
let entry = browserWorkerRuntimes.get(key);
|
|
294
|
+
if (!entry) {
|
|
295
|
+
entry = {
|
|
296
|
+
promise: createBrowserWorkerRuntime({ ...input, key })
|
|
297
|
+
};
|
|
298
|
+
browserWorkerRuntimes.set(key, entry);
|
|
299
|
+
}
|
|
300
|
+
const runtime = await entry.promise;
|
|
301
|
+
if (runtime.closing) {
|
|
302
|
+
browserWorkerRuntimes.delete(key);
|
|
303
|
+
return await acquireBrowserWorkerRuntime(input);
|
|
304
|
+
}
|
|
305
|
+
if (runtime.idleCloseTimer) {
|
|
306
|
+
clearTimeout(runtime.idleCloseTimer);
|
|
307
|
+
runtime.idleCloseTimer = null;
|
|
308
|
+
}
|
|
309
|
+
runtime.activeHarnesses += 1;
|
|
310
|
+
return runtime;
|
|
311
|
+
}
|
|
312
|
+
async function closeBrowserWorkerRuntime(runtime) {
|
|
313
|
+
if (runtime.closing) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
runtime.closing = true;
|
|
317
|
+
try {
|
|
318
|
+
await Promise.all([
|
|
319
|
+
waitForBoundedClose(runtime.server.close()),
|
|
320
|
+
waitForBoundedClose(runtime.browser.close())
|
|
321
|
+
]);
|
|
322
|
+
} finally {
|
|
323
|
+
fs.rmSync(runtime.cacheDir, { recursive: true, force: true });
|
|
324
|
+
browserWorkerCacheDirs.delete(runtime.cacheDir);
|
|
325
|
+
const entry = browserWorkerRuntimes.get(runtime.key);
|
|
326
|
+
const current = entry ? await entry.promise.catch(() => null) : null;
|
|
327
|
+
if (current === runtime) {
|
|
328
|
+
browserWorkerRuntimes.delete(runtime.key);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function scheduleBrowserWorkerRuntimeClose(runtime) {
|
|
333
|
+
if (runtime.closing || runtime.activeHarnesses > 0 || runtime.idleCloseTimer) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
runtime.idleCloseTimer = setTimeout(() => {
|
|
337
|
+
runtime.idleCloseTimer = null;
|
|
338
|
+
if (runtime.activeHarnesses === 0) {
|
|
339
|
+
void closeBrowserWorkerRuntime(runtime);
|
|
340
|
+
}
|
|
341
|
+
}, WORKER_RUNTIME_IDLE_CLOSE_MS);
|
|
342
|
+
runtime.idleCloseTimer.unref?.();
|
|
343
|
+
}
|
|
344
|
+
function releaseBrowserWorkerRuntime(runtime) {
|
|
345
|
+
runtime.activeHarnesses = Math.max(0, runtime.activeHarnesses - 1);
|
|
346
|
+
scheduleBrowserWorkerRuntimeClose(runtime);
|
|
347
|
+
}
|
|
182
348
|
function serverBaseUrl(server) {
|
|
183
349
|
return server.resolvedUrls?.local?.[0] ?? "http://127.0.0.1:5173";
|
|
184
350
|
}
|
|
@@ -385,8 +551,8 @@ async function installBridge(page, bridgeState, manifest, bridgeInit) {
|
|
|
385
551
|
window.__WOOD_BROWSER_BRIDGE_MANIFEST__ = entries;
|
|
386
552
|
}, manifest);
|
|
387
553
|
}
|
|
388
|
-
async function installTraceBinding(page,
|
|
389
|
-
if (!
|
|
554
|
+
async function installTraceBinding(page, traceRecorder, context) {
|
|
555
|
+
if (!traceRecorder) {
|
|
390
556
|
return;
|
|
391
557
|
}
|
|
392
558
|
await page.exposeBinding("__woodBrowserTrace", async (_source, payload) => {
|
|
@@ -469,20 +635,35 @@ async function elementRect(page, selector) {
|
|
|
469
635
|
}).catch(() => null);
|
|
470
636
|
}
|
|
471
637
|
function createExpect(page, outputDir) {
|
|
638
|
+
async function waitForVisible(selector) {
|
|
639
|
+
const locator = page.locator(selector).first();
|
|
640
|
+
try {
|
|
641
|
+
await locator.waitFor({ state: "attached", timeout: DEFAULT_EXPECT_TIMEOUT_MS });
|
|
642
|
+
} catch {
|
|
643
|
+
throw new Error(`Expected "${selector}" to exist.`);
|
|
644
|
+
}
|
|
645
|
+
try {
|
|
646
|
+
await locator.waitFor({ state: "visible", timeout: DEFAULT_EXPECT_TIMEOUT_MS });
|
|
647
|
+
} catch {
|
|
648
|
+
throw new Error(`Expected "${selector}" to be visible.`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
472
651
|
return {
|
|
473
652
|
async visible(selector) {
|
|
474
|
-
|
|
475
|
-
if (count < 1) throw new Error(`Expected "${selector}" to exist.`);
|
|
476
|
-
const visible = await page.locator(selector).first().isVisible();
|
|
477
|
-
if (!visible) throw new Error(`Expected "${selector}" to be visible.`);
|
|
653
|
+
await waitForVisible(selector);
|
|
478
654
|
},
|
|
479
655
|
async hidden(selector) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
656
|
+
try {
|
|
657
|
+
await page.locator(selector).first().waitFor({
|
|
658
|
+
state: "hidden",
|
|
659
|
+
timeout: DEFAULT_EXPECT_TIMEOUT_MS
|
|
660
|
+
});
|
|
661
|
+
} catch {
|
|
662
|
+
throw new Error(`Expected "${selector}" to be hidden.`);
|
|
663
|
+
}
|
|
484
664
|
},
|
|
485
665
|
async insideViewport(selector, options = {}) {
|
|
666
|
+
await waitForVisible(selector);
|
|
486
667
|
const margin = options.margin ?? 0;
|
|
487
668
|
const rect = await elementRect(page, selector);
|
|
488
669
|
if (!rect) throw new Error(`Expected "${selector}" to exist.`);
|
|
@@ -493,6 +674,7 @@ function createExpect(page, outputDir) {
|
|
|
493
674
|
}
|
|
494
675
|
},
|
|
495
676
|
async notClipped(selector) {
|
|
677
|
+
await waitForVisible(selector);
|
|
496
678
|
const result = await page.locator(selector).first().evaluate((element) => {
|
|
497
679
|
const rect = element.getBoundingClientRect();
|
|
498
680
|
let parent = element.parentElement;
|
|
@@ -514,6 +696,8 @@ function createExpect(page, outputDir) {
|
|
|
514
696
|
}
|
|
515
697
|
},
|
|
516
698
|
async notOverlapping(firstSelector, secondSelector) {
|
|
699
|
+
await waitForVisible(firstSelector);
|
|
700
|
+
await waitForVisible(secondSelector);
|
|
517
701
|
const first = await elementRect(page, firstSelector);
|
|
518
702
|
const second = await elementRect(page, secondSelector);
|
|
519
703
|
if (!first || !second) throw new Error(`Expected both "${firstSelector}" and "${secondSelector}" to exist.`);
|
|
@@ -523,6 +707,8 @@ function createExpect(page, outputDir) {
|
|
|
523
707
|
}
|
|
524
708
|
},
|
|
525
709
|
async zIndexAbove(topSelector, lowerSelector) {
|
|
710
|
+
await waitForVisible(topSelector);
|
|
711
|
+
await waitForVisible(lowerSelector);
|
|
526
712
|
const values = await page.evaluate((payload) => {
|
|
527
713
|
const { topSelector: top, lowerSelector: lower } = payload;
|
|
528
714
|
const topElement = document.querySelector(top);
|
|
@@ -596,7 +782,7 @@ async function openMountPage(input) {
|
|
|
596
782
|
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
597
783
|
const entryPath = writeEntry(input.runtime.tempDir, input.name, input.entrySource);
|
|
598
784
|
const htmlPath = writeHtml(input.runtime.tempDir, input.name, entryPath, input.css);
|
|
599
|
-
await installTraceBinding(input.runtime.page, input.
|
|
785
|
+
await installTraceBinding(input.runtime.page, input.traceRecorder, input.traceContext ?? {});
|
|
600
786
|
await installBridge(page, input.bridge.state, input.bridge.manifest, input.bridge.bridgeInit);
|
|
601
787
|
const mountedBridge = createMountedBridge(page, input.bridge.state);
|
|
602
788
|
const context = {
|
|
@@ -629,23 +815,47 @@ async function openMountPage(input) {
|
|
|
629
815
|
async function createHarness(config = {}) {
|
|
630
816
|
const rootDir = normalizeRoot(config.rootDir);
|
|
631
817
|
const browserName = config.browser?.browserName ?? "chromium";
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
|
|
818
|
+
const headless = config.browser?.headless ?? true;
|
|
819
|
+
const workerRuntime = await acquireBrowserWorkerRuntime({
|
|
820
|
+
rootDir,
|
|
821
|
+
browserName,
|
|
822
|
+
headless
|
|
635
823
|
});
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
824
|
+
let released = false;
|
|
825
|
+
let destroyed = false;
|
|
826
|
+
let tempDir = null;
|
|
827
|
+
let context = null;
|
|
828
|
+
let page = null;
|
|
829
|
+
let traceRecorder;
|
|
830
|
+
function releaseRuntimeOnce() {
|
|
831
|
+
if (released) {
|
|
832
|
+
return;
|
|
642
833
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
834
|
+
released = true;
|
|
835
|
+
releaseBrowserWorkerRuntime(workerRuntime);
|
|
836
|
+
}
|
|
837
|
+
try {
|
|
838
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "wood-browser-harness-"));
|
|
839
|
+
context = await workerRuntime.browser.newContext({
|
|
840
|
+
viewport: {
|
|
841
|
+
width: config.viewport?.width ?? DEFAULT_VIEWPORT.width,
|
|
842
|
+
height: config.viewport?.height ?? DEFAULT_VIEWPORT.height,
|
|
843
|
+
deviceScaleFactor: config.viewport?.deviceScaleFactor
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
page = await context.newPage();
|
|
847
|
+
traceRecorder = config.trace?.enabled === true ? new TraceRecorder() : void 0;
|
|
848
|
+
traceRecorder?.enable({ maxEvents: config.trace?.maxEvents });
|
|
849
|
+
} catch (error) {
|
|
850
|
+
if (page) await waitForBoundedClose(page.close());
|
|
851
|
+
if (context) await waitForBoundedClose(context.close());
|
|
852
|
+
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
|
|
853
|
+
releaseRuntimeOnce();
|
|
854
|
+
throw error;
|
|
855
|
+
}
|
|
646
856
|
const runtime = {
|
|
647
|
-
|
|
648
|
-
server,
|
|
857
|
+
context,
|
|
858
|
+
server: workerRuntime.server,
|
|
649
859
|
page,
|
|
650
860
|
tempDir,
|
|
651
861
|
rootDir
|
|
@@ -657,7 +867,7 @@ async function createHarness(config = {}) {
|
|
|
657
867
|
});
|
|
658
868
|
return {
|
|
659
869
|
page,
|
|
660
|
-
...
|
|
870
|
+
...traceRecorder ? { traceRecorder } : {},
|
|
661
871
|
async mountRoute(routeKey, options = {}) {
|
|
662
872
|
const bridge = createBrowserBridgeFixtureWithTrace(mergeBridge(config.bridge, options.bridge), config.trace);
|
|
663
873
|
const route = {
|
|
@@ -690,7 +900,7 @@ async function createHarness(config = {}) {
|
|
|
690
900
|
css,
|
|
691
901
|
viewport: options.viewport ?? config.viewport,
|
|
692
902
|
bridge,
|
|
693
|
-
|
|
903
|
+
traceRecorder,
|
|
694
904
|
traceContext: {
|
|
695
905
|
windowId: config.trace?.windowId ?? routeKey.split(".")[0],
|
|
696
906
|
routeKey,
|
|
@@ -727,7 +937,7 @@ async function createHarness(config = {}) {
|
|
|
727
937
|
css,
|
|
728
938
|
viewport: options.viewport ?? config.viewport,
|
|
729
939
|
bridge,
|
|
730
|
-
|
|
940
|
+
traceRecorder,
|
|
731
941
|
traceContext: {
|
|
732
942
|
windowId: config.trace?.windowId ?? "wood-browser-harness",
|
|
733
943
|
harnessId: config.trace?.harnessId
|
|
@@ -760,7 +970,7 @@ async function createHarness(config = {}) {
|
|
|
760
970
|
css,
|
|
761
971
|
viewport: tree.viewport ?? config.viewport,
|
|
762
972
|
bridge,
|
|
763
|
-
|
|
973
|
+
traceRecorder,
|
|
764
974
|
traceContext: {
|
|
765
975
|
windowId: config.trace?.windowId ?? tree.page?.key?.split(".")[0] ?? "wood-browser-harness",
|
|
766
976
|
routeKey: tree.page?.key,
|
|
@@ -777,11 +987,17 @@ async function createHarness(config = {}) {
|
|
|
777
987
|
});
|
|
778
988
|
},
|
|
779
989
|
async destroy() {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
990
|
+
if (destroyed) {
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
destroyed = true;
|
|
994
|
+
await Promise.all([
|
|
995
|
+
waitForBoundedClose(page.close()),
|
|
996
|
+
waitForBoundedClose(context.close())
|
|
997
|
+
]);
|
|
998
|
+
traceRecorder?.disable();
|
|
784
999
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
1000
|
+
releaseRuntimeOnce();
|
|
785
1001
|
}
|
|
786
1002
|
};
|
|
787
1003
|
}
|