- 开发工具
- 测试
- CLI
- MCP 服务
【免费下载链接】memlab
A framework for finding JavaScript memory leaks and analyzing heap snapshots
导读
Cookies是 memlab 核心类型系统中用于承载浏览器 Cookie 列表的数据结构,它是测试场景(Scenario)登录态配置、Cookie 文件加载与浏览器注入流程的枢纽。本文将以 Cookies.md 与 Cookie.md 为骨架,结合 Types.ts、TestPlanner.ts 与 API.ts 的源码实现,完整说明 Cookie 字段语义、三种注入方式、domain 自动推断机制以及从场景定义到page.setCookie的底层调用链,帮助你为需要登录认证的页面正确配置泄漏检测环境。
一、类型概览:Cookies与Cookie的关系
Cookies是 memlab 中最简单也最常用的类型别名之一,定义在 Types.ts:
export type Cookies = Array<Cookie>;即一个 Cookie 对象的数组,用于整体承载一次测试运行需要设置到浏览器页面的全部 Cookie。其单个元素Cookie是一个结构化对象,定义在 Types.ts:
export type Cookie = { name: string; // 必填 value: string; // 必填 domain?: string; // 建议提供 url?: Undefinable<string>; // 可选 path?: Undefinable<string>; // 可选 expires?: Undefinable<number>; // 可选 httpOnly?: Undefinable<boolean>; // 可选 session?: Undefinable<boolean>; // 可选 secure?: Undefinable<boolean>; // 可选 sameSite?: Undefinable<'Strict' | 'Lax'>; // 可选 };字段结构遵循一个明确约定(见 Types.ts 的 JSDoc 注释):
name与value为必填字段,缺少任何一个都会导致 Cookie 不完整;- 强烈建议同时指定
domain,否则 memlab 会尝试依据场景 URL 自动推断 domain(推断规则见下文第五节); - 其余字段均为可选,
Undefinable<T>表示该字段允许显式传入undefined(其类型定义为T | undefined,可参考 Undefinable.md)。
二、Cookie字段逐项详解
依据 Cookie.md 的字段说明,并结合 Types.ts 的源码注释,各字段语义如下:
| 字段 | 类型 | 必填 | 语义 |
|---|---|---|---|
name | string | ✅ | Cookie 的名称 |
value | string | ✅ | 赋予该 Cookie 的值 |
domain | string | 建议 | 与 Cookie 关联的域名;不指定时由 memlab 自动推断 |
url | Undefinable<string> | 否 | 与 Cookie 设置关联的请求 URI,会影响 Cookie 的默认 domain 与 path |
path | Undefinable<string> | 否 | 与 Cookie 关联的路径 |
expires | Undefinable<number> | 否 | Cookie 的过期时间,以Unix 时间(秒)表示 |
httpOnly | Undefinable<boolean> | 否 | 是否仅允许通过 HTTP 访问(不能由 JavaScript 读取) |
session | Undefinable<boolean> | 否 | 是否为会话 Cookie |
secure | Undefinable<boolean> | 否 | 传输是否要求安全协议(如 HTTPS) |
sameSite | Undefinable<'Strict' \| 'Lax'> | 否 | 是否随跨站请求发送,用于抵御跨站请求伪造(CSRF)攻击 |
几个值得注意的细节:
expires的时间单位是 Unix 秒,与 HTTP Set-Cookie 头中Expires的表示一致,不是毫秒时间戳;sameSite仅支持'Strict'与'Lax'两个取值,类型定义中没有'None',因此跨站 Cookie 场景需要在secure与sameSite的取值上自行斟酌;url与domain/path存在联动关系:指定url时,浏览器会依据该请求 URI 推导默认的 domain 和 path,这与 Puppeteer 的page.setCookie行为一致——memlab 最终就是通过page.setCookie(...)将这些对象原样注入浏览器的(见第四节调用链)。
三、Cookies 的三种注入来源
memlab 允许通过三种途径提供Cookies数据,按优先级从高到低分别是:场景回调scenario.cookies()、RunOptions.cookiesFile指定的 Cookie 文件、Synthesizer 内置的 Cookie 文件。其加载逻辑集中在 TestPlanner.ts 的loadCookies方法中。
3.1 场景内联:IScenario.cookies()回调
在测试场景中直接声明 Cookie 列表,是最常用、也最直观的方式。Types.ts 中IScenario接口将其声明为cookies?: () => Cookies。官方文档示例(见 IScenario.md)如下:
const scenario = { url: () => 'https://www.facebook.com/', cookies: () => [ {name: 'cm_j', value: 'none', domain: '.facebook.com'}, {name: 'datr', value: 'yJvIY...', domain: '.facebook.com'}, {name: 'c_user', value: '8917...', domain: '.facebook.com'}, {name: 'xs', value: '95:9WQ...', domain: '.facebook.com'}, // ... ], }; module.exports = scenario;该场景既可以在命令行通过memlab run --scenario test.js使用(见 CLI 文档),也可以直接传给@memlab/api的run({scenario})(见 API.ts 的RunOptions)。
3.2 文件注入:cookiesFile
当 Cookie 数量较多或需要从已有导出中复用时,可以将其序列化为 JSON 数组写入文件,再通过RunOptions.cookiesFile传入。该选项声明于 API.ts:
export type RunOptions = { /** the absolute path of cookies file */ cookiesFile?: string; // ... };对应的类型文档见 RunOptions.md。文件内容格式即Cookies类型的 JSON 序列化:
[ {"name": "session_id", "value": "abc123", "domain": ".example.com"}, {"name": "theme", "value": "dark", "domain": ".example.com"} ]3.3 Synthesizer 内置 Cookie 文件
当使用 memlab 内置的场景合成器(Synthesizer)时,IE2EScenarioSynthesizer接口通过getCookieFile(visitPlan)方法(见 Types.ts)返回一个相对于persistentDataDir的 Cookie 文件名;TestPlanner.ts 会将其拼接到持久化数据目录下并读取解析。默认的 DefaultScenarioSynthesizer.ts 与 TestSPAVisitSynthesizer.ts 均返回null(即不提供),此时只要未配置cookiesFile,loadCookies就会直接返回空数组:
// TestPlanner.ts if (cookieFile == null && this.config.externalCookiesFile == null) { return []; }三种来源的优先级逻辑在源码中非常清晰:场景回调优先于文件,文件内部则cookiesFile优先于 Synthesizer 内置文件。
四、底层调用链:从场景定义到page.setCookie
Cookies 的注入并不是“声明即生效”,而是经过一条明确的调用链,理解它有助于排查“为什么 Cookie 没生效”的问题:
- 规划访问计划:
TestPlanner.init()调用generateVisitPlan()(TestPlanner.ts),在生成visitPlan的同时调用loadCookies()得到Cookies数组,并暂存在TestPlanner实例中; - 读取 Cookie:
loadCookies()按第三节的优先级取出数据,随后调用populateDomainInCookiesFromScenario(场景来源)或populateDomainInCookies(文件来源)补齐 domain 字段; - 设置页面:浏览器交互入口
testInBrowser()(API.ts)中调用setupPage(page, options),其中关键一行是(API.ts):
// set login session await page.setCookie(...testPlanner.getCookies());即把经过处理后的Cookies数组展开为参数,一次性写入 Puppeteer 页面对象。之后才开始interactionManager.visitAndGetSnapshots(options)的页面访问与堆快照采集流程,因此登录态在首次页面加载前就已就绪。
从IE2EScenarioSynthesizer.getCookieFile、IE2EScenarioVisitPlan到Cookies的完整接口契约都可以在 Types.ts 中找到,感兴趣可以继续追踪。
五、domain 自动推断:不写 domain 时 memlab 做了什么
这是Cookies使用中最容易踩坑、也最能体现 memlab 设计细节的一点。官方类型注释明确说明(Types.ts):最好显式提供domain,否则 memlab 会根据场景 URL 自动推断。
对于场景内联的 Cookie,推断逻辑位于populateDomainInCookiesFromScenario(TestPlanner.ts):
const url = new URL(scenario.url()); const domain = '.' + url.hostname.split('.').slice(1).join('.'); for (const cookie of cookies) { if (cookie.domain) { ret.push({...cookie}); } else { ret.push({...cookie, domain}); } }推断规则可以概括为两步:
- 取场景 URL 的主机名,去掉第一段子域名,得到“根域”;
- 在根域前加
.前缀形成 domain 值。
例如场景 URL 为https://www.facebook.com/时,hostname是www.facebook.com,去掉www后得到facebook.com,最终自动填充domain = '.facebook.com'。这也解释了为什么官方示例中的 domain 都写成.facebook.com(见 Types.ts 的 JSDoc 说明)。
两个边界行为值得注意:
- 已有
domain的 Cookie 原样保留,不会被覆盖; - 该推断发生在
new URL(...)之上,要求场景必须提供合法的url()回调。
对于从文件加载的 Cookie,populateDomainInCookies(TestPlanner.ts)则采用另一套策略:以 Synthesizer 提供的domain为基准,并遍历domainPrefixes前缀集合(默认包含空字符串''),为每个前缀与 domain 的组合生成一份 Cookie 副本,从而覆盖多个子域。
六、测试验证:Cookie 回调的执行时机
仓库中的端到端测试对cookies回调的执行顺序有明确断言。在 E2ETestScenarioCallback.test.ts 中,测试场景同时定义了app、cookies、repeat、url等全部回调,并断言它们的调用顺序:
expect(normalizedCalls).toEqual([ // the first 4 calls (app, cookies, repeat, url) can be in any order 'url', 'repeat', 'app', 'cookies', // the following calls must be in the exact order listed 'beforeInitialPageLoad', 'isPageLoaded', 'setup', 'action', ... ]);该测试证实了两个关键事实:
cookies回调属于“规划阶段”回调,与url、repeat等并列,在浏览器加载页面前就已调用;- 回调返回的
Cookies数组(测试中返回[])会在beforeInitialPageLoad、页面加载与交互动作之前被消费,与第四节展示的调用链时序完全吻合。
七、实战建议与注意事项
综合类型定义与源码实现,使用Cookies时建议遵循以下实践:
- 登录类场景务必显式填写
domain。虽然 memlab 支持自动推断,但推断规则依赖场景url()的格式;显式填写(如.example.com带点前缀)可避免因 URL 结构差异导致的推断偏差,这也是官方示例一贯的做法; - Cookie 文件与场景回调不要混用。优先级规则是“场景回调 >
cookiesFile> Synthesizer 内置文件”,同时配置多个来源时只有高优先级者生效,容易造成误解; - 敏感字段按需声明。
httpOnly、secure、sameSite等字段会直接影响浏览器对 Cookie 的接纳与发送行为,模拟生产登录态时建议与真实环境保持一致; - 文件来源的 Cookie 需补全 domain。从 JSON 文件加载的 Cookie 会经过
populateDomainInCookies的“前缀 × domain”展开逻辑,若 Synthesizer 未提供正确 domain,最终写入浏览器的 domain 可能与预期不符; - 排查注入失败时,沿调用链检查:先确认
TestPlanner.getCookies()返回的内容是否符合预期,再检查setupPage中page.setCookie(...)的执行,中间环节均可在packages/e2e/src/lib/operations/TestPlanner.ts与packages/api/src/API.ts中直接审阅。
参考资料
- 类型别名文档:Cookies.md、Cookie.md、Undefinable.md
- 接口文档:IScenario.md、RunOptions.md
- 核心实现:Types.ts、TestPlanner.ts、API.ts
- 测试用例:E2ETestScenarioCallback.test.ts
- 开发工具
- 测试
- CLI
- MCP 服务
【免费下载链接】memlab
A framework for finding JavaScript memory leaks and analyzing heap snapshots
相关推荐
OctoPrint REST API 认证指南:Login、Logout、Current User 与登录会话数据模型详解
OctoPrint REST API 认证指南:Login、Logout、Current User 与登录会话数据模型详解 OctoPrint 的 REST A
物联网后端Zeek FTP 会话跟踪与 ftp.log 数据模型详解:深入解读 FTP::Info 记录结构与密码捕获机制
Zeek FTP 会话跟踪与 ftp.log 数据模型详解:深入解读 FTP::Info 记录结构与密码捕获机制 导读 本文以 Zeek 核心脚本 script
网络安全网络IDSOry Hydra 同意请求会话数据模型 AcceptOAuth2ConsentRequestSession 详解:Access Token 与 ID Token 会话注入实战
Ory Hydra 同意请求会话数据模型 AcceptOAuth2ConsentRequestSession 详解:Access Token 与 ID Toke
认证鉴权后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考