Playwright Worker 类详解:监控、求值与操作 Web Worker 的完整 API 指南

Playwright Worker 类详解:监控、求值与操作 Web Worker 的完整 API 指南 Playwright Worker 类详解监控、求值与操作 Web Worker 的完整 API 指南【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright本文以 Playwright 官方 API 文档中的Worker类docs/src/api/class-worker.md为主线完整讲解如何监听页面创建的 Web Worker、读取其 URL、在 Worker 上下文中执行 JS 表达式、等待其close/console事件并结合仓库中的客户端/服务端源码与测试用例说明Worker事件的分发链路、超时机制的取值来源以及 Worker 网络活动归属等关键行为帮助读者把 Worker 相关的自动化能力真正落地到测试脚本中。Worker 类是什么Worker类since v1.8代表一个 Web Worker。在 Playwright 中的事件模型是当页面派生出一个 dedicated worker 时worker事件会在Page 对象上发出对应 Page.worker 事件当 Worker 终止时close事件会在Worker 对象自身上发出。也就是说创建信号在 Page 上销毁信号在 Worker 上二者配合即可完整追踪一个 Worker 的生命周期。四种语言的完整用法继承自官方文档示例page.on(worker, worker { console.log(Worker created: worker.url()); worker.on(close, worker console.log(Worker destroyed: worker.url())); }); console.log(Current workers:); for (const worker of page.workers()) console.log( worker.url());page.onWorker(worker - { System.out.println(Worker created: worker.url()); worker.onClose(worker1 - System.out.println(Worker destroyed: worker1.url())); }); System.out.println(Current workers:); for (Worker worker : page.workers()) System.out.println( worker.url());def handle_worker(worker): print(worker created: worker.url) worker.on(close, lambda: print(worker destroyed: worker.url)) page.on(worker, handle_worker) print(current workers:) for worker in page.workers: print( worker.url)page.Worker (_, worker) { Console.WriteLine($Worker created: {worker.Url}); worker.Close (_, _) Console.WriteLine($Worker closed {worker.Url}); }; Console.WriteLine(Current Workers:); foreach(var pageWorker in page.Workers) { Console.WriteLine($\tWorker: {pageWorker.Url}); }API 总览成员类型语言支持起始版本说明worker.closeevent全部v1.8该 dedicated Web Worker 被终止时发出worker.consoleevent全部v1.57Worker 内 JS 调用console.log/console.dir等 API 时发出worker.url()method全部v1.8返回 Worker 的 URLworker.evaluateasync method全部v1.8在 Worker 上下文求值表达式/函数返回可序列化值worker.evaluateHandleasync method全部v1.8同上但返回 JSHandleworker.waitForEventasync methodJS、PythonPython 别名expect_eventv1.57等待指定事件并返回事件数据worker.waitForCloseasync methodJavav1.10执行动作并等待 Worker 关闭worker.waitForConsoleMessageasync methodJavav1.57执行动作并等待一条 console 消息url() 与 page.workers()worker.url()since v1.8直接返回创建该 Worker 时的 URL。注意用new Worker(URL.createObjectURL(new Blob(...)))内联创建的 Worker其 URL 是一个blob:地址这一点在测试断言时要留意仓库测试 tests/page/workers.spec.ts 中就用expect(page.url()).not.toContain(blob)防止把 Worker 的 blob URL 误当成页面 URL。page.workers()返回当前存活的全部 Worker。从客户端源码 packages/playwright-core/src/client/page.ts 可以看到Page内部维护一个_workers集合_onWorker在收到新 Worker 时将其加入集合并触发worker事件而 Worker 对象在close时会将自身从该集合移除packages/playwright-core/src/client/worker.ts因此page.workers()始终是当前存活视图。这一点被测试should clear upon navigation验证页面导航后 Worker 全部销毁page.workers().length变为 0。evaluate 与 evaluateHandle在 Worker 上下文中执行 JSworker.evaluatesince v1.8返回表达式的求值结果参数说明对应 docs/src/api/params.md 中的参数片段expression要在 Worker 上下文求值的 JavaScript 表达式若表达式求值结果是一个函数该函数会被自动调用。JS 版本也可以直接传函数pageFunction为 function 或 stringarg可选传给expression的参数可为EvaluationArgument类型可序列化的值或 JSHandle。语义要点若传入的函数返回 Promiseevaluate会等待 Promise resolve 后返回其值若返回值不可序列化非Serializableevaluate返回undefinedPlaywright 额外支持转移JSON无法序列化的少数值-0、NaN、Infinity、-Infinity。worker.evaluateHandlesince v1.8与evaluate的唯一区别是返回 JSHandle 而非序列化值参数与 Promise 等待语义相同。测试用例给出了典型用法tests/page/workers.spec.tsit(Page.workers smoke, async function({ page, server, browserName, browserMajorVersion }) { await Promise.all([ page.waitForEvent(worker), page.goto(server.PREFIX /worker/worker.html)]); const worker page.workers()[0]; expect(worker.url()).toContain(worker.js); expect(await worker.evaluate(() self[workerFunction]())).toBe(worker function result); await page.goto(server.EMPTY_PAGE); expect(page.workers().length).toBe(0); });源码视角求值如何落到 Worker 执行上下文客户端Worker.evaluate把表达式打包成 channel 请求发给服务端packages/playwright-core/src/client/worker.tsasync evaluateR, Arg(pageFunction: structs.PageFunctionArg, R, arg?: Arg): PromiseR { assertMaxArguments(arguments.length, 2); const result await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction function, arg: serializeArgument(arg) }, kNoTimeout); return parseResult(result.value); }服务端的Worker类packages/playwright-core/src/server/page.ts为每个 Worker 维护一个ExecutionContextcreateExecutionContext创建kind 为worker。evaluateExpression与evaluateExpressionHandle都等待_executionContextPromise解析后调用js.evaluateExpression完成求值前者returnByValue: true后者returnByValue: falseasync evaluateExpression(progress: Progress, expression: string, isFunction: boolean | undefined, arg: any): Promiseany { return progress.race(js.evaluateExpression(await this._executionContextPromise, expression, { returnByValue: true, isFunction }, arg)); }值得注意的是workerScriptLoaded()与destroyExecutionContext的配合当 Worker 的脚本尚未加载完成执行上下文处于未就绪状态_executionContextPromise保持 pending脚本加载完成或上下文重建后才会 resolve。从测试中多处it.skip(browserName chromium browserMajorVersion 143, needs workerScriptLoaded event)可以推断较新版本的 Chromium 引入了workerScriptLoaded事件来保证Worker 已创建但脚本还没执行完时对worker.evaluate的调用能够安全排队而不是立即失败。事件close 与 consoleevent: Worker.closesince v1.8当该 dedicated Web Worker 被终止时发出参数为Worker本身。客户端构造函数中可以看到服务端 channel 的close消息会先清理归属关系web worker 从page._workers移除、service worker 从browserContext._serviceWorkers移除再对外发出close事件并关闭内部LongStandingScopepackages/playwright-core/src/client/worker.ts。event: Worker.consolesince v1.57当 Worker 内的 JS 调用consoleAPI如console.log、console.dir时发出参数为ConsoleMessage。从测试可以确认一个重要行为同一条 Worker console 消息会同时出现在 Worker、Page、BrowserContext 三个对象上且是同一个消息对象tests/page/workers.spec.tsconst [message1, message2, message3] await Promise.all([ worker.waitForEvent(console), page.waitForEvent(console), page.context().waitForEvent(console), worker.evaluate(() { console.log(hello from worker); }), ]); expect(message1.text()).toBe(hello from worker); expect(message1).toBe(message2); expect(message1).toBe(message3);同时测试should not report console logs from workers twice验证了消息不会被重复上报。另外Worker 内throw抛出的未捕获错误会以pageerror事件的形式上报到 Page见should report errors测试。从服务端分发链看WorkerDispatcher 监听Worker.Events.Console只有当客户端订阅了console事件this._subscriptions.has(console)时才把消息序列化为console事件下发并携带type、text、argsJSHandle 形式与location、timestamp字段。客户端侧则在构造Worker时注册了console事件的订阅映射packages/playwright-core/src/client/worker.tsthis._setEventToSubscriptionMapping(new Mapstring, channels.WorkerUpdateSubscriptionParams[event]([ [Events.Worker.Console, console], ]));源码注释还说明通过chromium._connectToWorkerservice worker 场景接入的 Worker其 console 事件只在 Worker 对象上收到。waitForEvent等待 Worker 事件JS / Pythonworker.waitForEventsince v1.57langs: js, pythonPython 别名为expect_event等待事件触发并把事件数据传给谓词函数当谓词返回真值时返回事件数据若在事件触发前页面关闭会抛出错误。参数与选项参数语义见 docs/src/api/params.mdevent事件名与传给worker.on(event)的名称相同对 Worker 即console/closeoptionsOrPredicateJS 可选谓词函数或选项对象predicate接收事件数据返回真值时结束等待timeout最长等待毫秒数。JS 版本默认0不超时默认值可通过配置中的actionTimeout选项或BrowserContext.setDefaultTimeout/Page.setDefaultTimeout方法修改signalJSsince v1.62AbortSignal可用于取消等待。提供signal不会禁用默认超时要彻底关闭超时需传timeout: 0。C#/Java/Python 侧对应的timeout默认值为3000030 秒传0可禁用。官方文档示例// Start waiting for download before clicking. Note no await. const consolePromise worker.waitForEvent(console); await worker.evaluate(console.log(42)); const consoleMessage await consolePromise;async with worker.expect_event(console) as event_info: await worker.evaluate(console.log(42)) message await event_info.valuewith worker.expect_event(console) as event_info: worker.evaluate(console.log(42)) message event_info.value注意这个模式的关键点先启动等待、再触发行为否则会漏掉在await之前就已发出的事件。源码视角waitForEvent 的超时与取消客户端实现packages/playwright-core/src/client/worker.ts展示了三个值得了解的细节async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions {}): Promiseany { return await this._wrapApiCall(async () { const timeoutSettings this._page?._timeoutSettings ?? this._context?._timeoutSettings ?? new TimeoutSettings(); const timeoutOptions timeoutSettings.timeout(typeof optionsOrPredicate function ? {} : optionsOrPredicate); const predicate typeof optionsOrPredicate function ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter Waiter.createForEvent(this, event); waiter.rejectOnTimeout(timeoutOptions, Timeout ${timeoutOptions.timeout}ms exceeded while waiting for event ${event}); if (event ! Events.Worker.Close) waiter.rejectOnEvent(this, Events.Worker.Close, () this._closeErrorWithReason()); const result await waiter.waitForEvent(this, event, predicate as any); waiter.dispose(); return result; }); }超时来源的继承链Worker 自身不持有超时设置而是依次取所属Page的_timeoutSettings→ 所属BrowserContext的_timeoutSettings→ 全局默认。这解释了文档中默认值可通过setDefaultTimeout修改的机制Worker 关闭即失败只要等待的不是close事件本身一旦 Worker 关闭等待会以带关闭原因的TargetClosedError立即失败_closeErrorWithReason不会傻等到超时等待过程由Waiter封装先挂超时拒绝、再挂 close 拒绝、最后按谓词匹配事件。waitForClose 与 waitForConsoleMessageJavaJava 侧采用执行动作 等待事件的组合式 API文档中的两个方法都是这个模式worker.waitForClosesince v1.10langs: java执行传入的callback触发关闭的动作并等待 Worker 关闭返回Worker。选项timeoutsince v1.9最长等待毫秒数默认 30000传 0 禁用可用setDefaultTimeout修改signalsince v1.9java.util.concurrent的Abortable取消信号callbacksince v1.9Runnable执行触发事件的动作。worker.waitForConsoleMessagesince v1.57langs: java执行callback并等待一条 console 消息返回ConsoleMessage。选项predicatesince v1.57function(ConsoleMessage): boolean接收消息对象返回true时结束等待timeoutsince v1.57默认 30000传 0 禁用signalsince v1.57取消信号callbacksince v1.57Runnable。JS/Python 用户请用waitForEvent(close)/waitForEvent(console)Python 的expect_event上下文管理器实现等价逻辑。Worker 的网络行为从测试用例看归属与拦截Worker类虽然只暴露 URL、求值与事件几个方法但 Worker 发起的网络活动会完整接入 Page 的网络事件体系这在 tests/page/workers.spec.ts 中有系统性验证Worker 脚本本身是网络请求new Worker(/worker/worker.js)拉取脚本的request/requestfinished事件会被上报且能拿到完整的请求/响应头should report worker script as network request、should resolve worker script allHeaders in main frame/iframe等用例Worker 内的fetch归属到创建它的页面/iframeiframe 内 Worker 发起的请求request.frame()指向该 iframeshould attribute network activity for worker inside iframe to the iframeWorker 流量可以被page.route拦截should report and intercept network from nested worker用例里顶层 Worker 和Worker 里再 new 出来的嵌套 Worker发出的请求都被同一route.fulfill改写两条 console 日志都输出被改写后的{foo:not bar}——这说明 Playwright 会递归地识别嵌套 Worker额外 HTTP 头与离线模式生效于 Workerpage.setExtraHTTPHeaders设置的头会出现在 Worker 脚本请求与 Worker 内fetch请求上browserContext.setOffline(true)后worker.evaluate(() navigator.onLine)变为false。这些测试意味着为页面配置路由/断言时默认就覆盖了其派生的 Worker 流量无需在 Worker 对象上单独挂网络钩子。生命周期细节导航清理与销毁事件测试should emit created and destroyed events展示了一个完整的内联 Worker 生命周期页面evaluate创建 Worker →page发出worker事件 → 调用workerObj.terminate()→ Worker 对象发出close事件且close回调参数正是同一个 Worker 对象。Worker 销毁后原来持有的JSHandle会失效getProperty抛出包含目标已关闭信息kTargetClosedErrorMessage的错误——对 Worker 内对象持有引用做断言时需要注意这一点。should clear upon navigation/should clear upon cross-process navigation两个用例则验证了同进程与跨进程导航都会销毁旧文档的 Workerclose事件触发、page.workers()清空。小结与相关源码索引围绕 Worker API 文档本文覆盖的要点事件模型workerPage 上与close/consoleWorker 上的分工四种语言的完整示例求值能力evaluate/evaluateHandle的参数语义、Promise 等待、可序列化值限制与特殊值支持等待能力waitForEventJS/Python、waitForClose/waitForConsoleMessageJava的谓词、timeout、signal、callback参数与默认值差异JS 默认0不超时C#/Java/Python 默认 30000ms源码链路客户端 packages/playwright-core/src/client/worker.ts事件订阅、close 清理、waitForEvent 超时继承→ 服务端 packages/playwright-core/src/server/page.tsWorker 执行上下文与求值→ 分发器 packages/playwright-core/src/server/dispatchers/pageDispatcher.ts基于订阅的 console 事件下发行为验证tests/page/workers.spec.ts 中对事件、console 去重、嵌套 Worker 拦截、导航清理等的测试用例。延伸阅读Page APIworker 事件、ConsoleMessage API、JSHandle API、BrowserContext API。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考