@memlab/api 1.0.24 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/API.d.ts.map +1 -1
- package/dist/API.js +5 -13
- package/dist/__tests__/API/E2EFindMemoryLeaks.test.js +54 -0
- package/dist/__tests__/API/E2EFindMemoryLeaksWithSnapshotFiles.test.js +2 -2
- package/dist/__tests__/API/E2EFindTaggedMemoryLeaks.test.d.ts +11 -0
- package/dist/__tests__/API/E2EFindTaggedMemoryLeaks.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EFindTaggedMemoryLeaks.test.js +82 -0
- package/package.json +1 -1
package/dist/API.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"API.d.ts","sourceRoot":"","sources":["../src/API.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,SAAS,EAGT,QAAQ,EACT,MAAM,cAAc,CAAC;AAEtB,OAAO,EAML,YAAY,EAEb,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAEnD,OAAO,8BAA8B,MAAM,gDAAgD,CAAC;AAC5F,OAAO,gBAAgB,MAAM,kCAAkC,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,0BAA0B,CAAC,EAAE,WAAW,CAAC;IACzC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;OAEG;IACH,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB;;OAEG;IACH,SAAS,EAAE,8BAA8B,CAAC;CAC3C,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IAGvB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,0BAA0B,CAAC,EAAE,WAAW,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,8BAA8B,CAAC,CAWzC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,GAAG,CAAC,UAAU,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAazE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,8BAA8B,CAAC,CAQzC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,gBAAgB,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAK5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,4BAA4B,CAChD,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAM,GAC/B,OAAO,CAAC,eAAe,EAAE,CAAC,CAU5B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EAAE,YAAY,EAC1B,IAAI,GAAE,UAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCpE;
|
|
1
|
+
{"version":3,"file":"API.d.ts","sourceRoot":"","sources":["../src/API.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,SAAS,EAGT,QAAQ,EACT,MAAM,cAAc,CAAC;AAEtB,OAAO,EAML,YAAY,EAEb,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAEnD,OAAO,8BAA8B,MAAM,gDAAgD,CAAC;AAC5F,OAAO,gBAAgB,MAAM,kCAAkC,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,0BAA0B,CAAC,EAAE,WAAW,CAAC;IACzC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;OAEG;IACH,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB;;OAEG;IACH,SAAS,EAAE,8BAA8B,CAAC;CAC3C,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IAGvB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,0BAA0B,CAAC,EAAE,WAAW,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,8BAA8B,CAAC,CAWzC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,GAAG,CAAC,UAAU,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAazE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,8BAA8B,CAAC,CAQzC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,gBAAgB,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAK5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,4BAA4B,CAChD,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAM,GAC/B,OAAO,CAAC,eAAe,EAAE,CAAC,CAU5B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EAAE,YAAY,EAC1B,IAAI,GAAE,UAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCpE;AA4DD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD3E"}
|
package/dist/API.js
CHANGED
|
@@ -299,27 +299,20 @@ function setupPage(page, options = {}) {
|
|
|
299
299
|
yield page.setCacheEnabled(cache);
|
|
300
300
|
// automatically accept dialog
|
|
301
301
|
page.on('dialog', (dialog) => __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
if (config.verbose) {
|
|
303
|
+
core_1.info.lowLevel(`Browser dialog: ${dialog.message()}`);
|
|
304
|
+
}
|
|
302
305
|
yield dialog.accept();
|
|
303
306
|
}));
|
|
304
307
|
});
|
|
305
308
|
}
|
|
306
|
-
function autoDismissDialog(page, options = {}) {
|
|
307
|
-
var _a;
|
|
308
|
-
const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
|
|
309
|
-
page.on('dialog', (dialog) => __awaiter(this, void 0, void 0, function* () {
|
|
310
|
-
if (config.verbose) {
|
|
311
|
-
core_1.info.lowLevel(`Browser dialog: ${dialog.message()}`);
|
|
312
|
-
}
|
|
313
|
-
yield dialog.dismiss();
|
|
314
|
-
}));
|
|
315
|
-
}
|
|
316
309
|
function initBrowserInfoInConfig(browser, options = {}) {
|
|
317
310
|
var _a;
|
|
318
311
|
return __awaiter(this, void 0, void 0, function* () {
|
|
319
312
|
const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
|
|
320
|
-
core_1.browserInfo.
|
|
313
|
+
core_1.browserInfo.recordPuppeteerConfig(config.puppeteerConfig);
|
|
321
314
|
const version = yield browser.version();
|
|
322
|
-
core_1.browserInfo.
|
|
315
|
+
core_1.browserInfo.recordBrowserVersion(version);
|
|
323
316
|
if (config.verbose) {
|
|
324
317
|
core_1.info.lowLevel(JSON.stringify(core_1.browserInfo, null, 2));
|
|
325
318
|
}
|
|
@@ -355,7 +348,6 @@ function testInBrowser(options = {}) {
|
|
|
355
348
|
const visitPlan = testPlanner.getVisitPlan();
|
|
356
349
|
// setup page configuration
|
|
357
350
|
config.setDevice(visitPlan.device);
|
|
358
|
-
autoDismissDialog(page);
|
|
359
351
|
yield initBrowserInfoInConfig(browser);
|
|
360
352
|
core_1.browserInfo.monitorWebConsole(page);
|
|
361
353
|
yield setupPage(page, options);
|
|
@@ -81,3 +81,57 @@ test('self-defined leak detector can find TestObject', () => __awaiter(void 0, v
|
|
|
81
81
|
const reader = result.runResult;
|
|
82
82
|
expect(path_1.default.resolve(reader.getRootDirectory())).toBe(path_1.default.resolve(workDir));
|
|
83
83
|
}), E2ETestSettings_1.testTimeout);
|
|
84
|
+
function injectDetachedDOMElementsWithPrompt() {
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
window.injectHookForLink4 = () => {
|
|
87
|
+
class TestObject {
|
|
88
|
+
}
|
|
89
|
+
const arr = [];
|
|
90
|
+
for (let i = 0; i < 23; ++i) {
|
|
91
|
+
arr.push(document.createElement('div'));
|
|
92
|
+
}
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
window.__injectedValue = arr;
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
window._path_1 = { x: { y: document.createElement('div') } };
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
window._path_2 = new Set([document.createElement('div')]);
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
window._randomObject = [new TestObject()];
|
|
101
|
+
// pop up dialogs
|
|
102
|
+
alert('alert test');
|
|
103
|
+
confirm('confirm test');
|
|
104
|
+
prompt('prompt test:', 'default');
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
test('test runner should work with pages having pop up dialog', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
+
const selfDefinedScenario = {
|
|
109
|
+
app: () => 'test-spa',
|
|
110
|
+
url: () => '',
|
|
111
|
+
action: (page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
yield page.evaluate(() => {
|
|
113
|
+
// pop up dialogs
|
|
114
|
+
alert('alert test');
|
|
115
|
+
confirm('confirm test');
|
|
116
|
+
prompt('prompt test:', 'default');
|
|
117
|
+
});
|
|
118
|
+
yield page.click('[data-testid="link-4"]');
|
|
119
|
+
}),
|
|
120
|
+
leakFilter: (node) => {
|
|
121
|
+
return node.name === 'TestObject' && node.type === 'object';
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
const workDir = path_1.default.join(os_1.default.tmpdir(), 'memlab-api-test', `${process.pid}`);
|
|
125
|
+
fs_extra_1.default.mkdirsSync(workDir);
|
|
126
|
+
const result = yield (0, index_1.run)({
|
|
127
|
+
scenario: selfDefinedScenario,
|
|
128
|
+
evalInBrowserAfterInitLoad: injectDetachedDOMElementsWithPrompt,
|
|
129
|
+
workDir,
|
|
130
|
+
});
|
|
131
|
+
// detected all different leak trace cluster
|
|
132
|
+
expect(result.leaks.length).toBe(1);
|
|
133
|
+
// expect all traces are found
|
|
134
|
+
expect(result.leaks.some(leak => JSON.stringify(leak).includes('_randomObject')));
|
|
135
|
+
const reader = result.runResult;
|
|
136
|
+
expect(path_1.default.resolve(reader.getRootDirectory())).toBe(path_1.default.resolve(workDir));
|
|
137
|
+
}), E2ETestSettings_1.testTimeout);
|
|
@@ -60,7 +60,7 @@ test('leak detector can find detached DOM elements', () => __awaiter(void 0, voi
|
|
|
60
60
|
newSnapshotFiles.push(newFile);
|
|
61
61
|
fs_extra_1.default.moveSync(file, newFile);
|
|
62
62
|
});
|
|
63
|
-
fs_extra_1.default.
|
|
63
|
+
fs_extra_1.default.removeSync(result.getRootDirectory());
|
|
64
64
|
// find memory leaks with the new snapshot files
|
|
65
65
|
const leaks = yield (0, index_1.findLeaksBySnapshotFilePaths)(newSnapshotFiles[0], newSnapshotFiles[1], newSnapshotFiles[2]);
|
|
66
66
|
// detected all different leak trace cluster
|
|
@@ -70,7 +70,7 @@ test('leak detector can find detached DOM elements', () => __awaiter(void 0, voi
|
|
|
70
70
|
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_1')));
|
|
71
71
|
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_2')));
|
|
72
72
|
// finally clean up the temporary directory
|
|
73
|
-
fs_extra_1.default.
|
|
73
|
+
fs_extra_1.default.removeSync(tmpDir);
|
|
74
74
|
}), E2ETestSettings_1.testTimeout);
|
|
75
75
|
test('takeSnapshot API allows to throw and catch exceptions from scenario', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
76
|
const scenarioThatThrows = Object.assign({}, E2ETestSettings_1.scenario);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall web_perf_infra
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=E2EFindTaggedMemoryLeaks.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EFindTaggedMemoryLeaks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EFindTaggedMemoryLeaks.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
* @oncall web_perf_infra
|
|
10
|
+
*/
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
const os_1 = __importDefault(require("os"));
|
|
25
|
+
const path_1 = __importDefault(require("path"));
|
|
26
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
27
|
+
const index_1 = require("../../index");
|
|
28
|
+
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
29
|
+
beforeEach(E2ETestSettings_1.testSetup);
|
|
30
|
+
function tagObjectsAsLeaks() {
|
|
31
|
+
// this class definition must be defined within `tagObjectsAsLeaks`
|
|
32
|
+
// since this function will be serialized and executed in browser context
|
|
33
|
+
class MemLabTracker {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.memlabIdentifier = 'MemLabObjectTracker';
|
|
36
|
+
this.tagToTrackedObjectsMap = new Map();
|
|
37
|
+
}
|
|
38
|
+
tag(target, useCaseId = 'MEMLAB_TAGGED') {
|
|
39
|
+
let trackedItems = this.tagToTrackedObjectsMap.get(useCaseId);
|
|
40
|
+
if (!trackedItems) {
|
|
41
|
+
trackedItems = {
|
|
42
|
+
useCaseId,
|
|
43
|
+
taggedObjects: new WeakSet(),
|
|
44
|
+
};
|
|
45
|
+
this.tagToTrackedObjectsMap.set(useCaseId, trackedItems);
|
|
46
|
+
}
|
|
47
|
+
trackedItems.taggedObjects.add(target);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
window.injectHookForLink4 = () => {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const tracker = (window._memlabTrack = new MemLabTracker());
|
|
54
|
+
const leaks = [];
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
window._taggedLeaks = leaks;
|
|
57
|
+
for (let i = 0; i < 11; ++i) {
|
|
58
|
+
leaks.push({ x: i });
|
|
59
|
+
}
|
|
60
|
+
leaks.forEach(item => {
|
|
61
|
+
tracker.tag(item);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
test('tagged leak objects can be identified as leaks', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
|
+
const selfDefinedScenario = {
|
|
67
|
+
app: () => 'test-spa',
|
|
68
|
+
url: () => '',
|
|
69
|
+
action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
|
|
70
|
+
};
|
|
71
|
+
const workDir = path_1.default.join(os_1.default.tmpdir(), 'memlab-api-test', `${process.pid}`);
|
|
72
|
+
fs_extra_1.default.mkdirsSync(workDir);
|
|
73
|
+
const result = yield (0, index_1.run)({
|
|
74
|
+
scenario: selfDefinedScenario,
|
|
75
|
+
evalInBrowserAfterInitLoad: tagObjectsAsLeaks,
|
|
76
|
+
workDir,
|
|
77
|
+
});
|
|
78
|
+
// tagged objects should be detected as leaks
|
|
79
|
+
expect(result.leaks.length).toBe(1);
|
|
80
|
+
// expect all traces are found
|
|
81
|
+
expect(result.leaks.some(leak => JSON.stringify(leak).includes('_taggedLeaks')));
|
|
82
|
+
}), E2ETestSettings_1.testTimeout);
|