@memlab/api 1.0.2 → 1.0.5
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/README.md +3 -2
- package/dist/API.d.ts +108 -19
- package/dist/API.js +89 -23
- package/dist/__tests__/API/E2EFindLeaks.example.js +1 -1
- package/dist/__tests__/API/E2EFindMemoryLeaks.test.js +35 -3
- package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.js +4 -2
- package/dist/__tests__/heap/examples/example-1.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-1.js +37 -0
- package/dist/__tests__/heap/examples/example-2.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-2.js +33 -0
- package/dist/__tests__/heap/examples/example-3.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-3.js +33 -0
- package/dist/__tests__/heap/examples/example-4.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-4.js +32 -0
- package/dist/__tests__/heap/examples/example-5.test.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-5.test.js +41 -0
- package/dist/__tests__/heap/examples/example-6.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-6.js +32 -0
- package/dist/__tests__/heap/examples/example-7.test.d.ts +11 -0
- package/dist/__tests__/heap/examples/example-7.test.js +36 -0
- package/dist/__tests__/packages/heap-analysis.test.d.ts +11 -0
- package/dist/__tests__/packages/heap-analysis.test.js +82 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +25 -3
- package/dist/result-reader/BaseResultReader.d.ts +30 -9
- package/dist/result-reader/BaseResultReader.js +30 -6
- package/dist/result-reader/BrowserInteractionResultReader.d.ts +64 -5
- package/dist/result-reader/BrowserInteractionResultReader.js +64 -5
- package/package.json +3 -2
- package/dist/API.d.ts.map +0 -1
- package/dist/__tests__/API/E2EBasicAnalysis.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2EDetachedDOMAnalysis.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2EDuplicateObjectAnalysis.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2EFindLeaks.example.d.ts.map +0 -1
- package/dist/__tests__/API/E2EFindMemoryLeaks.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2EResultReader.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2ERunMultipleSnapshots.example.d.ts.map +0 -1
- package/dist/__tests__/API/E2ERunSingleSnapshot.example.d.ts.map +0 -1
- package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.d.ts.map +0 -1
- package/dist/__tests__/API/E2EStringAnalysis.test.d.ts.map +0 -1
- package/dist/__tests__/API/lib/E2ETestSettings.d.ts.map +0 -1
- package/dist/__tests__/heap/E2EHeapParser.test.d.ts.map +0 -1
- package/dist/__tests__/heap/lib/HeapParserTestUtils.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/APIUtils.d.ts.map +0 -1
- package/dist/result-reader/BaseResultReader.d.ts.map +0 -1
- package/dist/result-reader/BrowserInteractionResultReader.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
This is the memlab API library. Add this dependency to your project
|
|
4
4
|
if you want to integrate memlab with your continuous testing system.
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
https://facebookincubator.github.io/memlab
|
|
6
|
+
## Online Resources
|
|
7
|
+
* [Official Website and Demo](https://facebookincubator.github.io/memlab)
|
|
8
|
+
* [Documentation](https://facebookincubator.github.io/memlab/docs/intro)
|
package/dist/API.d.ts
CHANGED
|
@@ -8,20 +8,26 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
import type { ParsedArgs } from 'minimist';
|
|
11
|
-
import type { AnyFunction,
|
|
11
|
+
import type { AnyFunction, ISerializedInfo, IScenario } from '@memlab/core';
|
|
12
12
|
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
16
|
/**
|
|
17
|
-
* Options for configuring browser interaction run
|
|
17
|
+
* Options for configuring browser interaction run, all fields are optional
|
|
18
18
|
*/
|
|
19
19
|
export declare type RunOptions = {
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* test scenario specifying how to interact with browser
|
|
22
|
+
* (for more details view {@link IScenario})
|
|
23
|
+
*/
|
|
21
24
|
scenario?: IScenario;
|
|
22
|
-
/** cookies file */
|
|
25
|
+
/** the absolute path of cookies file */
|
|
23
26
|
cookiesFile?: string;
|
|
24
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* function to be evaluated in browser context after
|
|
29
|
+
* the web page initial load
|
|
30
|
+
*/
|
|
25
31
|
evalInBrowserAfterInitLoad?: AnyFunction;
|
|
26
32
|
/**
|
|
27
33
|
* if true, take heap snapshot for each interaction step,
|
|
@@ -29,10 +35,33 @@ export declare type RunOptions = {
|
|
|
29
35
|
* which steps it will take heap snapshots
|
|
30
36
|
*/
|
|
31
37
|
snapshotForEachStep?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* specify the working directory where you want memlab to dump
|
|
40
|
+
* heap snapshots and other meta data of the test run. If no
|
|
41
|
+
* working directory is provided, memlab will generate a random
|
|
42
|
+
* temp directory under the operating system's default directory
|
|
43
|
+
* for temporary files.
|
|
44
|
+
* Note: It's the caller's responsibility to make sure the
|
|
45
|
+
* specified working directory exists.
|
|
46
|
+
*/
|
|
47
|
+
workDir?: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A data structure holding the result of the {@link run} API call.
|
|
51
|
+
*/
|
|
52
|
+
export declare type RunResult = {
|
|
53
|
+
/**
|
|
54
|
+
* leak traces detected and clustered from the browser interaction
|
|
55
|
+
*/
|
|
56
|
+
leaks: ISerializedInfo[];
|
|
57
|
+
/**
|
|
58
|
+
* a utility for reading browser interaction results from disk
|
|
59
|
+
*/
|
|
60
|
+
runResult: BrowserInteractionResultReader;
|
|
32
61
|
};
|
|
33
62
|
/**
|
|
34
|
-
* @ignore
|
|
35
63
|
* Options for memlab inter-package API calls
|
|
64
|
+
* @internal
|
|
36
65
|
*/
|
|
37
66
|
export declare type APIOptions = {
|
|
38
67
|
testPlanner?: TestPlanner;
|
|
@@ -43,29 +72,62 @@ export declare type APIOptions = {
|
|
|
43
72
|
/**
|
|
44
73
|
* This API warms up web server, runs E2E interaction, and takes heap snapshots.
|
|
45
74
|
* This is equivalent to run `memlab warmup-and-snapshot` in CLI.
|
|
46
|
-
* This is also equivalent to
|
|
75
|
+
* This is also equivalent to warm up and call {@link takeSnapshots}.
|
|
47
76
|
*
|
|
48
77
|
* @param options configure browser interaction run
|
|
49
78
|
* @returns browser interaction results
|
|
79
|
+
* * **Examples**:
|
|
80
|
+
* ```javascript
|
|
81
|
+
* const {warmupAndTakeSnapshots} = require('@memlab/api');
|
|
82
|
+
*
|
|
83
|
+
* (async function () {
|
|
84
|
+
* const scenario = {
|
|
85
|
+
* url: () => 'https://www.facebook.com',
|
|
86
|
+
* };
|
|
87
|
+
* const result = await warmupAndTakeSnapshots({scenario});
|
|
88
|
+
* })();
|
|
89
|
+
* ```
|
|
50
90
|
*/
|
|
51
91
|
export declare function warmupAndTakeSnapshots(options?: RunOptions): Promise<BrowserInteractionResultReader>;
|
|
52
92
|
/**
|
|
53
93
|
* This API runs browser interaction and find memory leaks triggered in browser
|
|
54
94
|
* This is equivalent to run `memlab run` in CLI.
|
|
55
|
-
* This is also equivalent to
|
|
95
|
+
* This is also equivalent to warm up, and call {@link takeSnapshots}
|
|
56
96
|
* and {@link findLeaks}.
|
|
57
97
|
*
|
|
58
|
-
* @param
|
|
59
|
-
* @returns
|
|
60
|
-
*
|
|
98
|
+
* @param runOptions configure browser interaction run
|
|
99
|
+
* @returns memory leaks detected and a utility reading browser
|
|
100
|
+
* interaction results from disk
|
|
101
|
+
* * **Examples**:
|
|
102
|
+
* ```javascript
|
|
103
|
+
* const {run} = require('@memlab/api');
|
|
104
|
+
*
|
|
105
|
+
* (async function () {
|
|
106
|
+
* const scenario = {
|
|
107
|
+
* url: () => 'https://www.facebook.com',
|
|
108
|
+
* };
|
|
109
|
+
* const {leaks} = await run({scenario});
|
|
110
|
+
* })();
|
|
111
|
+
* ```
|
|
61
112
|
*/
|
|
62
|
-
export declare function run(
|
|
113
|
+
export declare function run(runOptions?: RunOptions): Promise<RunResult>;
|
|
63
114
|
/**
|
|
64
115
|
* This API runs E2E interaction and takes heap snapshots.
|
|
65
116
|
* This is equivalent to run `memlab snapshot` in CLI.
|
|
66
117
|
*
|
|
67
118
|
* @param options configure browser interaction run
|
|
68
|
-
* @returns browser interaction results
|
|
119
|
+
* @returns a utility reading browser interaction results from disk
|
|
120
|
+
* * **Examples**:
|
|
121
|
+
* ```javascript
|
|
122
|
+
* const {takeSnapshots} = require('@memlab/api');
|
|
123
|
+
*
|
|
124
|
+
* (async function () {
|
|
125
|
+
* const scenario = {
|
|
126
|
+
* url: () => 'https://www.facebook.com',
|
|
127
|
+
* };
|
|
128
|
+
* const result = await takeSnapshots({scenario});
|
|
129
|
+
* })();
|
|
130
|
+
* ```
|
|
69
131
|
*/
|
|
70
132
|
export declare function takeSnapshots(options?: RunOptions): Promise<BrowserInteractionResultReader>;
|
|
71
133
|
/**
|
|
@@ -73,8 +135,19 @@ export declare function takeSnapshots(options?: RunOptions): Promise<BrowserInte
|
|
|
73
135
|
* This is equivalent to `memlab find-leaks` in CLI.
|
|
74
136
|
*
|
|
75
137
|
* @param runResult return value of a browser interaction run
|
|
76
|
-
* @returns
|
|
77
|
-
*
|
|
138
|
+
* @returns leak traces detected and clustered from the browser interaction
|
|
139
|
+
* * **Examples**:
|
|
140
|
+
* ```javascript
|
|
141
|
+
* const {findLeaks, takeSnapshots} = require('@memlab/api');
|
|
142
|
+
*
|
|
143
|
+
* (async function () {
|
|
144
|
+
* const scenario = {
|
|
145
|
+
* url: () => 'https://www.facebook.com',
|
|
146
|
+
* };
|
|
147
|
+
* const result = await takeSnapshots({scenario});
|
|
148
|
+
* const leaks = findLeaks(result);
|
|
149
|
+
* })();
|
|
150
|
+
* ```
|
|
78
151
|
*/
|
|
79
152
|
export declare function findLeaks(runResult: BrowserInteractionResultReader): Promise<ISerializedInfo[]>;
|
|
80
153
|
/**
|
|
@@ -84,19 +157,35 @@ export declare function findLeaks(runResult: BrowserInteractionResultReader): Pr
|
|
|
84
157
|
* @param runResult return value of a browser interaction run
|
|
85
158
|
* @param heapAnalyzer instance of a heap analysis
|
|
86
159
|
* @param args other CLI arguments that needs to be passed to the heap analysis
|
|
87
|
-
* @returns
|
|
160
|
+
* @returns each analysis may have a different return type, please check out
|
|
161
|
+
* the type definition or the documentation for the `process` method of the
|
|
162
|
+
* analysis class you are using for `heapAnalyzer`.
|
|
163
|
+
* * **Examples**:
|
|
164
|
+
* ```javascript
|
|
165
|
+
* const {takeSnapshots, StringAnalysis} = require('@memlab/api');
|
|
166
|
+
*
|
|
167
|
+
* (async function () {
|
|
168
|
+
* const scenario = {
|
|
169
|
+
* url: () => 'https://www.facebook.com',
|
|
170
|
+
* };
|
|
171
|
+
* const result = await takeSnapshots({scenario});
|
|
172
|
+
* const analysis = new StringAnalysis();
|
|
173
|
+
* await analyze(result, analysis);
|
|
174
|
+
* })();
|
|
175
|
+
* ```
|
|
88
176
|
*/
|
|
89
|
-
export declare function analyze(runResult: BrowserInteractionResultReader, heapAnalyzer: BaseAnalysis, args?: ParsedArgs): Promise<
|
|
177
|
+
export declare function analyze(runResult: BrowserInteractionResultReader, heapAnalyzer: BaseAnalysis, args?: ParsedArgs): Promise<void>;
|
|
90
178
|
/**
|
|
91
|
-
* @ignore
|
|
92
179
|
* This warms up web server by sending web requests to the web sever.
|
|
93
180
|
* This is equivalent to run `memlab warmup` in CLI.
|
|
181
|
+
* @internal
|
|
94
182
|
*
|
|
95
183
|
* @param options configure browser interaction run
|
|
96
184
|
*/
|
|
97
185
|
export declare function warmup(options?: APIOptions): Promise<void>;
|
|
98
186
|
/**
|
|
99
|
-
*
|
|
187
|
+
* Browser interaction API used by MemLab API and MemLab CLI
|
|
188
|
+
* @internal
|
|
100
189
|
*/
|
|
101
190
|
export declare function testInBrowser(options?: APIOptions): Promise<void>;
|
|
102
191
|
//# sourceMappingURL=API.d.ts.map
|
package/dist/API.js
CHANGED
|
@@ -29,15 +29,25 @@ const BrowserInteractionResultReader_1 = __importDefault(require("./result-reade
|
|
|
29
29
|
/**
|
|
30
30
|
* This API warms up web server, runs E2E interaction, and takes heap snapshots.
|
|
31
31
|
* This is equivalent to run `memlab warmup-and-snapshot` in CLI.
|
|
32
|
-
* This is also equivalent to
|
|
32
|
+
* This is also equivalent to warm up and call {@link takeSnapshots}.
|
|
33
33
|
*
|
|
34
34
|
* @param options configure browser interaction run
|
|
35
35
|
* @returns browser interaction results
|
|
36
|
+
* * **Examples**:
|
|
37
|
+
* ```javascript
|
|
38
|
+
* const {warmupAndTakeSnapshots} = require('@memlab/api');
|
|
39
|
+
*
|
|
40
|
+
* (async function () {
|
|
41
|
+
* const scenario = {
|
|
42
|
+
* url: () => 'https://www.facebook.com',
|
|
43
|
+
* };
|
|
44
|
+
* const result = await warmupAndTakeSnapshots({scenario});
|
|
45
|
+
* })();
|
|
46
|
+
* ```
|
|
36
47
|
*/
|
|
37
48
|
function warmupAndTakeSnapshots(options = {}) {
|
|
38
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
-
const config =
|
|
40
|
-
setConfigByRunOptions(config, options);
|
|
50
|
+
const config = getConfigFromRunOptions(options);
|
|
41
51
|
config.externalCookiesFile = options.cookiesFile;
|
|
42
52
|
config.scenario = options.scenario;
|
|
43
53
|
const testPlanner = new e2e_1.TestPlanner({ config });
|
|
@@ -51,25 +61,36 @@ exports.warmupAndTakeSnapshots = warmupAndTakeSnapshots;
|
|
|
51
61
|
/**
|
|
52
62
|
* This API runs browser interaction and find memory leaks triggered in browser
|
|
53
63
|
* This is equivalent to run `memlab run` in CLI.
|
|
54
|
-
* This is also equivalent to
|
|
64
|
+
* This is also equivalent to warm up, and call {@link takeSnapshots}
|
|
55
65
|
* and {@link findLeaks}.
|
|
56
66
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @returns
|
|
59
|
-
*
|
|
67
|
+
* @param runOptions configure browser interaction run
|
|
68
|
+
* @returns memory leaks detected and a utility reading browser
|
|
69
|
+
* interaction results from disk
|
|
70
|
+
* * **Examples**:
|
|
71
|
+
* ```javascript
|
|
72
|
+
* const {run} = require('@memlab/api');
|
|
73
|
+
*
|
|
74
|
+
* (async function () {
|
|
75
|
+
* const scenario = {
|
|
76
|
+
* url: () => 'https://www.facebook.com',
|
|
77
|
+
* };
|
|
78
|
+
* const {leaks} = await run({scenario});
|
|
79
|
+
* })();
|
|
80
|
+
* ```
|
|
60
81
|
*/
|
|
61
|
-
function run(
|
|
82
|
+
function run(runOptions = {}) {
|
|
62
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
const config =
|
|
64
|
-
|
|
65
|
-
config.
|
|
66
|
-
config.scenario = options.scenario;
|
|
84
|
+
const config = getConfigFromRunOptions(runOptions);
|
|
85
|
+
config.externalCookiesFile = runOptions.cookiesFile;
|
|
86
|
+
config.scenario = runOptions.scenario;
|
|
67
87
|
const testPlanner = new e2e_1.TestPlanner({ config });
|
|
68
|
-
const { evalInBrowserAfterInitLoad } =
|
|
88
|
+
const { evalInBrowserAfterInitLoad } = runOptions;
|
|
69
89
|
yield warmup({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
70
90
|
yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
71
91
|
const runResult = BrowserInteractionResultReader_1.default.from(config.workDir);
|
|
72
|
-
|
|
92
|
+
const leaks = yield findLeaks(runResult);
|
|
93
|
+
return { leaks, runResult };
|
|
73
94
|
});
|
|
74
95
|
}
|
|
75
96
|
exports.run = run;
|
|
@@ -78,12 +99,22 @@ exports.run = run;
|
|
|
78
99
|
* This is equivalent to run `memlab snapshot` in CLI.
|
|
79
100
|
*
|
|
80
101
|
* @param options configure browser interaction run
|
|
81
|
-
* @returns browser interaction results
|
|
102
|
+
* @returns a utility reading browser interaction results from disk
|
|
103
|
+
* * **Examples**:
|
|
104
|
+
* ```javascript
|
|
105
|
+
* const {takeSnapshots} = require('@memlab/api');
|
|
106
|
+
*
|
|
107
|
+
* (async function () {
|
|
108
|
+
* const scenario = {
|
|
109
|
+
* url: () => 'https://www.facebook.com',
|
|
110
|
+
* };
|
|
111
|
+
* const result = await takeSnapshots({scenario});
|
|
112
|
+
* })();
|
|
113
|
+
* ```
|
|
82
114
|
*/
|
|
83
115
|
function takeSnapshots(options = {}) {
|
|
84
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
-
const config =
|
|
86
|
-
setConfigByRunOptions(config, options);
|
|
117
|
+
const config = getConfigFromRunOptions(options);
|
|
87
118
|
config.externalCookiesFile = options.cookiesFile;
|
|
88
119
|
config.scenario = options.scenario;
|
|
89
120
|
const testPlanner = new e2e_1.TestPlanner();
|
|
@@ -98,8 +129,19 @@ exports.takeSnapshots = takeSnapshots;
|
|
|
98
129
|
* This is equivalent to `memlab find-leaks` in CLI.
|
|
99
130
|
*
|
|
100
131
|
* @param runResult return value of a browser interaction run
|
|
101
|
-
* @returns
|
|
102
|
-
*
|
|
132
|
+
* @returns leak traces detected and clustered from the browser interaction
|
|
133
|
+
* * **Examples**:
|
|
134
|
+
* ```javascript
|
|
135
|
+
* const {findLeaks, takeSnapshots} = require('@memlab/api');
|
|
136
|
+
*
|
|
137
|
+
* (async function () {
|
|
138
|
+
* const scenario = {
|
|
139
|
+
* url: () => 'https://www.facebook.com',
|
|
140
|
+
* };
|
|
141
|
+
* const result = await takeSnapshots({scenario});
|
|
142
|
+
* const leaks = findLeaks(result);
|
|
143
|
+
* })();
|
|
144
|
+
* ```
|
|
103
145
|
*/
|
|
104
146
|
function findLeaks(runResult) {
|
|
105
147
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -117,7 +159,22 @@ exports.findLeaks = findLeaks;
|
|
|
117
159
|
* @param runResult return value of a browser interaction run
|
|
118
160
|
* @param heapAnalyzer instance of a heap analysis
|
|
119
161
|
* @param args other CLI arguments that needs to be passed to the heap analysis
|
|
120
|
-
* @returns
|
|
162
|
+
* @returns each analysis may have a different return type, please check out
|
|
163
|
+
* the type definition or the documentation for the `process` method of the
|
|
164
|
+
* analysis class you are using for `heapAnalyzer`.
|
|
165
|
+
* * **Examples**:
|
|
166
|
+
* ```javascript
|
|
167
|
+
* const {takeSnapshots, StringAnalysis} = require('@memlab/api');
|
|
168
|
+
*
|
|
169
|
+
* (async function () {
|
|
170
|
+
* const scenario = {
|
|
171
|
+
* url: () => 'https://www.facebook.com',
|
|
172
|
+
* };
|
|
173
|
+
* const result = await takeSnapshots({scenario});
|
|
174
|
+
* const analysis = new StringAnalysis();
|
|
175
|
+
* await analyze(result, analysis);
|
|
176
|
+
* })();
|
|
177
|
+
* ```
|
|
121
178
|
*/
|
|
122
179
|
function analyze(runResult, heapAnalyzer, args = { _: [] }) {
|
|
123
180
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -128,9 +185,9 @@ function analyze(runResult, heapAnalyzer, args = { _: [] }) {
|
|
|
128
185
|
}
|
|
129
186
|
exports.analyze = analyze;
|
|
130
187
|
/**
|
|
131
|
-
* @ignore
|
|
132
188
|
* This warms up web server by sending web requests to the web sever.
|
|
133
189
|
* This is equivalent to run `memlab warmup` in CLI.
|
|
190
|
+
* @internal
|
|
134
191
|
*
|
|
135
192
|
* @param options configure browser interaction run
|
|
136
193
|
*/
|
|
@@ -174,8 +231,16 @@ function warmup(options = {}) {
|
|
|
174
231
|
});
|
|
175
232
|
}
|
|
176
233
|
exports.warmup = warmup;
|
|
177
|
-
function
|
|
234
|
+
function getConfigFromRunOptions(options) {
|
|
235
|
+
let config = core_1.MemLabConfig.getInstance();
|
|
236
|
+
if (options.workDir) {
|
|
237
|
+
core_1.fileManager.initDirs(config, { workDir: options.workDir });
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
config = core_1.MemLabConfig.resetConfigWithTranscientDir();
|
|
241
|
+
}
|
|
178
242
|
config.isFullRun = !!options.snapshotForEachStep;
|
|
243
|
+
return config;
|
|
179
244
|
}
|
|
180
245
|
function setupPage(page, options = {}) {
|
|
181
246
|
var _a, _b, _c;
|
|
@@ -221,7 +286,8 @@ function initBrowserInfoInConfig(browser, options = {}) {
|
|
|
221
286
|
});
|
|
222
287
|
}
|
|
223
288
|
/**
|
|
224
|
-
*
|
|
289
|
+
* Browser interaction API used by MemLab API and MemLab CLI
|
|
290
|
+
* @internal
|
|
225
291
|
*/
|
|
226
292
|
function testInBrowser(options = {}) {
|
|
227
293
|
var _a, _b;
|
|
@@ -43,7 +43,7 @@ function inject() {
|
|
|
43
43
|
}
|
|
44
44
|
function test() {
|
|
45
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
const leaks = yield (0, index_1.run)({ scenario, evalInBrowserAfterInitLoad: inject });
|
|
46
|
+
const { leaks } = yield (0, index_1.run)({ scenario, evalInBrowserAfterInitLoad: inject });
|
|
47
47
|
core_1.info.lowLevel(`${leaks.length}`);
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -17,14 +17,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
17
17
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
20
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
|
|
24
|
+
const os_1 = __importDefault(require("os"));
|
|
25
|
+
const path_1 = __importDefault(require("path"));
|
|
26
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
22
27
|
const index_1 = require("../../index");
|
|
23
28
|
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
24
29
|
beforeEach(E2ETestSettings_1.testSetup);
|
|
25
30
|
function injectDetachedDOMElements() {
|
|
26
31
|
// @ts-ignore
|
|
27
32
|
window.injectHookForLink4 = () => {
|
|
33
|
+
class TestObject {
|
|
34
|
+
}
|
|
28
35
|
const arr = [];
|
|
29
36
|
for (let i = 0; i < 23; ++i) {
|
|
30
37
|
arr.push(document.createElement('div'));
|
|
@@ -35,17 +42,42 @@ function injectDetachedDOMElements() {
|
|
|
35
42
|
window._path_1 = { x: { y: document.createElement('div') } };
|
|
36
43
|
// @ts-ignore
|
|
37
44
|
window._path_2 = new Set([document.createElement('div')]);
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
window._randomObject = [new TestObject()];
|
|
38
47
|
};
|
|
39
48
|
}
|
|
40
49
|
test('leak detector can find detached DOM elements', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
-
const leaks = yield (0, index_1.run)({
|
|
50
|
+
const { leaks } = yield (0, index_1.run)({
|
|
42
51
|
scenario: E2ETestSettings_1.scenario,
|
|
43
52
|
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
44
53
|
});
|
|
45
54
|
// detected all different leak trace cluster
|
|
46
|
-
expect(leaks.length).toBe(
|
|
55
|
+
expect(leaks.length >= 1).toBe(true);
|
|
47
56
|
// expect all traces are found
|
|
48
57
|
expect(leaks.some(leak => JSON.stringify(leak).includes('__injectedValue')));
|
|
49
58
|
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_1')));
|
|
50
59
|
expect(leaks.some(leak => JSON.stringify(leak).includes('_path_2')));
|
|
51
60
|
}), E2ETestSettings_1.testTimeout);
|
|
61
|
+
test('self-defined leak detector can find TestObject', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
const selfDefinedScenario = {
|
|
63
|
+
app: () => 'test-spa',
|
|
64
|
+
url: () => '',
|
|
65
|
+
action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
|
|
66
|
+
leakFilter: (node) => {
|
|
67
|
+
return node.name === 'TestObject' && node.type === 'object';
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
const workDir = path_1.default.join(os_1.default.tmpdir(), 'memlab-api-test', `${process.pid}`);
|
|
71
|
+
fs_extra_1.default.mkdirsSync(workDir);
|
|
72
|
+
const result = yield (0, index_1.run)({
|
|
73
|
+
scenario: selfDefinedScenario,
|
|
74
|
+
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
75
|
+
workDir,
|
|
76
|
+
});
|
|
77
|
+
// detected all different leak trace cluster
|
|
78
|
+
expect(result.leaks.length).toBe(1);
|
|
79
|
+
// expect all traces are found
|
|
80
|
+
expect(result.leaks.some(leak => JSON.stringify(leak).includes('_randomObject')));
|
|
81
|
+
const reader = result.runResult;
|
|
82
|
+
expect(path_1.default.resolve(reader.getRootDirectory())).toBe(path_1.default.resolve(workDir));
|
|
83
|
+
}), E2ETestSettings_1.testTimeout);
|
|
@@ -47,12 +47,14 @@ test('Shape unbound analysis works as expected', () => __awaiter(void 0, void 0,
|
|
|
47
47
|
});
|
|
48
48
|
// test analysis from auto loading
|
|
49
49
|
let analysis = new index_1.ShapeUnboundGrowthAnalysis();
|
|
50
|
-
|
|
50
|
+
yield analysis.run();
|
|
51
|
+
let shapeSummary = analysis.getShapesWithUnboundGrowth();
|
|
51
52
|
expect(shapeSummary.reduce((acc, summary) => acc || summary.shape.includes('LeakObject'), false)).toBe(true);
|
|
52
53
|
// test analysis from file
|
|
53
54
|
const snapshotDir = result.getSnapshotFileDir();
|
|
54
55
|
analysis = new index_1.ShapeUnboundGrowthAnalysis();
|
|
55
|
-
|
|
56
|
+
yield analysis.analyzeSnapshotsInDirectory(snapshotDir);
|
|
57
|
+
shapeSummary = analysis.getShapesWithUnboundGrowth();
|
|
56
58
|
expect(shapeSummary.some((summary) => summary.shape.includes('LeakObject'))).toBe(true);
|
|
57
59
|
// expect incorrect use of heap analysis to throw
|
|
58
60
|
const snapshotFile = result.getSnapshotFiles().pop();
|
|
@@ -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
|
+
* @emails oncall+ws_labs
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=example-1.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
* @emails oncall+ws_labs
|
|
9
|
+
* @format
|
|
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 core_1 = require("@memlab/core");
|
|
22
|
+
class TestObject {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.arr1 = [1, 2, 3];
|
|
25
|
+
this.arr2 = ['1', '2', '3'];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
(function () {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
31
|
+
const obj = new TestObject();
|
|
32
|
+
// get a heap snapshot of the current program state
|
|
33
|
+
const heap = yield (0, core_1.getNodeInnocentHeap)();
|
|
34
|
+
const node = heap.getAnyObjectWithClassName('TestObject');
|
|
35
|
+
console.log(node === null || node === void 0 ? void 0 : node.name);
|
|
36
|
+
});
|
|
37
|
+
})();
|
|
@@ -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
|
+
* @emails oncall+ws_labs
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=example-2.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
* @emails oncall+ws_labs
|
|
9
|
+
* @format
|
|
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 core_1 = require("@memlab/core");
|
|
22
|
+
const heap_analysis_1 = require("@memlab/heap-analysis");
|
|
23
|
+
(function () {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const heapFile = (0, core_1.dumpNodeHeapSnapshot)();
|
|
26
|
+
const heap = yield (0, heap_analysis_1.getHeapFromFile)(heapFile);
|
|
27
|
+
// get the total number of heap objects
|
|
28
|
+
heap.nodes.length;
|
|
29
|
+
heap.nodes.forEach((node) => {
|
|
30
|
+
console.log(node.name);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
})();
|
|
@@ -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
|
+
* @emails oncall+ws_labs
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=example-3.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
* @emails oncall+ws_labs
|
|
9
|
+
* @format
|
|
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 core_1 = require("@memlab/core");
|
|
22
|
+
const heap_analysis_1 = require("@memlab/heap-analysis");
|
|
23
|
+
(function () {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const heapFile = (0, core_1.dumpNodeHeapSnapshot)();
|
|
26
|
+
const heap = yield (0, heap_analysis_1.getHeapFromFile)(heapFile);
|
|
27
|
+
// get the total number of heap references
|
|
28
|
+
heap.edges.length;
|
|
29
|
+
heap.edges.forEach((edge) => {
|
|
30
|
+
console.log(edge.name_or_index);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
})();
|
|
@@ -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
|
+
* @emails oncall+ws_labs
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=example-4.d.ts.map
|