@memlab/api 1.0.23 → 1.0.25
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 +10 -5
- package/dist/API.d.ts.map +1 -0
- package/dist/API.js +9 -13
- package/dist/__tests__/API/E2EBasicAnalysis.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EDetachedDOMAnalysis.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EDuplicateObjectAnalysis.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EFindLeaks.example.d.ts.map +1 -0
- package/dist/__tests__/API/E2EFindMemoryLeaks.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EFindMemoryLeaks.test.js +54 -0
- package/dist/__tests__/API/E2EFindMemoryLeaksWithSnapshotFiles.test.d.ts.map +1 -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/dist/__tests__/API/E2EInjectLeaksWithSetup.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EReloadInSetup.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EResultReader.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EResultReader.test.js +36 -0
- package/dist/__tests__/API/E2ERunMultipleSnapshots.example.d.ts.map +1 -0
- package/dist/__tests__/API/E2ERunSingleSnapshot.example.d.ts.map +1 -0
- package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2EStringAnalysis.test.d.ts.map +1 -0
- package/dist/__tests__/API/{E2EFindWebWorkerLeaks.test.d.ts → E2ETestScenarioCallback.test.d.ts} +1 -1
- package/dist/__tests__/API/E2ETestScenarioCallback.test.d.ts.map +1 -0
- package/dist/__tests__/API/E2ETestScenarioCallback.test.js +135 -0
- package/dist/__tests__/API/lib/E2ETestSettings.d.ts.map +1 -0
- package/dist/__tests__/heap/E2EHeapParser.test.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-1.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-2.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-3.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-4.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-5.test.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-6.d.ts.map +1 -0
- package/dist/__tests__/heap/examples/example-7.test.d.ts.map +1 -0
- package/dist/__tests__/heap/lib/HeapParserTestUtils.d.ts.map +1 -0
- package/dist/__tests__/heap/lib/HeapParserTestUtils.js +1 -0
- package/dist/__tests__/packages/heap-analysis.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -1
- package/dist/lib/APIUtils.d.ts.map +1 -0
- package/dist/lib/APIUtils.js +1 -0
- package/dist/result-reader/BaseResultReader.d.ts.map +1 -0
- package/dist/result-reader/BrowserInteractionResultReader.d.ts +1 -1
- package/dist/result-reader/BrowserInteractionResultReader.d.ts.map +1 -0
- package/dist/result-reader/BrowserInteractionResultReader.js +1 -1
- package/dist/result-reader/SnapshotResultReader.d.ts +115 -0
- package/dist/result-reader/SnapshotResultReader.d.ts.map +1 -0
- package/dist/result-reader/SnapshotResultReader.js +156 -0
- package/package.json +5 -4
- package/dist/__tests__/API/E2EFindWebWorkerLeaks.test.js +0 -69
package/dist/API.d.ts
CHANGED
|
@@ -13,10 +13,11 @@ import { MemLabConfig } from '@memlab/core';
|
|
|
13
13
|
import { TestPlanner } from '@memlab/e2e';
|
|
14
14
|
import { BaseAnalysis } from '@memlab/heap-analysis';
|
|
15
15
|
import BrowserInteractionResultReader from './result-reader/BrowserInteractionResultReader';
|
|
16
|
+
import BaseResultReader from './result-reader/BaseResultReader';
|
|
16
17
|
/**
|
|
17
18
|
* Options for configuring browser interaction run, all fields are optional
|
|
18
19
|
*/
|
|
19
|
-
export
|
|
20
|
+
export type RunOptions = {
|
|
20
21
|
/**
|
|
21
22
|
* test scenario specifying how to interact with browser
|
|
22
23
|
* (for more details view {@link IScenario})
|
|
@@ -52,11 +53,15 @@ export declare type RunOptions = {
|
|
|
52
53
|
* means analyzing the heap of the web worker with name: `'workerTitle'`.
|
|
53
54
|
*/
|
|
54
55
|
webWorker?: Optional<string>;
|
|
56
|
+
/**
|
|
57
|
+
* skip warmup page load for the target web app
|
|
58
|
+
*/
|
|
59
|
+
skipWarmup?: boolean;
|
|
55
60
|
};
|
|
56
61
|
/**
|
|
57
62
|
* A data structure holding the result of the {@link run} API call.
|
|
58
63
|
*/
|
|
59
|
-
export
|
|
64
|
+
export type RunResult = {
|
|
60
65
|
/**
|
|
61
66
|
* leak traces detected and clustered from the browser interaction
|
|
62
67
|
*/
|
|
@@ -70,7 +75,7 @@ export declare type RunResult = {
|
|
|
70
75
|
* Options for memlab inter-package API calls
|
|
71
76
|
* @internal
|
|
72
77
|
*/
|
|
73
|
-
export
|
|
78
|
+
export type APIOptions = {
|
|
74
79
|
testPlanner?: TestPlanner;
|
|
75
80
|
cache?: boolean;
|
|
76
81
|
config?: MemLabConfig;
|
|
@@ -156,7 +161,7 @@ export declare function takeSnapshots(options?: RunOptions): Promise<BrowserInte
|
|
|
156
161
|
* })();
|
|
157
162
|
* ```
|
|
158
163
|
*/
|
|
159
|
-
export declare function findLeaks(runResult:
|
|
164
|
+
export declare function findLeaks(runResult: BaseResultReader): Promise<ISerializedInfo[]>;
|
|
160
165
|
/**
|
|
161
166
|
* This API finds memory leaks by analyzing specified heap snapshots.
|
|
162
167
|
* This is equivalent to `memlab find-leaks` with
|
|
@@ -196,7 +201,7 @@ export declare function findLeaksBySnapshotFilePaths(baselineSnapshot: string, t
|
|
|
196
201
|
* })();
|
|
197
202
|
* ```
|
|
198
203
|
*/
|
|
199
|
-
export declare function analyze(runResult:
|
|
204
|
+
export declare function analyze(runResult: BaseResultReader, heapAnalyzer: BaseAnalysis, args?: ParsedArgs): Promise<void>;
|
|
200
205
|
/**
|
|
201
206
|
* This warms up web server by sending web requests to the web sever.
|
|
202
207
|
* This is equivalent to running `memlab warmup` in CLI.
|
|
@@ -0,0 +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;AA4DD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD3E"}
|
package/dist/API.js
CHANGED
|
@@ -52,7 +52,9 @@ function warmupAndTakeSnapshots(options = {}) {
|
|
|
52
52
|
config.scenario = options.scenario;
|
|
53
53
|
const testPlanner = new e2e_1.TestPlanner({ config });
|
|
54
54
|
const { evalInBrowserAfterInitLoad } = options;
|
|
55
|
-
|
|
55
|
+
if (!options.skipWarmup) {
|
|
56
|
+
yield warmup({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
57
|
+
}
|
|
56
58
|
yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
57
59
|
return BrowserInteractionResultReader_1.default.from(config.workDir);
|
|
58
60
|
});
|
|
@@ -86,7 +88,9 @@ function run(runOptions = {}) {
|
|
|
86
88
|
config.scenario = runOptions.scenario;
|
|
87
89
|
const testPlanner = new e2e_1.TestPlanner({ config });
|
|
88
90
|
const { evalInBrowserAfterInitLoad } = runOptions;
|
|
89
|
-
|
|
91
|
+
if (!runOptions.skipWarmup) {
|
|
92
|
+
yield warmup({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
93
|
+
}
|
|
90
94
|
yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
91
95
|
const runResult = BrowserInteractionResultReader_1.default.from(config.workDir);
|
|
92
96
|
const leaks = yield findLeaks(runResult);
|
|
@@ -295,20 +299,13 @@ function setupPage(page, options = {}) {
|
|
|
295
299
|
yield page.setCacheEnabled(cache);
|
|
296
300
|
// automatically accept dialog
|
|
297
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
|
+
}
|
|
298
305
|
yield dialog.accept();
|
|
299
306
|
}));
|
|
300
307
|
});
|
|
301
308
|
}
|
|
302
|
-
function autoDismissDialog(page, options = {}) {
|
|
303
|
-
var _a;
|
|
304
|
-
const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
|
|
305
|
-
page.on('dialog', (dialog) => __awaiter(this, void 0, void 0, function* () {
|
|
306
|
-
if (config.verbose) {
|
|
307
|
-
core_1.info.lowLevel(`Browser dialog: ${dialog.message()}`);
|
|
308
|
-
}
|
|
309
|
-
yield dialog.dismiss();
|
|
310
|
-
}));
|
|
311
|
-
}
|
|
312
309
|
function initBrowserInfoInConfig(browser, options = {}) {
|
|
313
310
|
var _a;
|
|
314
311
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -351,7 +348,6 @@ function testInBrowser(options = {}) {
|
|
|
351
348
|
const visitPlan = testPlanner.getVisitPlan();
|
|
352
349
|
// setup page configuration
|
|
353
350
|
config.setDevice(visitPlan.device);
|
|
354
|
-
autoDismissDialog(page);
|
|
355
351
|
yield initBrowserInfoInConfig(browser);
|
|
356
352
|
core_1.browserInfo.monitorWebConsole(page);
|
|
357
353
|
yield setupPage(page, options);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EBasicAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EBasicAnalysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EDetachedDOMAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EDetachedDOMAnalysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EDuplicateObjectAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EDuplicateObjectAnalysis.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EFindLeaks.example.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EFindLeaks.example.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EFindMemoryLeaks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EFindMemoryLeaks.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -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);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EFindMemoryLeaksWithSnapshotFiles.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EFindMemoryLeaksWithSnapshotFiles.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -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);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EInjectLeaksWithSetup.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EInjectLeaksWithSetup.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EReloadInSetup.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EReloadInSetup.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EResultReader.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EResultReader.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -26,6 +26,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
26
26
|
const BrowserInteractionResultReader_1 = __importDefault(require("../../result-reader/BrowserInteractionResultReader"));
|
|
27
27
|
const index_1 = require("../../index");
|
|
28
28
|
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
29
|
+
const SnapshotResultReader_1 = __importDefault(require("../../result-reader/SnapshotResultReader"));
|
|
29
30
|
beforeEach(E2ETestSettings_1.testSetup);
|
|
30
31
|
function inject() {
|
|
31
32
|
// @ts-ignore
|
|
@@ -70,3 +71,38 @@ test('ResultReader.from is working as expected', () => __awaiter(void 0, void 0,
|
|
|
70
71
|
});
|
|
71
72
|
checkResultReader(BrowserInteractionResultReader_1.default.from(result.getRootDirectory()));
|
|
72
73
|
}), E2ETestSettings_1.testTimeout);
|
|
74
|
+
function injectDetachedDOMElements() {
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
window.injectHookForLink4 = () => {
|
|
77
|
+
class TestObject {
|
|
78
|
+
}
|
|
79
|
+
const arr = [];
|
|
80
|
+
for (let i = 0; i < 23; ++i) {
|
|
81
|
+
arr.push(document.createElement('div'));
|
|
82
|
+
}
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
window.__injectedValue = arr;
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
window._path_1 = { x: { y: document.createElement('div') } };
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
window._path_2 = new Set([document.createElement('div')]);
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
window._randomObject = [new TestObject()];
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
test('SnapshotResultReader is working as expected', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
94
|
+
const result = yield (0, index_1.warmupAndTakeSnapshots)({
|
|
95
|
+
scenario: E2ETestSettings_1.scenario,
|
|
96
|
+
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
97
|
+
});
|
|
98
|
+
const snapshotFiles = result.getSnapshotFiles();
|
|
99
|
+
expect(snapshotFiles.length).toBe(3);
|
|
100
|
+
const reader = SnapshotResultReader_1.default.fromSnapshots(snapshotFiles[0], snapshotFiles[1], snapshotFiles[2]);
|
|
101
|
+
const leaks = yield (0, index_1.findLeaks)(reader);
|
|
102
|
+
// detected all different leak trace cluster
|
|
103
|
+
expect(leaks.length >= 1).toBe(true);
|
|
104
|
+
// expect all traces are found
|
|
105
|
+
expect(leaks.some(leak => JSON.stringify(leak).includes('__injectedValue')));
|
|
106
|
+
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_1')));
|
|
107
|
+
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_2')));
|
|
108
|
+
}), E2ETestSettings_1.testTimeout);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2ERunMultipleSnapshots.example.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2ERunMultipleSnapshots.example.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2ERunSingleSnapshot.example.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2ERunSingleSnapshot.example.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EShapeUnboundGrowthAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EStringAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EStringAnalysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2ETestScenarioCallback.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2ETestScenarioCallback.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
const index_1 = require("../../index");
|
|
22
|
+
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
23
|
+
beforeEach(E2ETestSettings_1.testSetup);
|
|
24
|
+
function injectDetachedDOMElements() {
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
window.injectHookForLink4 = () => {
|
|
27
|
+
class TestObject {
|
|
28
|
+
}
|
|
29
|
+
const arr = [];
|
|
30
|
+
for (let i = 0; i < 23; ++i) {
|
|
31
|
+
arr.push(document.createElement('div'));
|
|
32
|
+
}
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
window.__injectedValue = arr;
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
window._path_1 = { x: { y: document.createElement('div') } };
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
window._path_2 = new Set([document.createElement('div')]);
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
window._randomObject = [new TestObject()];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
test('callbacks in test scenarios are called in the right order', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
|
+
const actualCalls = [];
|
|
45
|
+
// define a test scenario with all callbacks offered by memlab
|
|
46
|
+
const selfDefinedScenario = {
|
|
47
|
+
app: () => {
|
|
48
|
+
actualCalls.push('app');
|
|
49
|
+
return 'test-spa';
|
|
50
|
+
},
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
52
|
+
beforeInitialPageLoad: (_page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
actualCalls.push('beforeInitialPageLoad');
|
|
54
|
+
}),
|
|
55
|
+
cookies: () => {
|
|
56
|
+
actualCalls.push('cookies');
|
|
57
|
+
return [];
|
|
58
|
+
},
|
|
59
|
+
repeat: () => {
|
|
60
|
+
actualCalls.push('repeat');
|
|
61
|
+
return 0;
|
|
62
|
+
},
|
|
63
|
+
isPageLoaded: (page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
|
+
actualCalls.push('isPageLoaded');
|
|
65
|
+
yield page.waitForNavigation({
|
|
66
|
+
// consider navigation to be finished when there are
|
|
67
|
+
// no more than 2 network connections for at least 500 ms.
|
|
68
|
+
waitUntil: 'networkidle2',
|
|
69
|
+
// Maximum navigation time in milliseconds
|
|
70
|
+
timeout: 5000,
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}),
|
|
74
|
+
url: () => {
|
|
75
|
+
actualCalls.push('url');
|
|
76
|
+
return '';
|
|
77
|
+
},
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
79
|
+
setup: (_page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
80
|
+
actualCalls.push('setup');
|
|
81
|
+
}),
|
|
82
|
+
action: (page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
+
actualCalls.push('action');
|
|
84
|
+
yield page.click('[data-testid="link-4"]');
|
|
85
|
+
}),
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
87
|
+
back: (_page) => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
actualCalls.push('back');
|
|
89
|
+
}),
|
|
90
|
+
beforeLeakFilter: (
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
92
|
+
_snapshot,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
94
|
+
_leakedNodeIds) => {
|
|
95
|
+
actualCalls.push('beforeLeakFilter');
|
|
96
|
+
},
|
|
97
|
+
leakFilter: (node) => {
|
|
98
|
+
actualCalls.push('leakFilter');
|
|
99
|
+
return node.name === 'TestObject' && node.type === 'object';
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
// run test scenario
|
|
103
|
+
yield (0, index_1.run)({
|
|
104
|
+
scenario: selfDefinedScenario,
|
|
105
|
+
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
106
|
+
skipWarmup: true,
|
|
107
|
+
});
|
|
108
|
+
// squash all leakFilter call into a single identifier
|
|
109
|
+
let normalizedCalls = squash(actualCalls, ['leakFilter', 'isPageLoaded']);
|
|
110
|
+
normalizedCalls = normalizedCalls.slice(normalizedCalls.lastIndexOf('url'));
|
|
111
|
+
// expect all callbacks are called in the right order
|
|
112
|
+
expect(normalizedCalls).toEqual([
|
|
113
|
+
// the first 4 calls (app, cookies, repeat, url) can be in any order
|
|
114
|
+
'url',
|
|
115
|
+
'repeat',
|
|
116
|
+
'app',
|
|
117
|
+
'cookies',
|
|
118
|
+
// the following calls must be in the exact order listed
|
|
119
|
+
'beforeInitialPageLoad',
|
|
120
|
+
'isPageLoaded',
|
|
121
|
+
'setup',
|
|
122
|
+
'action',
|
|
123
|
+
'isPageLoaded',
|
|
124
|
+
'back',
|
|
125
|
+
'isPageLoaded',
|
|
126
|
+
'beforeLeakFilter',
|
|
127
|
+
'leakFilter',
|
|
128
|
+
]);
|
|
129
|
+
}), E2ETestSettings_1.testTimeout);
|
|
130
|
+
// Squashes consecutive occurrences of a specified element in
|
|
131
|
+
// an array into a single occurrence of that element
|
|
132
|
+
function squash(arr, elementsToSquash) {
|
|
133
|
+
const squashSet = new Set(elementsToSquash);
|
|
134
|
+
return arr.filter((value, index) => !squashSet.has(value) || value !== arr[index - 1]);
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2ETestSettings.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/API/lib/E2ETestSettings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,WAAW,QAAgB,CAAC;AAEzC,eAAO,MAAM,mBAAmB;;;;CAAkB,CAAC;AAEnD,eAAO,MAAM,QAAQ;eACV,MAAM;eACN,MAAM;mBACM,IAAI,KAAG,QAAQ,IAAI,CAAC;CAE1C,CAAC;AAEF,eAAO,MAAM,SAAS,QAAO,IAK5B,CAAC;AAGF,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EHeapParser.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/heap/E2EHeapParser.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,QAAQ,EAA2B,MAAM,cAAc,CAAC;AAKrE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,QAAQ,EAAE,QAAQ,CAAC;KACpB;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-1.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-1.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-2.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-2.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-3.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-3.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-4.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-4.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-5.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-5.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-6.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-6.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example-7.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/examples/example-7.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeapParserTestUtils.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/lib/HeapParserTestUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAUhD,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,IAAI,EACxB,eAAe,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,OAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -69,6 +69,7 @@ function saveSnapshotToFile(page, file) {
|
|
|
69
69
|
const TEST_URL = 'about:blank';
|
|
70
70
|
function dumpHeap(snapshotFile, leakInjector) {
|
|
71
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
core_1.utils.tryToMutePuppeteerWarning();
|
|
72
73
|
const browser = yield puppeteer.launch(core_1.config.puppeteerConfig);
|
|
73
74
|
const page = yield browser.newPage();
|
|
74
75
|
// set page size
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heap-analysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/packages/heap-analysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function registerPackage(): Promise<void>;
|
|
|
12
12
|
export * from './API';
|
|
13
13
|
export * from '@memlab/heap-analysis';
|
|
14
14
|
export { default as BrowserInteractionResultReader } from './result-reader/BrowserInteractionResultReader';
|
|
15
|
+
export { default as SnapshotResultReader } from './result-reader/SnapshotResultReader';
|
|
15
16
|
export { dumpNodeHeapSnapshot, getNodeInnocentHeap, takeNodeMinimalHeap, } from '@memlab/core';
|
|
16
17
|
/** @internal */
|
|
17
18
|
export { config } from '@memlab/core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,gBAAgB;AAChB,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,cAAc,OAAO,CAAC;AACtB,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAC,OAAO,IAAI,8BAA8B,EAAC,MAAM,gDAAgD,CAAC;AACzG,OAAO,EAAC,OAAO,IAAI,oBAAoB,EAAC,MAAM,sCAAsC,CAAC;AACrF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,gBAAgB;AAChB,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.config = exports.takeNodeMinimalHeap = exports.getNodeInnocentHeap = exports.dumpNodeHeapSnapshot = exports.BrowserInteractionResultReader = exports.registerPackage = void 0;
|
|
38
|
+
exports.config = exports.takeNodeMinimalHeap = exports.getNodeInnocentHeap = exports.dumpNodeHeapSnapshot = exports.SnapshotResultReader = exports.BrowserInteractionResultReader = exports.registerPackage = void 0;
|
|
39
39
|
const path_1 = __importDefault(require("path"));
|
|
40
40
|
const core_1 = require("@memlab/core");
|
|
41
41
|
/** @internal */
|
|
@@ -49,6 +49,8 @@ __exportStar(require("./API"), exports);
|
|
|
49
49
|
__exportStar(require("@memlab/heap-analysis"), exports);
|
|
50
50
|
var BrowserInteractionResultReader_1 = require("./result-reader/BrowserInteractionResultReader");
|
|
51
51
|
Object.defineProperty(exports, "BrowserInteractionResultReader", { enumerable: true, get: function () { return __importDefault(BrowserInteractionResultReader_1).default; } });
|
|
52
|
+
var SnapshotResultReader_1 = require("./result-reader/SnapshotResultReader");
|
|
53
|
+
Object.defineProperty(exports, "SnapshotResultReader", { enumerable: true, get: function () { return __importDefault(SnapshotResultReader_1).default; } });
|
|
52
54
|
var core_2 = require("@memlab/core");
|
|
53
55
|
Object.defineProperty(exports, "dumpNodeHeapSnapshot", { enumerable: true, get: function () { return core_2.dumpNodeHeapSnapshot; } });
|
|
54
56
|
Object.defineProperty(exports, "getNodeInnocentHeap", { enumerable: true, get: function () { return core_2.getNodeInnocentHeap; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"APIUtils.d.ts","sourceRoot":"","sources":["../../src/lib/APIUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,cAAc,CAAC;AAU/C,iBAAe,UAAU,CACvB,OAAO,GAAE;IAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAM,GACtD,OAAO,CAAC,OAAO,CAAC,CAoBlB;;;;AAED,wBAEE"}
|
package/dist/lib/APIUtils.js
CHANGED
|
@@ -29,6 +29,7 @@ function getBrowser(options = {}) {
|
|
|
29
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30
30
|
const runConfig = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
|
|
31
31
|
let browser;
|
|
32
|
+
core_1.utils.tryToMutePuppeteerWarning();
|
|
32
33
|
if (runConfig.isLocalPuppeteer && !options.warmup) {
|
|
33
34
|
try {
|
|
34
35
|
browser = yield puppeteer.connect(Object.assign({ browserURL: `http://localhost:${runConfig.localBrowserPort}` }, runConfig.puppeteerConfig));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseResultReader.d.ts","sourceRoot":"","sources":["../../src/result-reader/BaseResultReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAQ,WAAW,EAAC,MAAM,cAAc,CAAC;AAGhD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC;IACnC,OAAO,CAAC,OAAO,CAAU;IAEzB;;;;OAIG;IACH,SAAS,aAAa,OAAO,SAAK;IASlC,SAAS,CAAC,KAAK,IAAI,IAAI;IAOvB;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,SAAK,GAAG,gBAAgB;IAI3C;;;;;;;;;;;;;;;;;OAiBG;IACI,gBAAgB,IAAI,MAAM;IAKjC;;;;;;;;;;;;;;;OAeG;IACI,OAAO,IAAI,IAAI;CAOvB"}
|
|
@@ -12,7 +12,7 @@ import BaseResultReader from './BaseResultReader';
|
|
|
12
12
|
/**
|
|
13
13
|
* A utility entity to read all generated files from
|
|
14
14
|
* the directory holding the data and results from the
|
|
15
|
-
* last browser interaction run
|
|
15
|
+
* last MemLab browser interaction run
|
|
16
16
|
*/
|
|
17
17
|
export default class BrowserInteractionResultReader extends BaseResultReader {
|
|
18
18
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserInteractionResultReader.d.ts","sourceRoot":"","sources":["../../src/result-reader/BrowserInteractionResultReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAE,WAAW,EAAC,MAAM,cAAc,CAAC;AAK3D,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAElD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,8BAA+B,SAAQ,gBAAgB;IAC1E;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,SAAK,GAAG,8BAA8B;IAIzD;;;;;;;;;;;;;;;OAeG;IACI,gBAAgB,IAAI,MAAM,EAAE;IASnC;;;;;;;;;;;;;;;OAeG;IACI,kBAAkB,IAAI,MAAM;IAKnC;;;;;;;;;;;;;;;;OAgBG;IACI,mBAAmB,IAAI,WAAW,EAAE;IAQ3C;;;;;;;;;;;;;;;;OAgBG;IACI,cAAc,IAAI,WAAW;CAMrC"}
|
|
@@ -19,7 +19,7 @@ const BaseResultReader_1 = __importDefault(require("./BaseResultReader"));
|
|
|
19
19
|
/**
|
|
20
20
|
* A utility entity to read all generated files from
|
|
21
21
|
* the directory holding the data and results from the
|
|
22
|
-
* last browser interaction run
|
|
22
|
+
* last MemLab browser interaction run
|
|
23
23
|
*/
|
|
24
24
|
class BrowserInteractionResultReader extends BaseResultReader_1.default {
|
|
25
25
|
/**
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
import { E2EStepInfo, RunMetaInfo } from '@memlab/core';
|
|
11
|
+
import BaseResultReader from './BaseResultReader';
|
|
12
|
+
/**
|
|
13
|
+
* A utility entity to read all MemLab files generated from
|
|
14
|
+
* baseline, target and final heap snapshots.
|
|
15
|
+
*
|
|
16
|
+
* The most useful feature of this class is when you have
|
|
17
|
+
* three separate snapshots (baseline, target, and final)
|
|
18
|
+
* that are not taken from MemLab, but you still would
|
|
19
|
+
* like to use the `findLeaks` to detect memory leaks:
|
|
20
|
+
*
|
|
21
|
+
* ```javascript
|
|
22
|
+
* const {SnapshotResultReader, findLeaks} = require('@memlab/api');
|
|
23
|
+
*
|
|
24
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
25
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
26
|
+
* const leaks = await findLeaks(reader);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export default class SnapshotResultReader extends BaseResultReader {
|
|
30
|
+
private baselineSnapshot;
|
|
31
|
+
private targetSnapshot;
|
|
32
|
+
private finalSnapshot;
|
|
33
|
+
/**
|
|
34
|
+
* build a result reader
|
|
35
|
+
* @param workDir absolute path of the directory where the data
|
|
36
|
+
* and generated files of the memlab run were stored
|
|
37
|
+
*/
|
|
38
|
+
protected constructor(baselineSnapshot: string, targetSnapshot: string, finalSnapshot: string);
|
|
39
|
+
private createMetaFilesOnDisk;
|
|
40
|
+
private checkSnapshotFiles;
|
|
41
|
+
/**
|
|
42
|
+
* Build a result reader from baseline, target, and final heap snapshot files.
|
|
43
|
+
* The three snapshot files do not have to be in the same directory.
|
|
44
|
+
* @param baselineSnapshot file path of the baseline heap snapshot
|
|
45
|
+
* @param targetSnapshot file path of the target heap snapshot
|
|
46
|
+
* @param finalSnapshot file path of the final heap snapshot
|
|
47
|
+
* @returns the ResultReader instance
|
|
48
|
+
*
|
|
49
|
+
* * **Examples**:
|
|
50
|
+
* ```javascript
|
|
51
|
+
* const {SnapshotResultReader, findLeaks} = require('@memlab/api');
|
|
52
|
+
*
|
|
53
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
54
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
55
|
+
* const leaks = await findLeaks(reader);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
static fromSnapshots(baselineSnapshot: string, targetSnapshot: string, finalSnapshot: string): SnapshotResultReader;
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
static from(workDir?: string): BaseResultReader;
|
|
63
|
+
/**
|
|
64
|
+
* get all snapshot files related to this SnapshotResultReader
|
|
65
|
+
* @returns an array of snapshot file's absolute path
|
|
66
|
+
*
|
|
67
|
+
* * **Examples**:
|
|
68
|
+
* ```javascript
|
|
69
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
70
|
+
*
|
|
71
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
72
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
73
|
+
* const paths = reader.getSnapshotFiles();
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
getSnapshotFiles(): string[];
|
|
77
|
+
/**
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
getSnapshotFileDir(): string;
|
|
81
|
+
/**
|
|
82
|
+
* browser interaction step sequence
|
|
83
|
+
* @returns an array of browser interaction step information
|
|
84
|
+
*
|
|
85
|
+
* * **Examples**:
|
|
86
|
+
* ```javascript
|
|
87
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
88
|
+
*
|
|
89
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
90
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
91
|
+
* const paths = reader.getInteractionSteps();
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
getInteractionSteps(): E2EStepInfo[];
|
|
95
|
+
/**
|
|
96
|
+
* @internal
|
|
97
|
+
* general meta data of the browser interaction run
|
|
98
|
+
* @returns meta data about the entire browser interaction
|
|
99
|
+
*
|
|
100
|
+
* * **Examples**:
|
|
101
|
+
* ```javascript
|
|
102
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
103
|
+
*
|
|
104
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
105
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
106
|
+
* const metaInfo = reader.getRunMetaInfo();
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
getRunMetaInfo(): RunMetaInfo;
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
cleanup(): void;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=SnapshotResultReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SnapshotResultReader.d.ts","sourceRoot":"","sources":["../../src/result-reader/SnapshotResultReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAS,WAAW,EAAE,WAAW,EAAC,MAAM,cAAc,CAAC;AAI9D,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,gBAAgB;IAChE,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;OAIG;IACH,SAAS,aACP,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM;IAavB,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,kBAAkB;IAY1B;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,aAAa,CAClB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACpB,oBAAoB;IAQvB;;OAEG;WACW,IAAI,CAAC,OAAO,SAAK,GAAG,gBAAgB;IAKlD;;;;;;;;;;;;OAYG;IACI,gBAAgB,IAAI,MAAM,EAAE;IAInC;;OAEG;IACI,kBAAkB,IAAI,MAAM;IAOnC;;;;;;;;;;;;OAYG;IACI,mBAAmB,IAAI,WAAW,EAAE;IAQ3C;;;;;;;;;;;;;OAaG;IACI,cAAc,IAAI,WAAW;IAIpC;;OAEG;IACI,OAAO,IAAI,IAAI;CAGvB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const core_1 = require("@memlab/core");
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const core_2 = require("@memlab/core");
|
|
18
|
+
const BaseResultReader_1 = __importDefault(require("./BaseResultReader"));
|
|
19
|
+
/**
|
|
20
|
+
* A utility entity to read all MemLab files generated from
|
|
21
|
+
* baseline, target and final heap snapshots.
|
|
22
|
+
*
|
|
23
|
+
* The most useful feature of this class is when you have
|
|
24
|
+
* three separate snapshots (baseline, target, and final)
|
|
25
|
+
* that are not taken from MemLab, but you still would
|
|
26
|
+
* like to use the `findLeaks` to detect memory leaks:
|
|
27
|
+
*
|
|
28
|
+
* ```javascript
|
|
29
|
+
* const {SnapshotResultReader, findLeaks} = require('@memlab/api');
|
|
30
|
+
*
|
|
31
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
32
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
33
|
+
* const leaks = await findLeaks(reader);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
class SnapshotResultReader extends BaseResultReader_1.default {
|
|
37
|
+
/**
|
|
38
|
+
* build a result reader
|
|
39
|
+
* @param workDir absolute path of the directory where the data
|
|
40
|
+
* and generated files of the memlab run were stored
|
|
41
|
+
*/
|
|
42
|
+
constructor(baselineSnapshot, targetSnapshot, finalSnapshot) {
|
|
43
|
+
const fileManager = new core_2.FileManager();
|
|
44
|
+
const workDir = fileManager.generateTmpHeapDir();
|
|
45
|
+
fs_extra_1.default.ensureDirSync(workDir);
|
|
46
|
+
super(workDir);
|
|
47
|
+
this.baselineSnapshot = baselineSnapshot;
|
|
48
|
+
this.targetSnapshot = targetSnapshot;
|
|
49
|
+
this.finalSnapshot = finalSnapshot;
|
|
50
|
+
this.checkSnapshotFiles();
|
|
51
|
+
this.createMetaFilesOnDisk(fileManager, workDir);
|
|
52
|
+
}
|
|
53
|
+
createMetaFilesOnDisk(fileManager, workDir) {
|
|
54
|
+
fileManager.initDirs(core_1.config, { workDir });
|
|
55
|
+
const visitOrder = this.getInteractionSteps();
|
|
56
|
+
const snapSeqFile = fileManager.getSnapshotSequenceMetaFile({ workDir });
|
|
57
|
+
fs_extra_1.default.writeFileSync(snapSeqFile, JSON.stringify(visitOrder, null, 2), 'UTF-8');
|
|
58
|
+
}
|
|
59
|
+
checkSnapshotFiles() {
|
|
60
|
+
if (!fs_extra_1.default.existsSync(this.baselineSnapshot) ||
|
|
61
|
+
!fs_extra_1.default.existsSync(this.targetSnapshot) ||
|
|
62
|
+
!fs_extra_1.default.existsSync(this.finalSnapshot)) {
|
|
63
|
+
throw core_2.utils.haltOrThrow('invalid file path of baseline, target, or final heap snapshots');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a result reader from baseline, target, and final heap snapshot files.
|
|
68
|
+
* The three snapshot files do not have to be in the same directory.
|
|
69
|
+
* @param baselineSnapshot file path of the baseline heap snapshot
|
|
70
|
+
* @param targetSnapshot file path of the target heap snapshot
|
|
71
|
+
* @param finalSnapshot file path of the final heap snapshot
|
|
72
|
+
* @returns the ResultReader instance
|
|
73
|
+
*
|
|
74
|
+
* * **Examples**:
|
|
75
|
+
* ```javascript
|
|
76
|
+
* const {SnapshotResultReader, findLeaks} = require('@memlab/api');
|
|
77
|
+
*
|
|
78
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
79
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
80
|
+
* const leaks = await findLeaks(reader);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
static fromSnapshots(baselineSnapshot, targetSnapshot, finalSnapshot) {
|
|
84
|
+
return new SnapshotResultReader(baselineSnapshot, targetSnapshot, finalSnapshot);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
static from(workDir = '') {
|
|
90
|
+
throw core_2.utils.haltOrThrow('SnapshotResultReader.from is not supported');
|
|
91
|
+
return new BaseResultReader_1.default(workDir);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* get all snapshot files related to this SnapshotResultReader
|
|
95
|
+
* @returns an array of snapshot file's absolute path
|
|
96
|
+
*
|
|
97
|
+
* * **Examples**:
|
|
98
|
+
* ```javascript
|
|
99
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
100
|
+
*
|
|
101
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
102
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
103
|
+
* const paths = reader.getSnapshotFiles();
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
getSnapshotFiles() {
|
|
107
|
+
return [this.baselineSnapshot, this.targetSnapshot, this.finalSnapshot];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
getSnapshotFileDir() {
|
|
113
|
+
throw core_2.utils.haltOrThrow('SnapshotResultReader getSnapshotFileDir() method is not supported');
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* browser interaction step sequence
|
|
118
|
+
* @returns an array of browser interaction step information
|
|
119
|
+
*
|
|
120
|
+
* * **Examples**:
|
|
121
|
+
* ```javascript
|
|
122
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
123
|
+
*
|
|
124
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
125
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
126
|
+
* const paths = reader.getInteractionSteps();
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
getInteractionSteps() {
|
|
130
|
+
return this.fileManager.createVisitOrderWithSnapshots(this.baselineSnapshot, this.targetSnapshot, this.finalSnapshot);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* @internal
|
|
134
|
+
* general meta data of the browser interaction run
|
|
135
|
+
* @returns meta data about the entire browser interaction
|
|
136
|
+
*
|
|
137
|
+
* * **Examples**:
|
|
138
|
+
* ```javascript
|
|
139
|
+
* const {SnapshotResultReader} = require('@memlab/api');
|
|
140
|
+
*
|
|
141
|
+
* // baseline, target, and final are file paths of heap snapshot files
|
|
142
|
+
* const reader = SnapshotResultReader.fromSnapshots(baseline, target, final);
|
|
143
|
+
* const metaInfo = reader.getRunMetaInfo();
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
getRunMetaInfo() {
|
|
147
|
+
return new core_2.RunMetaInfoManager().loadRunMetaExternalTemplate();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
cleanup() {
|
|
153
|
+
// do nothing
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.default = SnapshotResultReader;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memlab/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "memlab API",
|
|
6
6
|
"author": "Liang Gong <lgong@fb.com>",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"chalk": "^4.0.0",
|
|
36
36
|
"fs-extra": "^4.0.2",
|
|
37
37
|
"minimist": "^1.2.0",
|
|
38
|
-
"puppeteer": "^
|
|
38
|
+
"puppeteer": "^21.0.3",
|
|
39
|
+
"puppeteer-core": "^21.0.3",
|
|
39
40
|
"string-width": "^4.2.0",
|
|
40
41
|
"util.promisify": "^1.1.1",
|
|
41
42
|
"xvfb": "^0.4.0"
|
|
@@ -46,8 +47,8 @@
|
|
|
46
47
|
"@types/minimist": "^1.2.2",
|
|
47
48
|
"@types/node": "^12.16.3",
|
|
48
49
|
"@types/puppeteer": "^5.4.4",
|
|
49
|
-
"jest": "^
|
|
50
|
-
"ts-jest": "^
|
|
50
|
+
"jest": "^29.6.2",
|
|
51
|
+
"ts-jest": "^29.1.1",
|
|
51
52
|
"typescript": "^4.6.3"
|
|
52
53
|
},
|
|
53
54
|
"repository": {
|
|
@@ -1,69 +0,0 @@
|
|
|
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
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
const index_1 = require("../../index");
|
|
22
|
-
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
23
|
-
beforeEach(E2ETestSettings_1.testSetup);
|
|
24
|
-
test('self-defined leak detector can find Web Worker TestObject (in initial load)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
-
const selfDefinedScenario = {
|
|
26
|
-
app: () => 'test-spa',
|
|
27
|
-
url: () => '',
|
|
28
|
-
leakFilter: (node) => {
|
|
29
|
-
return node.name === 'WorkerTestObject' && node.type === 'object';
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
const result = yield (0, index_1.run)({
|
|
33
|
-
scenario: selfDefinedScenario,
|
|
34
|
-
webWorker: null,
|
|
35
|
-
});
|
|
36
|
-
// detected all different leak trace cluster
|
|
37
|
-
expect(result.leaks.length).toBe(1);
|
|
38
|
-
}), E2ETestSettings_1.testTimeout);
|
|
39
|
-
test('self-defined leak detector can find Web Worker TestObject with specified worker name (in initial load)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
-
const selfDefinedScenario = {
|
|
41
|
-
app: () => 'test-spa',
|
|
42
|
-
url: () => '',
|
|
43
|
-
leakFilter: (node) => {
|
|
44
|
-
return node.name === 'WorkerTestObject' && node.type === 'object';
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
const result = yield (0, index_1.run)({
|
|
48
|
-
scenario: selfDefinedScenario,
|
|
49
|
-
webWorker: 'test-worker',
|
|
50
|
-
});
|
|
51
|
-
// detected all different leak trace cluster
|
|
52
|
-
expect(result.leaks.length).toBe(1);
|
|
53
|
-
}), E2ETestSettings_1.testTimeout);
|
|
54
|
-
test('self-defined leak detector can find Web Worker TestObject (during interaction)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
-
const selfDefinedScenario = {
|
|
56
|
-
app: () => 'test-spa',
|
|
57
|
-
url: () => '',
|
|
58
|
-
action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
|
|
59
|
-
leakFilter: (node) => {
|
|
60
|
-
return node.name === 'WorkerTestObject' && node.type === 'object';
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
const result = yield (0, index_1.run)({
|
|
64
|
-
scenario: selfDefinedScenario,
|
|
65
|
-
webWorker: null,
|
|
66
|
-
});
|
|
67
|
-
// detected all different leak trace cluster
|
|
68
|
-
expect(result.leaks.length).toBe(1);
|
|
69
|
-
}), E2ETestSettings_1.testTimeout);
|