@memlab/api 1.0.23 → 1.0.24
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 +6 -2
- 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/E2EFindMemoryLeaksWithSnapshotFiles.test.d.ts.map +1 -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;AAmED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAsD3E"}
|
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);
|
|
@@ -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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EFindMemoryLeaksWithSnapshotFiles.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EFindMemoryLeaksWithSnapshotFiles.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -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.24",
|
|
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);
|