跳到主要内容

接口: 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 对象都将成为内存泄漏过滤的候选对象。

  • 参数:

    • page: Page | puppeteer Page 对象,它提供与 Web 浏览器交互的 API。要导入此类型,请查看 Page
  • 示例:

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 回调。可以将其视为撤消操作。

  • 参数:

    • page: Page | puppeteer Page 对象,它提供与 Web 浏览器交互的 API。要导入此类型,请查看 Page
  • 示例:

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 标头或在加载网页之前准备数据。

  • 参数:

    • page: Page | puppeteer Page 对象,它提供与 Web 浏览器交互的 API。要导入此类型,请查看 Page
  • 示例:

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 会认为导航已完成。

  • 参数:

    • page: Page | puppeteer Page 对象,它提供与 Web 浏览器交互的 API。要导入此类型,请查看 Page
  • 返回:一个布尔值,如果返回 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 调用之前准备数据。

  • 参数:

    • page: Page | puppeteer Page 对象,它提供与 Web 浏览器交互的 API。要导入此类型,请查看 Page
  • 示例:

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 执行的 actionback 操作的额外数量。

  • 返回:一个数值,指定额外操作的数量。

  • 示例:

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,
};

url()

页面的初始 url 的字符串值。

  • 返回: string | 初始 url 的字符串值
  • 示例:
const scenario = {
url: () => 'https://npmjs.net.cn/',
};

module.exports = scenario;

如果测试场景仅指定 url 回调(没有 action 回调),memlab 将尝试检测初始页面加载中的内存泄漏。 初始页面加载分配的所有对象都将成为内存泄漏过滤的候选对象。

  • 属性
    • 可选 action: InteractionsCallback
    • Optional back: InteractionsCallback (可选)back: InteractionsCallback
    • Optional beforeInitialPageLoad: InteractionsCallback (可选)beforeInitialPageLoad: InteractionsCallback
    • Optional beforeLeakFilter: InitLeakFilterCallback (可选)beforeLeakFilter: InitLeakFilterCallback
    • Optional isPageLoaded: CheckPageLoadCallback (可选)isPageLoaded: CheckPageLoadCallback
    • Optional leakFilter: LeakFilterCallback (可选)leakFilter: LeakFilterCallback
    • Optional retainerReferenceFilter: ReferenceFilterCallback (可选)retainerReferenceFilter: ReferenceFilterCallback
    • Optional setup: InteractionsCallback (可选)setup: InteractionsCallback
  • 方法
    • Optional cookies() (可选)cookies()
    • Optional repeat() (可选)repeat()
    • url()