接口: IScenario
测试场景指定了 E2E 测试与 Web 浏览器交互的方式。测试场景可以保存为 .js
文件,并传递给 memlab run --scenario
命令
// save as test.js and use in terminal:
// $ memlab run --scenario test.js
module.exports = {
url: () => 'https://npmjs.net.cn/',
action: async () => ... ,
back: async () => ... ,
cookies: () => ... , // optional
repeat: () => ... , // optional
...
};
测试场景实例也可以传递给 @memlab/api
导出的 run API。
const {run} = require('@memlab/api');
(async function () {
const scenario = {
url: () => 'https://#',
action: async () => ... ,
back: async () => ... ,
cookies: () => ... , // optional
repeat: () => ... , // optional
...
};
const leaks = await run({scenario});
})();
属性
可选
action: InteractionsCallback
action
是回调函数,用于定义在初始页面加载后要触发内存泄漏的交互。从 action
回调触发的浏览器交互所分配的所有浏览器中的 JS 对象都将成为内存泄漏过滤的候选对象。
const scenario = {
url: () => 'https://npmjs.net.cn/',
action: async (page) => {
await page.click('a[href="/link"]');
},
back: async (page) => {
await page.click('a[href="/back"]');
},
}
module.exports = scenario;
注意:始终清理对浏览器上下文中 JS 对象的外部 puppeteer 引用。
const scenario = {
url: () => 'https://npmjs.net.cn/',
action: async (page) => {
const elements = await page.$x("//button[contains(., 'Text in Button')]");
const [button] = elements;
if (button) {
await button.click();
}
// dispose external references to JS objects in browser context
await promise.all(elements.map(e => e.dispose()));
},
back: async (page) => ... ,
}
module.exports = scenario;
可选
back: InteractionsCallback
back
是回调函数,用于指定 memlab 应如何返回/还原 action
回调。可以将其视为撤消操作。
const scenario = {
url: () => 'https://npmjs.net.cn/',
action: async (page) => {
await page.click('a[href="/link"]');
},
back: async (page) => {
await page.click('a[href="/back"]');
},
}
查看 此页面,了解为什么 memlab 需要撤消/还原 action
回调。
可选
beforeInitialPageLoad: InteractionsCallback
beforeInitialPageLoad
是仅在初始页面加载之前调用一次的回调函数。此回调可用于设置 HTTP 标头或在加载网页之前准备数据。
const scenario = {
url: () => 'https://npmjs.net.cn/',
beforeInitialPageLoad: async (page) => {
// before the initial page load
},
action: async (page) => {
await page.click('a[href="/link"]');
},
back: async (page) => {
await page.click('a[href="/back"]');
},
}
module.exports = scenario;
可选
beforeLeakFilter: InitLeakFilterCallback
生命周期函数回调,最初在后续 leakFilter
函数调用之前调用一次。此回调可用于初始化某些数据存储或进行任何一次性预处理。
参数:
snapshot
:IHeapSnapshot
| 在完成所有浏览器交互后拍摄的最终堆快照。查看 IHeapSnapshot,了解有关查询堆快照的更多 API。leakedNodeIds
:Set<number>
| 由action
调用分配但在浏览器中的back
调用后未释放的所有 JS 堆对象的 id 集合。
示例:
module.exports = {
url: () => ... ,
action: async (page) => ... ,
back: async (page) => ... ,
beforeLeakFilter: (snapshot, leakedNodeIds) {
// initialize some data stores
},
};
可选
isPageLoaded: CheckPageLoadCallback
可选回调函数,用于检查是否已加载初始页面加载和后续浏览器交互的网页。
如果未提供此回调,则默认情况下,当至少 500 毫秒没有网络连接时,memlab 会认为导航已完成。
参数:
返回:一个布尔值,如果返回
true
,则 memlab 将认为导航已完成;如果返回false
,则 memlab 将继续调用此回调,直到它返回true
。这是一个异步回调,您也可以await
并返回true
,直到某些异步逻辑得到解决。示例:
module.exports = {
url: () => ... ,
action: async (page) => ... ,
back: async (page) => ... ,
isPageLoaded: async (page) => {
await page.waitForNavigation({
// consider navigation to be finished when there are
// no more than 2 network connections for at least 500 ms.
waitUntil: 'networkidle2',
// Maximum navigation time in milliseconds
timeout: 5000,
});
return true;
},
};
可选
leakFilter: LeakFilterCallback
此回调定义了如何过滤泄漏的对象。对于由 action
回调分配的每个节点(浏览器中的 JS 堆对象)调用该回调,但在 back
回调后未释放。这些对象可能是故意保留在内存中的缓存,或者它们是内存泄漏。
此可选回调允许您定义自己的算法,以选择特定受测 JS 程序的内存泄漏。
如果未定义此可选回调,memlab 将使用其内置的泄漏过滤器,该过滤器将分离的 DOM 元素和未安装的 Fiber 节点(与 React Fiber 树分离)视为内存泄漏。
参数:
node
:IHeapNode
| 已分配但未释放的堆对象。此过滤器回调将应用于堆快照中已分配但未释放的每个节点。snapshot
:IHeapSnapshot
| 在完成所有浏览器交互后拍摄的最终堆快照。查看 IHeapSnapshot,了解有关查询堆快照的更多 API。leakedNodeIds
:Set<number>
| 由action
调用分配但在浏览器中的back
调用后未释放的所有 JS 堆对象的 id 集合。
返回:布尔值,指示快照中给定的节点是否应被视为泄漏。
示例:
module.exports = {
url: () => ... ,
action: async (page) => ... ,
back: async (page) => ... ,
leakFilter(node, snapshot, leakedNodeIds) {
// any unreleased node (JS heap object) with 1MB+
// retained size is considered a memory leak
return node.retainedSize > 1000000;
},
};
可选
retainerReferenceFilter: ReferenceFilterCallback
回调,可用于定义逻辑以确定是否应将引用视为保留器跟踪的一部分。为堆快照中的每个引用(边)调用该回调。
有关具体示例,请查看 leakFilter。
参数:
edge
:IHeapEdge
| 用于计算保留器跟踪的引用(边)snapshot
:IHeapSnapshot
| 在完成所有浏览器交互后拍摄的堆快照。查看 IHeapSnapshot,了解有关查询堆快照的更多 API。isReferenceUsedByDefault
:boolean
| MemLab 有其自己的默认逻辑,用于确定是否应将引用视为保留器跟踪的一部分,如果此参数为 true,则表示 MemLab 在计算保留器跟踪时将考虑此引用。
返回:指示在计算保留器跟踪时是否应考虑给定引用的值。请注意,当此回调返回 true 时,该引用仅被视为保留器跟踪的候选对象,因此可能包含也可能不包含在保留器跟踪中;但是,如果此回调返回 false,则该引用将被排除。
请注意,通过排除对象的支配者引用(即,必须经过的边才能从 GC 根到达堆对象),该对象将被视为在堆图中不可达的;因此,该引用和堆对象将不包含在保留器跟踪检测和保留器大小计算中。
- 示例:
// save as leak-filter.js
module.exports = {
retainerReferenceFilter(edge, _snapshot, _leakedNodeIds) {
// exclude react fiber references
if (edge.name_or_index.toString().startsWith('__reactFiber$')) {
return false;
}
return true;
}
};
可选
setup: InteractionsCallback
setup
是仅在初始页面加载后调用一次的回调函数。 如果必须,可以使用此回调登录(我们建议使用 cookies)或在 action 调用之前准备数据。
const scenario = {
url: () => 'https://npmjs.net.cn/',
setup: async (page) => {
// log in or prepare data for the interaction
},
action: async (page) => {
await page.click('a[href="/link"]');
},
back: async (page) => {
await page.click('a[href="/back"]');
},
}
module.exports = scenario;
方法
可选
cookies()
如果您正在运行 memlab 的页面需要身份验证或设置特定的 cookie,则可以将它们作为 <name, value, domain>
元组的列表传递。
注意:请确保为 cookie 元组提供正确的 domain
字段。 如果未指定 domain
字段,memlab 将尝试根据 url
回调填充域。 例如,当 domain
字段不存在时,memlab 将根据初始页面加载的 url 自动填充 .facebook.com
作为域:https://#/
。
- 返回:
Cookies
| cookie 列表 - 示例:
const scenario = {
url: () => 'https://#/',
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;
可选
repeat()
指定 memlab 执行的 action
和 back
操作的额外数量。
返回:一个数值,指定额外操作的数量。
示例:
module.exports = {
url: () => ... ,
action: async (page) => ... ,
back: async (page) => ... ,
// browser interaction: two additional [ action -> back ]
// init-load -> action -> back -> action -> back -> action -> back
repeat: () => 2,
};
- 返回:
number
- 来源:
url()
页面的初始 url 的字符串值。
- 返回:
string
| 初始 url 的字符串值 - 示例:
const scenario = {
url: () => 'https://npmjs.net.cn/',
};
module.exports = scenario;
如果测试场景仅指定 url
回调(没有 action
回调),memlab 将尝试检测初始页面加载中的内存泄漏。 初始页面加载分配的所有对象都将成为内存泄漏过滤的候选对象。