ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

WebdriverIO Clock 对象:用 `browser.emulate(‘clock‘, ...)` 精确控制浏览器时间

WebdriverIO Clock 对象:用 `browser.emulate(‘clock‘, ...)` 精确控制浏览器时间 WebdriverIO Clock 对象用browser.emulate(clock, ...)精确控制浏览器时间【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverioWebdriverIO 的browser.emulate()命令允许你在运行时模拟浏览器系统时钟通过返回的 Clock 对象同步控制setTimeout、setInterval、Date等与时间相关的全局函数。本文以 Clock.md 为骨架结合 clock.ts 与 emulate.ts 的源码实现讲解时钟模拟的工作原理、配置选项与三个核心方法tick()、setSystemTime()、restore()帮助你写出可复现、不依赖真实时间的测试。1. 什么是 Clock 对象在 WebdriverIO 中你可以通过browser.emulate(clock, { ... })修改浏览器的系统时钟。该命令会覆盖与时间相关的原生全局函数使它们可以被clock.tick()或返回的 Clock 对象同步控制。被覆盖的全局函数包括setTimeoutclearTimeoutsetIntervalclearIntervalDate对象一个关键的行为是时钟默认从 Unix 纪元时间戳 0开始。这意味着如果你的应用通过new Date()实例化日期而你在调用emulate命令时没有传入任何其他选项得到的将是1970 年 1 月 1 日。2. 前提条件WebDriver Bidi时钟模拟依赖 WebDriver Bidi 的 preload script 机制详见 emulate.ts 与 Emulation.md当前 Chrome、Edge、Firefox 的较新版本支持Safari 不支持如果使用云端厂商启动浏览器需确认厂商支持 WebDriver Bidi需要在 capabilities 中设置webSocketUrl: true以启用 WebDriver Bidi。在非 Bidi 环境下emulate命令会直接抛出错误。测试用例 emulate.test.ts 验证了这一点await expect(() browser.emulate(geoLocation, {})) .rejects.toThrow(/emulate command is only supported for Bidi/)3. 基础示例立即覆盖当前页与后续所有页面当调用browser.emulate(clock, { ... })时它会立即覆盖当前页面的全局函数并且对所有后续加载的页面同样生效const clock await browser.emulate(clock, { now: new Date(1989, 7, 4) }) console.log(await browser.execute(() (new Date()).toString())) // returns Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time) await browser.url(https://webdriverio) console.log(await browser.execute(() (new Date()).toString())) // returns Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time) await clock.restore() console.log(await browser.execute(() (new Date()).toString())) // returns Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time) await browser.url(http://guinea-pig.webdriver.io/pointer.html) console.log(await browser.execute(() (new Date()).toString())) // returns Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time)可以看到调用emulate后当前页面与导航后的新页面中new Date()都返回模拟时间1989 年 8 月 4 日调用clock.restore()后当前页面立即恢复真实时间示例输出为 2024 年某时刻恢复后再导航到新页面新页面也不再被模拟时钟影响说明 preload script 已被移除。4. 底层实现ClockManager 与 fake-timers时钟模拟的实际实现位于 clock.ts其核心是ClockManager类底层依赖sinonjs/fake-timers库。当你在emulate中传入clock作用域时emulate.ts 会实例化ClockManager并调用其install()if (scope clock) { const clock new ClockManager(this) await clock.install(options as FakeTimerInstallOpts) storeRestoreFunction(this, clock, clock.restore.bind(clock)) return clock }ClockManager.install()的安装过程分为三步clock.ts当前页面即时生效通过browser.executeScript注入 fake-timers 库再用browser.execute(installFakeTimers, installOptions)调用window.__wdio_sinon.install(options)立即安装假时钟后续页面预加载通过 Bidi 的scriptAddPreloadScript添加 preload script保证跳转后的新页面同样被覆盖初始化脚本兜底调用addInitScript确保即使没有 preload script 支持的环境中也能注入。install()还会把传入的Date实例转换为时间戳后再传给 fake-timersclock.tsconst installOptions: FakeTimerInstallOpts { ...emulateOptions, now: emulateOptions.now (emulateOptions.now instanceof Date) ? emulateOptions.now.getTime() : emulateOptions.now }测试 emulate.test.ts 验证了这一调用链emulate(clock, { now })会分别触发executeScript、execute、addInitScript、scriptAddPreloadScript各一次且addInitScript收到的now已被转换为now.getTime()。注意当在 Node.js 环境globalThis.window存在下运行时install()会直接跳过安装因此该特性面向浏览器运行时clock.ts。4.1 配置选项FakeTimerInstallOptsemulate(clock, ...)的选项类型为FakeTimerInstallOpts与sinonjs/fake-timers的安装选项一致完整定义见 Emulation.mdinterface FakeTimerInstallOpts { // 安装指定 Unix 纪元时间的假时钟 // default: 0 now?: number | Date | undefined; // 需要伪造的全局方法和 API 名称数组。默认情况下 WebdriverIO // 不替换 nextTick() 和 queueMicrotask()。例如 // browser.emulate(clock, { toFake: [setTimeout, nextTick] }) // 只伪造 setTimeout() 与 nextTick() toFake?: FakeMethod[] | undefined; // 调用 runAll() 时最多运行多少个定时器默认: 1000 loopLimit?: number | undefined; // 是否让模拟时间基于真实系统时间自动递增 //例如真实系统时间每变化 20ms模拟时间也递增 20ms // default false shouldAdvanceTime?: boolean | undefined; // 仅在与 shouldAdvanceTime: true 配合时生效。 // 真实系统时间每变化 advanceTimeDelta ms模拟时间递增 advanceTimeDelta ms // default: 20 advanceTimeDelta?: number | undefined; // 是否让 FakeTimers 委托各自的处理函数来清除原生即非伪造的定时器。 // 默认不清理如果安装 FakeTimers 之前就已存在定时器可能导致意外行为 // default: false shouldClearNativeTimers?: boolean | undefined; }各选项要点选项默认值作用now0Unix 纪元指定模拟时钟的起始时间接受时间戳或DatetoFake除nextTick、queueMicrotask外的全部限定伪造哪些全局方法与 APIloopLimit1000runAll()一次性可执行的定时器数量上限shouldAdvanceTimefalse是否随真实系统时间流逝自动推进模拟时钟advanceTimeDelta20配合shouldAdvanceTime指定自动推进的步长毫秒shouldClearNativeTimersfalse是否委托原生 handler 清理安装前已存在的原生定时器例如只想伪造Date而不影响定时器行为可以这样配置await browser.emulate(clock, { toFake: [Date] })5. 三个核心方法ClockManager暴露了三个方法源码实现均位于 clock.ts命令壳文件分别为 restore.ts、setSystemTime.ts、tick.ts。5.1tick(ms)推进时钟把时钟向前拨动指定毫秒数落在该时间范围内的定时器会被触发const clock await browser.emulate(clock, { now: new Date(2021, 3, 14) }) console.log(await browser.execute(() new Date().getTime())) // returns 1618383600000 await clock.tick(1000) console.log(await browser.execute(() new Date().getTime())) // returns 1618383601000其实现是直接在页面内调用已安装假时钟的tick方法clock.tsasync tick(ms: number) { await this.#browser.execute((ms) window.__clock.tick(ms), ms) }典型应用测试setTimeout/setInterval驱动的逻辑如轮询、节流、倒计时无需真实等待时间流逝。5.2setSystemTime(date)设置系统时间把系统时间改为新的now。与tick不同它不会触发任何定时器也不会改变定时器剩余触发时间const clock await browser.emulate(clock, { now: new Date(2021, 3, 14) }) console.log(await browser.execute(() new Date().getTime())) // returns 1618383600000 await clock.setSystemTime(new Date(2011, 3, 15)) console.log(await browser.execute(() new Date().getTime())) // returns 1302850800000参数接受Date对象或时间戳数字传入Date时会在命令层转换为时间戳再注入页面clock.tsasync setSystemTime(date: number | Date) { const serializableSystemTime date instanceof Date ? date.getTime() : date await this.#browser.execute((date) window.__clock.setSystemTime(date), serializableSystemTime) }典型应用模拟时间穿越场景如验证日志时间戳、日期切换月末/年末、超时判定等。5.3restore()恢复原生函数恢复所有被覆盖的原生函数。WebdriverIO会在每个测试之间自动调用 restore因此通常不需要手动调用clock.tsasync restore() { await this.#resetFn() this.#isInstalled false }restore()内部会执行#resetFn它同时完成三件事clock.ts通过scriptRemovePreloadScript移除预加载脚本通过execute(uninstallFakeTimers)在当前页面卸载假时钟还原初始化脚本。一个完整的使用示例it(should restore the clock, async () { console.log(new Date()) // returns e.g. 1722560447102 const clock await browser.emulate(clock, { now: new Date(2021, 3, 14) }) console.log(await browser.execute(() new Date().getTime())) // returns 1618383600000 await clock.restore() console.log(await browser.execute(() new Date().getTime())) // returns 1722560447102 })6. 测试间自动恢复机制除了手动调用clock.restore()WebdriverIO 还维护了一张全局的恢复函数表restoreFunctions定义于 constants.tsexport const restoreFunctions new MapWebdriverIO.Browser, RestoreMap()每次emulate(clock, ...)都会通过storeRestoreFunction把clock.restore注册进去emulate.ts。browser.restore()命令会遍历该表并依次执行所有已注册的恢复函数从而在测试之间自动清理模拟状态见 restore.ts 的实现。这意味着在单个测试内你可以放心使用clock.tick()/setSystemTime()推进或改写时间测试结束或切换测试时WebdriverIO 会自动恢复真实时钟避免模拟状态泄漏到后续用例。7. 常见应用场景小结场景推荐方式让时间停在某个固定日期emulate(clock, { now: new Date(1989, 7, 4) })只伪造Date保留真实定时器emulate(clock, { toFake: [Date] })触发一段延时后的回调clock.tick(1000)无副作用地改写当前时间clock.setSystemTime(new Date(...))用例结束后还原await clock.restore()框架也会自动处理模拟真实时间流速emulate(clock, { shouldAdvanceTime: true, advanceTimeDelta: 20 })8. 进一步阅读时钟命令壳与 JSDoc 示例restore.ts、setSystemTime.ts、tick.tsemulate命令支持的其他作用域geolocation、userAgent、colorScheme、onLine、deviceemulate.ts 与 Emulation.md时钟模拟的单元测试emulate.test.ts【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表