@memlab/api 1.0.0

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.
Files changed (41) hide show
  1. package/README.md +11 -0
  2. package/dist/API.d.ts +40 -0
  3. package/dist/API.d.ts.map +1 -0
  4. package/dist/API.js +203 -0
  5. package/dist/__tests__/API/E2EBasicAnalysis.test.d.ts +11 -0
  6. package/dist/__tests__/API/E2EBasicAnalysis.test.d.ts.map +1 -0
  7. package/dist/__tests__/API/E2EBasicAnalysis.test.js +65 -0
  8. package/dist/__tests__/API/E2EDetachedDOMAnalysis.test.d.ts +11 -0
  9. package/dist/__tests__/API/E2EDetachedDOMAnalysis.test.d.ts.map +1 -0
  10. package/dist/__tests__/API/E2EDetachedDOMAnalysis.test.js +49 -0
  11. package/dist/__tests__/API/E2EDuplicateObjectAnalysis.test.d.ts +11 -0
  12. package/dist/__tests__/API/E2EDuplicateObjectAnalysis.test.d.ts.map +1 -0
  13. package/dist/__tests__/API/E2EDuplicateObjectAnalysis.test.js +54 -0
  14. package/dist/__tests__/API/E2ERunMultipleSnapshots.example.d.ts +11 -0
  15. package/dist/__tests__/API/E2ERunMultipleSnapshots.example.d.ts.map +1 -0
  16. package/dist/__tests__/API/E2ERunMultipleSnapshots.example.js +52 -0
  17. package/dist/__tests__/API/E2ERunSingleSnapshot.example.d.ts +11 -0
  18. package/dist/__tests__/API/E2ERunSingleSnapshot.example.d.ts.map +1 -0
  19. package/dist/__tests__/API/E2ERunSingleSnapshot.example.js +38 -0
  20. package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.d.ts +11 -0
  21. package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.d.ts.map +1 -0
  22. package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.js +72 -0
  23. package/dist/__tests__/API/E2EStringAnalysis.test.d.ts +11 -0
  24. package/dist/__tests__/API/E2EStringAnalysis.test.d.ts.map +1 -0
  25. package/dist/__tests__/API/E2EStringAnalysis.test.js +66 -0
  26. package/dist/__tests__/API/lib/E2ETestSettings.d.ts +23 -0
  27. package/dist/__tests__/API/lib/E2ETestSettings.d.ts.map +1 -0
  28. package/dist/__tests__/API/lib/E2ETestSettings.js +36 -0
  29. package/dist/__tests__/heap/E2EHeapParser.test.d.ts +16 -0
  30. package/dist/__tests__/heap/E2EHeapParser.test.d.ts.map +1 -0
  31. package/dist/__tests__/heap/E2EHeapParser.test.js +57 -0
  32. package/dist/__tests__/heap/lib/HeapParserTestUtils.d.ts +12 -0
  33. package/dist/__tests__/heap/lib/HeapParserTestUtils.d.ts.map +1 -0
  34. package/dist/__tests__/heap/lib/HeapParserTestUtils.js +99 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +30 -0
  38. package/dist/lib/APIUtils.d.ts +20 -0
  39. package/dist/lib/APIUtils.d.ts.map +1 -0
  40. package/dist/lib/APIUtils.js +50 -0
  41. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # `@memlab/api`
2
+
3
+ > TODO: description
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ const api = require('@memlab/api');
9
+
10
+ // TODO: DEMONSTRATE API
11
+ ```
package/dist/API.d.ts ADDED
@@ -0,0 +1,40 @@
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
+ import type { Page } from 'puppeteer';
11
+ import type { ParsedArgs } from 'minimist';
12
+ import type { AnyFunction, E2EStepInfo, IScenario, RunMetaInfo } from '@memlab/core';
13
+ import { MemLabConfig } from '@memlab/core';
14
+ import { TestPlanner } from '@memlab/e2e';
15
+ import { BaseAnalysis } from '@memlab/heap-analysis';
16
+ declare type APIOptions = {
17
+ testPlanner?: TestPlanner;
18
+ cache?: boolean;
19
+ config?: MemLabConfig;
20
+ evalInBrowserAfterInitLoad?: AnyFunction;
21
+ };
22
+ declare type RunOptions = {
23
+ scenario?: IScenario;
24
+ cookiesFile?: string;
25
+ evalInBrowserAfterInitLoad?: AnyFunction;
26
+ snapshotForEachStep?: boolean;
27
+ };
28
+ declare type RunResult = {
29
+ config: MemLabConfig;
30
+ tabsOrder: E2EStepInfo[];
31
+ metaInfo: RunMetaInfo;
32
+ };
33
+ export declare function run(options?: RunOptions): Promise<RunResult>;
34
+ export declare function takeSnapshots(options?: RunOptions): Promise<RunResult>;
35
+ export declare function analyze(runResult: RunResult, heapAnalyzer: BaseAnalysis, args?: ParsedArgs): Promise<void>;
36
+ export declare function warmup(options?: APIOptions): Promise<void>;
37
+ export declare function setupPage(page: Page, options?: APIOptions): Promise<void>;
38
+ export declare function testInBrowser(options?: APIOptions): Promise<void>;
39
+ export {};
40
+ //# sourceMappingURL=API.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"API.d.ts","sourceRoot":"","sources":["../src/API.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAU,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,WAAW,EAGZ,MAAM,cAAc,CAAC;AAEtB,OAAO,EAKL,YAAY,EACb,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,WAAW,EAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAGnD,aAAK,UAAU,GAAG;IAGhB,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,aAAK,UAAU,GAAG;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B,CAAC,EAAE,WAAW,CAAC;IACzC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,aAAK,SAAS,GAAG;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AASF,wBAAsB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CActE;AAED,wBAAsB,aAAa,CACjC,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CAapB;AAED,wBAAsB,OAAO,CAC3B,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,YAAY,EAC1B,IAAI,GAAE,UAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,MAAM,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCpE;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAyBD,wBAAsB,aAAa,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkD3E"}
package/dist/API.js ADDED
@@ -0,0 +1,203 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.testInBrowser = exports.setupPage = exports.warmup = exports.analyze = exports.takeSnapshots = exports.run = void 0;
25
+ const core_1 = require("@memlab/core");
26
+ const e2e_1 = require("@memlab/e2e");
27
+ const APIUtils_1 = __importDefault(require("./lib/APIUtils"));
28
+ function setConfigByRunOptions(config, options) {
29
+ config.isFullRun = !!options.snapshotForEachStep;
30
+ }
31
+ function run(options = {}) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const config = core_1.MemLabConfig.resetConfigWithTranscientDir();
34
+ setConfigByRunOptions(config, options);
35
+ config.externalCookiesFile = options.cookiesFile;
36
+ config.scenario = options.scenario;
37
+ const testPlanner = new e2e_1.TestPlanner({ config });
38
+ const { evalInBrowserAfterInitLoad } = options;
39
+ yield warmup({ testPlanner, config, evalInBrowserAfterInitLoad });
40
+ yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
41
+ return {
42
+ config,
43
+ tabsOrder: core_1.utils.loadTabsOrder(),
44
+ metaInfo: core_1.utils.loadRunMetaInfo(),
45
+ };
46
+ });
47
+ }
48
+ exports.run = run;
49
+ function takeSnapshots(options = {}) {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ const config = core_1.MemLabConfig.resetConfigWithTranscientDir();
52
+ setConfigByRunOptions(config, options);
53
+ config.externalCookiesFile = options.cookiesFile;
54
+ config.scenario = options.scenario;
55
+ const testPlanner = new e2e_1.TestPlanner();
56
+ const { evalInBrowserAfterInitLoad } = options;
57
+ yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
58
+ return {
59
+ config,
60
+ tabsOrder: core_1.utils.loadTabsOrder(),
61
+ metaInfo: core_1.utils.loadRunMetaInfo(),
62
+ };
63
+ });
64
+ }
65
+ exports.takeSnapshots = takeSnapshots;
66
+ function analyze(runResult, heapAnalyzer, args = { _: [] }) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ yield heapAnalyzer.run({ args });
69
+ });
70
+ }
71
+ exports.analyze = analyze;
72
+ function warmup(options = {}) {
73
+ var _a, _b;
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
76
+ if (config.verbose) {
77
+ core_1.info.lowLevel(`Xvfb: ${config.useXVFB}`);
78
+ }
79
+ const testPlanner = (_b = options.testPlanner) !== null && _b !== void 0 ? _b : e2e_1.defaultTestPlanner;
80
+ try {
81
+ if (config.skipWarmup) {
82
+ return;
83
+ }
84
+ const browser = yield APIUtils_1.default.getBrowser({ warmup: true });
85
+ const visitPlan = testPlanner.getVisitPlan();
86
+ config.setDevice(visitPlan.device);
87
+ const numOfWarmup = visitPlan.numOfWarmup || 3;
88
+ const promises = [];
89
+ for (let i = 0; i < numOfWarmup; ++i) {
90
+ promises.push(browser.newPage());
91
+ }
92
+ const pages = yield Promise.all(promises);
93
+ core_1.info.beginSection('warmup');
94
+ yield Promise.all(pages.map((page) => __awaiter(this, void 0, void 0, function* () {
95
+ yield setupPage(page, { cache: false });
96
+ const interactionManager = new e2e_1.E2EInteractionManager(page);
97
+ yield interactionManager.warmupInPage();
98
+ }))).catch(err => {
99
+ core_1.info.error(err.message);
100
+ });
101
+ core_1.info.endSection('warmup');
102
+ yield core_1.utils.closePuppeteer(browser, pages, { warmup: true });
103
+ }
104
+ catch (ex) {
105
+ const error = core_1.utils.getError(ex);
106
+ core_1.utils.checkUninstalledLibrary(error);
107
+ throw ex;
108
+ }
109
+ });
110
+ }
111
+ exports.warmup = warmup;
112
+ function setupPage(page, options = {}) {
113
+ var _a, _b, _c;
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
116
+ const testPlanner = (_b = options.testPlanner) !== null && _b !== void 0 ? _b : e2e_1.defaultTestPlanner;
117
+ if (config.emulateDevice) {
118
+ yield page.emulate(config.emulateDevice);
119
+ }
120
+ if (config.defaultUserAgent) {
121
+ yield page.setUserAgent(config.defaultUserAgent);
122
+ }
123
+ // set login session
124
+ yield page.setCookie(...testPlanner.getCookies());
125
+ const cache = (_c = options.cache) !== null && _c !== void 0 ? _c : true;
126
+ yield page.setCacheEnabled(cache);
127
+ // automatically accept dialog
128
+ page.on('dialog', (dialog) => __awaiter(this, void 0, void 0, function* () {
129
+ yield dialog.accept();
130
+ }));
131
+ });
132
+ }
133
+ exports.setupPage = setupPage;
134
+ function autoDismissDialog(page, options = {}) {
135
+ var _a;
136
+ const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
137
+ page.on('dialog', (dialog) => __awaiter(this, void 0, void 0, function* () {
138
+ if (config.verbose) {
139
+ core_1.info.lowLevel(`Browser dialog: ${dialog.message()}`);
140
+ }
141
+ yield dialog.dismiss();
142
+ }));
143
+ }
144
+ function initBrowserInfoInConfig(browser, options = {}) {
145
+ var _a;
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
148
+ core_1.browserInfo.setPuppeteerConfig(config.puppeteerConfig);
149
+ const version = yield browser.version();
150
+ core_1.browserInfo.setBrowserVersion(version);
151
+ if (config.verbose) {
152
+ core_1.info.lowLevel(JSON.stringify(core_1.browserInfo, null, 2));
153
+ }
154
+ });
155
+ }
156
+ function testInBrowser(options = {}) {
157
+ var _a, _b;
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ const config = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
160
+ if (config.verbose) {
161
+ core_1.info.lowLevel(`Xvfb: ${config.useXVFB}`);
162
+ }
163
+ const testPlanner = (_b = options.testPlanner) !== null && _b !== void 0 ? _b : e2e_1.defaultTestPlanner;
164
+ let interactionManager = null;
165
+ let xvfb = null;
166
+ try {
167
+ xvfb = e2e_1.Xvfb.startIfEnabled();
168
+ const browser = yield APIUtils_1.default.getBrowser();
169
+ const pages = yield browser.pages();
170
+ const page = pages.length > 0 ? pages[0] : yield browser.newPage();
171
+ interactionManager = new e2e_1.E2EInteractionManager(page);
172
+ if (options.evalInBrowserAfterInitLoad) {
173
+ interactionManager.setEvalFuncAfterInitLoad(options.evalInBrowserAfterInitLoad);
174
+ }
175
+ const visitPlan = testPlanner.getVisitPlan();
176
+ config.setDevice(visitPlan.device);
177
+ autoDismissDialog(page);
178
+ yield initBrowserInfoInConfig(browser);
179
+ core_1.browserInfo.monitorWebConsole(page);
180
+ yield setupPage(page, options);
181
+ yield interactionManager.visitAndGetSnapshots(options);
182
+ yield core_1.utils.closePuppeteer(browser, [page]);
183
+ }
184
+ catch (ex) {
185
+ const error = core_1.utils.getError(ex);
186
+ core_1.utils.checkUninstalledLibrary(error);
187
+ core_1.info.error(error.message);
188
+ }
189
+ finally {
190
+ if (interactionManager) {
191
+ interactionManager.clearCDPSession();
192
+ }
193
+ if (xvfb) {
194
+ xvfb.stop((err) => {
195
+ if (err) {
196
+ core_1.utils.haltOrThrow(err);
197
+ }
198
+ });
199
+ }
200
+ }
201
+ });
202
+ }
203
+ exports.testInBrowser = testInBrowser;
@@ -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=E2EBasicAnalysis.test.d.ts.map
@@ -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,65 @@
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
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
22
+ const index_1 = require("../../index");
23
+ const heap_analysis_1 = require("@memlab/heap-analysis");
24
+ const E2ETestSettings_1 = require("./lib/E2ETestSettings");
25
+ beforeEach(E2ETestSettings_1.testSetup);
26
+ function inject() {
27
+ // @ts-ignore
28
+ window.injectHookForLink3 = () => {
29
+ // @ts-ignore
30
+ window.__injectedValue3 = {};
31
+ };
32
+ // @ts-ignore
33
+ window.injectHookForLink4 = () => {
34
+ // @ts-ignore
35
+ window.__injectedValue4 = {};
36
+ const __injectedTimeoutValue1 = { v: 0 };
37
+ setTimeout(() => {
38
+ __injectedTimeoutValue1.v = 1;
39
+ }, 1);
40
+ };
41
+ }
42
+ test('E2E SPA test hooks work as expected', () => __awaiter(void 0, void 0, void 0, function* () {
43
+ yield (0, index_1.run)({ scenario: E2ETestSettings_1.scenario, evalInBrowserAfterInitLoad: inject });
44
+ const snapshot = yield heap_analysis_1.PluginUtils.loadHeapSnapshot(E2ETestSettings_1.defaultAnalysisArgs);
45
+ let foundInjectedValueForLink3 = false;
46
+ let foundInjectedValueForLink4 = false;
47
+ let foundInjectedTimeoutValue1 = false;
48
+ snapshot.edges.forEach(e => {
49
+ if (e.name_or_index === '__injectedValue3') {
50
+ foundInjectedValueForLink3 = true;
51
+ }
52
+ if (e.name_or_index === '__injectedValue4') {
53
+ foundInjectedValueForLink4 = true;
54
+ }
55
+ if (e.name_or_index === '__injectedTimeoutValue1') {
56
+ foundInjectedTimeoutValue1 = true;
57
+ }
58
+ });
59
+ // link-3 is not clicked, so injectHookForLink3 is not executed
60
+ expect(foundInjectedValueForLink3).toBe(false);
61
+ // link-4 is clicked, so injectHookForLink3 should be executed
62
+ expect(foundInjectedValueForLink4).toBe(true);
63
+ // __injectedValue is a local variable, which should not be retained
64
+ expect(foundInjectedTimeoutValue1).toBe(false);
65
+ }), E2ETestSettings_1.testTimeout);
@@ -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=E2EDetachedDOMAnalysis.test.d.ts.map
@@ -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,49 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
25
+ const path_1 = __importDefault(require("path"));
26
+ const index_1 = require("../../index");
27
+ const E2ETestSettings_1 = require("./lib/E2ETestSettings");
28
+ beforeEach(E2ETestSettings_1.testSetup);
29
+ function inject() {
30
+ // @ts-ignore
31
+ window.injectHookForLink4 = () => {
32
+ // @ts-ignore
33
+ window.__injectedValue = document.createElement('table');
34
+ };
35
+ }
36
+ test('Detached DOM analysis works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
37
+ const result = yield (0, index_1.run)({ scenario: E2ETestSettings_1.scenario, evalInBrowserAfterInitLoad: inject });
38
+ // test analysis from auto loading
39
+ let analysis = new index_1.DetachedDOMElementAnalysis();
40
+ yield analysis.run();
41
+ let domElems = analysis.getDetachedElements();
42
+ expect(domElems.some(node => node.name === 'Detached HTMLTableElement')).toBe(true);
43
+ // test analysis from file
44
+ const snapshotFile = path_1.default.join(result.config.curDataDir, 's3.heapsnapshot');
45
+ analysis = new index_1.DetachedDOMElementAnalysis();
46
+ yield analysis.analyzeSnapshotFromFile(snapshotFile);
47
+ domElems = analysis.getDetachedElements();
48
+ expect(domElems.some(node => node.name === 'Detached HTMLTableElement')).toBe(true);
49
+ }), E2ETestSettings_1.testTimeout);
@@ -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=E2EDuplicateObjectAnalysis.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"E2EDuplicateObjectAnalysis.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/API/E2EDuplicateObjectAnalysis.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,54 @@
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
+ 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 path_1 = __importDefault(require("path"));
25
+ const index_1 = require("../../index");
26
+ const E2ETestSettings_1 = require("./lib/E2ETestSettings");
27
+ beforeEach(E2ETestSettings_1.testSetup);
28
+ function inject() {
29
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
+ // @ts-ignore
31
+ window.injectHookForLink4 = () => {
32
+ const arr = [];
33
+ for (let i = 0; i < 12345; ++i) {
34
+ arr.push({ v: i % 1 });
35
+ }
36
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
37
+ // @ts-ignore
38
+ window.__injectedValue = arr;
39
+ };
40
+ }
41
+ test('Duplicate object analysis works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
42
+ const result = yield (0, index_1.run)({ scenario: E2ETestSettings_1.scenario, evalInBrowserAfterInitLoad: inject });
43
+ // test analysis from auto loading
44
+ let analysis = new index_1.ObjectShallowAnalysis();
45
+ yield analysis.run();
46
+ let dupcatedObjectInfo = analysis.getTopDuplicatedObjectInCount();
47
+ expect(dupcatedObjectInfo[0].n).toBe(12345);
48
+ // test analysis from file
49
+ const snapshotFile = path_1.default.join(result.config.curDataDir, 's3.heapsnapshot');
50
+ analysis = new index_1.ObjectShallowAnalysis();
51
+ yield analysis.analyzeSnapshotFromFile(snapshotFile);
52
+ dupcatedObjectInfo = analysis.getTopDuplicatedObjectInCount();
53
+ expect(dupcatedObjectInfo[0].n).toBe(12345);
54
+ }), E2ETestSettings_1.testTimeout);
@@ -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=E2ERunMultipleSnapshots.example.d.ts.map
@@ -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,52 @@
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 index_1 = require("../../index");
22
+ const scenario = {
23
+ app: () => 'test-spa',
24
+ url: () => '',
25
+ action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
26
+ repeat: () => 4,
27
+ };
28
+ function inject() {
29
+ // @ts-ignore
30
+ window.injectHookForLink4 = () => {
31
+ class LeakObject {
32
+ constructor() {
33
+ this.value = `value: ${Math.random()}`;
34
+ }
35
+ }
36
+ // @ts-ignore
37
+ const leak = (window.__injectedValue = window.__injectedValue || []);
38
+ for (let i = 0; i < 10000; ++i) {
39
+ leak.push(new LeakObject());
40
+ }
41
+ };
42
+ }
43
+ const promise = (0, index_1.run)({
44
+ scenario,
45
+ evalInBrowserAfterInitLoad: inject,
46
+ snapshotForEachStep: true,
47
+ });
48
+ promise.then(() => {
49
+ // test analysis from auto loading
50
+ const analysis = new index_1.ShapeUnboundGrowthAnalysis();
51
+ analysis.run();
52
+ });
@@ -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=E2ERunSingleSnapshot.example.d.ts.map
@@ -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,38 @@
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
+ 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 path_1 = __importDefault(require("path"));
25
+ const index_1 = require("../../index");
26
+ const scenario = {
27
+ app: () => 'test-spa',
28
+ url: () => '',
29
+ action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
30
+ };
31
+ const promise = (0, index_1.run)({
32
+ scenario,
33
+ });
34
+ promise.then(result => {
35
+ const analysis = new index_1.StringAnalysis();
36
+ const snapshotFile = path_1.default.join(result.config.curDataDir, 's3.heapsnapshot');
37
+ analysis.analyzeSnapshotFromFile(snapshotFile);
38
+ });
@@ -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=E2EShapeUnboundGrowthAnalysis.test.d.ts.map
@@ -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,72 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
25
+ const path_1 = __importDefault(require("path"));
26
+ const index_1 = require("../../index");
27
+ const E2ETestSettings_1 = require("./lib/E2ETestSettings");
28
+ beforeEach(E2ETestSettings_1.testSetup);
29
+ function inject() {
30
+ // @ts-ignore
31
+ window.injectHookForLink4 = () => {
32
+ function LeakObject() {
33
+ // @ts-ignore
34
+ this.value = `value: ${Math.random()}`;
35
+ }
36
+ // @ts-ignore
37
+ const leak = (window.__injectedValue = window.__injectedValue || []);
38
+ for (let i = 0; i < 10000; ++i) {
39
+ // @ts-ignore
40
+ leak.push(new LeakObject());
41
+ }
42
+ };
43
+ }
44
+ test('Shape unbound analysis works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
45
+ const repeatScenario = Object.assign({ repeat: () => 2 }, E2ETestSettings_1.scenario);
46
+ // test analysis from auto loading
47
+ const result = yield (0, index_1.run)({
48
+ scenario: repeatScenario,
49
+ evalInBrowserAfterInitLoad: inject,
50
+ snapshotForEachStep: true,
51
+ });
52
+ // test analysis from auto loading
53
+ let analysis = new index_1.ShapeUnboundGrowthAnalysis();
54
+ let shapeSummary = yield analysis.run();
55
+ expect(shapeSummary.reduce((acc, summary) => acc || summary.shape.includes('LeakObject'), false)).toBe(true);
56
+ // test analysis from file
57
+ const snapshotDir = path_1.default.join(result.config.curDataDir);
58
+ analysis = new index_1.ShapeUnboundGrowthAnalysis();
59
+ shapeSummary = yield analysis.analyzeSnapshotsInDirectory(snapshotDir);
60
+ expect(shapeSummary.some((summary) => summary.shape.includes('LeakObject'))).toBe(true);
61
+ // expect incorrect use of heap analysis to throw
62
+ const snapshotFile = path_1.default.join(result.config.curDataDir, 's3.heapsnapshot');
63
+ analysis = new index_1.ShapeUnboundGrowthAnalysis();
64
+ let isThrow = false;
65
+ try {
66
+ yield analysis.analyzeSnapshotFromFile(snapshotFile);
67
+ }
68
+ catch (ex) {
69
+ isThrow = true;
70
+ }
71
+ expect(isThrow).toBe(true);
72
+ }), E2ETestSettings_1.testTimeout);
@@ -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=E2EStringAnalysis.test.d.ts.map
@@ -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,66 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
25
+ const path_1 = __importDefault(require("path"));
26
+ const index_1 = require("../../index");
27
+ const E2ETestSettings_1 = require("./lib/E2ETestSettings");
28
+ beforeEach(E2ETestSettings_1.testSetup);
29
+ function inject() {
30
+ // @ts-ignore
31
+ window.injectHookForLink4 = () => {
32
+ const arr = [];
33
+ for (let i = 0; i < 10000; ++i) {
34
+ arr.push('duplicated string value' + (i % 1));
35
+ }
36
+ // @ts-ignore
37
+ window.__injectedValue = arr;
38
+ };
39
+ }
40
+ test('String analysis works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
41
+ const result = yield (0, index_1.run)({ scenario: E2ETestSettings_1.scenario, evalInBrowserAfterInitLoad: inject });
42
+ // test analysis from auto loading
43
+ let analysis = new index_1.StringAnalysis();
44
+ yield analysis.run();
45
+ let dupStrings = analysis.getTopDuplicatedStringsInCount();
46
+ expect(dupStrings[0].n).toBe(10000);
47
+ expect(dupStrings[0].str).toBe('duplicated string value0');
48
+ // test analysis from file
49
+ const snapshotFile = path_1.default.join(result.config.curDataDir, 's3.heapsnapshot');
50
+ analysis = new index_1.StringAnalysis();
51
+ yield analysis.analyzeSnapshotFromFile(snapshotFile);
52
+ dupStrings = analysis.getTopDuplicatedStringsInCount();
53
+ expect(dupStrings[0].n).toBe(10000);
54
+ expect(dupStrings[0].str).toBe('duplicated string value0');
55
+ // expect incorrect use of heap analysis to throw
56
+ const snapshotDir = result.config.curDataDir;
57
+ analysis = new index_1.StringAnalysis();
58
+ let isThrow = false;
59
+ try {
60
+ yield analysis.analyzeSnapshotsInDirectory(snapshotDir);
61
+ }
62
+ catch (ex) {
63
+ isThrow = true;
64
+ }
65
+ expect(isThrow).toBe(true);
66
+ }), E2ETestSettings_1.testTimeout);
@@ -0,0 +1,23 @@
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
+ import type { Page } from 'puppeteer';
11
+ export declare const testTimeout: number;
12
+ export declare const defaultAnalysisArgs: {
13
+ args: {
14
+ _: never[];
15
+ };
16
+ };
17
+ export declare const scenario: {
18
+ app: () => string;
19
+ url: () => string;
20
+ action: (page: Page) => Promise<void>;
21
+ };
22
+ export declare const testSetup: () => void;
23
+ //# sourceMappingURL=E2ETestSettings.d.ts.map
@@ -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"}
@@ -0,0 +1,36 @@
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
+ exports.testSetup = exports.scenario = exports.defaultAnalysisArgs = exports.testTimeout = void 0;
22
+ const core_1 = require("@memlab/core");
23
+ exports.testTimeout = 5 * 60 * 1000;
24
+ exports.defaultAnalysisArgs = { args: { _: [] } };
25
+ exports.scenario = {
26
+ app: () => 'test-spa',
27
+ url: () => '',
28
+ action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
29
+ };
30
+ const testSetup = () => {
31
+ core_1.config.isTest = true;
32
+ core_1.config.useXVFB = false;
33
+ core_1.config.skipExtraOps = true;
34
+ core_1.config.errorHandling = core_1.ErrorHandling.Throw;
35
+ };
36
+ exports.testSetup = testSetup;
@@ -0,0 +1,16 @@
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
+ import type { AnyValue } from '@memlab/core';
11
+ declare global {
12
+ interface Window {
13
+ injected: AnyValue;
14
+ }
15
+ }
16
+ //# sourceMappingURL=E2EHeapParser.test.d.ts.map
@@ -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;AAOrE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,QAAQ,EAAE,QAAQ,CAAC;KACpB;CACF"}
@@ -0,0 +1,57 @@
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 HeapParserTestUtils_1 = require("./lib/HeapParserTestUtils");
23
+ beforeEach(() => {
24
+ core_1.config.isTest = true;
25
+ });
26
+ const timeout = 5 * 60 * 1000;
27
+ test('Capture numeric value from heap in browser', () => __awaiter(void 0, void 0, void 0, function* () {
28
+ const leakInjector = () => {
29
+ class TestObject {
30
+ constructor() {
31
+ this.numProp = 0.1;
32
+ }
33
+ }
34
+ window.injected = new TestObject();
35
+ };
36
+ const checker = (snapshot) => {
37
+ let detected = false;
38
+ snapshot.nodes.forEach((node) => {
39
+ if (node.name !== 'TestObject' || node.type !== 'object') {
40
+ return;
41
+ }
42
+ const refs = node.references;
43
+ for (const ref of refs) {
44
+ if (ref.name_or_index === 'numProp') {
45
+ const node = ref.toNode;
46
+ if (node.type === 'number' &&
47
+ core_1.utils.getNumberNodeValue(node) === 0.1) {
48
+ detected = true;
49
+ }
50
+ break;
51
+ }
52
+ }
53
+ });
54
+ return detected;
55
+ };
56
+ yield (0, HeapParserTestUtils_1.isExpectedSnapshot)(leakInjector, checker);
57
+ }), timeout);
@@ -0,0 +1,12 @@
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
+ import type { IHeapSnapshot } from '@memlab/core';
11
+ export declare function isExpectedSnapshot(leakInjector: () => void, checkSnapshotCb: (snapshot: IHeapSnapshot) => boolean): Promise<void>;
12
+ //# sourceMappingURL=HeapParserTestUtils.d.ts.map
@@ -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"}
@@ -0,0 +1,99 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.isExpectedSnapshot = void 0;
25
+ const fs_1 = __importDefault(require("fs"));
26
+ const path_1 = __importDefault(require("path"));
27
+ const core_1 = require("@memlab/core");
28
+ const puppeteer = core_1.config.isFB
29
+ ? require('puppeteer-core')
30
+ : require('puppeteer');
31
+ function isExpectedSnapshot(leakInjector, checkSnapshotCb) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const snapshot = yield getHeapSnapshot(leakInjector);
34
+ expect(checkSnapshotCb(snapshot)).toBe(true);
35
+ });
36
+ }
37
+ exports.isExpectedSnapshot = isExpectedSnapshot;
38
+ function getHeapDirPrefix() {
39
+ const dir = path_1.default.join(core_1.config.dataBaseDir, 'gen-files');
40
+ if (!fs_1.default.existsSync(dir)) {
41
+ fs_1.default.mkdirSync(dir);
42
+ }
43
+ return dir;
44
+ }
45
+ function saveSnapshotToFile(page, file) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ core_1.info.lowLevel(`saving heap snapshot to file ${file}`);
48
+ let heap = '';
49
+ const devtoolsProtocolClient = yield page.target().createCDPSession();
50
+ devtoolsProtocolClient.on('HeapProfiler.addHeapSnapshotChunk', data => {
51
+ heap += data.chunk;
52
+ });
53
+ yield devtoolsProtocolClient.send('HeapProfiler.takeHeapSnapshot', {
54
+ reportProgress: false,
55
+ captureNumericValue: true,
56
+ });
57
+ return new Promise((resolve, reject) => {
58
+ fs_1.default.writeFile(file, heap, 'UTF-8', err => {
59
+ if (err) {
60
+ reject(err);
61
+ }
62
+ else {
63
+ resolve();
64
+ }
65
+ });
66
+ });
67
+ });
68
+ }
69
+ const TEST_URL = 'about:blank';
70
+ function dumpHeap(snapshotFile, leakInjector) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ const browser = yield puppeteer.launch(core_1.config.puppeteerConfig);
73
+ const page = yield browser.newPage();
74
+ // set page size
75
+ yield page.setViewport({
76
+ width: 1680,
77
+ height: 1050,
78
+ deviceScaleFactor: 1,
79
+ });
80
+ // visit page
81
+ yield page.goto(TEST_URL);
82
+ // insert a memory leak object
83
+ yield page.evaluate(leakInjector);
84
+ // take a heap snapshot
85
+ yield saveSnapshotToFile(page, snapshotFile);
86
+ yield browser.close();
87
+ });
88
+ }
89
+ let fileId = 0;
90
+ function getHeapSnapshot(leakInjector) {
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ const snapshotFile = path_1.default.join(getHeapDirPrefix(), `snapshot-${Date.now()}-${fileId++}.json`);
93
+ yield dumpHeap(snapshotFile, leakInjector);
94
+ // parse the heap
95
+ const opt = { buildNodeIdIndex: true };
96
+ const snapshot = yield core_1.utils.getSnapshotFromFile(snapshotFile, opt);
97
+ return snapshot;
98
+ });
99
+ }
@@ -0,0 +1,13 @@
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 * from './API';
11
+ export * from '@memlab/heap-analysis';
12
+ export { config } from '@memlab/core';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,OAAO,CAAC;AACtB,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
23
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.config = void 0;
27
+ __exportStar(require("./API"), exports);
28
+ __exportStar(require("@memlab/heap-analysis"), exports);
29
+ var core_1 = require("@memlab/core");
30
+ Object.defineProperty(exports, "config", { enumerable: true, get: function () { return core_1.config; } });
@@ -0,0 +1,20 @@
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
+ import type { Browser } from 'puppeteer';
11
+ import type { MemLabConfig } from '@memlab/core';
12
+ declare function getBrowser(options?: {
13
+ config?: MemLabConfig;
14
+ warmup?: boolean;
15
+ }): Promise<Browser>;
16
+ declare const _default: {
17
+ getBrowser: typeof getBrowser;
18
+ };
19
+ export default _default;
20
+ //# sourceMappingURL=APIUtils.d.ts.map
@@ -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,CAmBlB;;;;AAED,wBAEE"}
@@ -0,0 +1,50 @@
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 puppeteer = core_1.constant.isFRL
23
+ ? {}
24
+ : core_1.constant.isFB
25
+ ? require('puppeteer-core')
26
+ : require('puppeteer');
27
+ function getBrowser(options = {}) {
28
+ var _a;
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ const runConfig = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
31
+ let browser;
32
+ if (runConfig.isLocalPuppeteer && !options.warmup) {
33
+ try {
34
+ browser = yield puppeteer.connect(Object.assign({ browserURL: `http://localhost:${runConfig.localBrowserPort}` }, runConfig.puppeteerConfig));
35
+ }
36
+ catch (e) {
37
+ throw core_1.utils.haltOrThrow(core_1.utils.getError(e), {
38
+ primaryMessageToPrint: 'Failed to connect to local browser. Ensure that the local-puppeteer script is running.',
39
+ });
40
+ }
41
+ }
42
+ else {
43
+ browser = yield puppeteer.launch(runConfig.puppeteerConfig);
44
+ }
45
+ return browser;
46
+ });
47
+ }
48
+ exports.default = {
49
+ getBrowser,
50
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@memlab/api",
3
+ "version": "1.0.0",
4
+ "description": "API of memlab",
5
+ "keywords": [
6
+ "api"
7
+ ],
8
+ "author": "Liang Gong <lgong@fb.com>",
9
+ "contributors": [],
10
+ "license": "MIT",
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "@memlab/core": "^1.0.0",
21
+ "@memlab/e2e": "^1.0.0",
22
+ "@memlab/heap-analysis": "^1.0.0",
23
+ "ansi": "^0.3.1",
24
+ "babar": "^0.2.0",
25
+ "chalk": "^4.0.0",
26
+ "fs-extra": "^4.0.2",
27
+ "minimist": "^1.2.0",
28
+ "puppeteer": "^13.5.1",
29
+ "string-width": "^4.2.0",
30
+ "util.promisify": "^1.1.1",
31
+ "xvfb": "^0.4.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/fs-extra": "^9.0.3",
35
+ "@types/jest": "^27.4.1",
36
+ "@types/minimist": "^1.2.2",
37
+ "@types/node": "^12.16.3",
38
+ "@types/puppeteer": "^5.4.4",
39
+ "jest": "^27.5.1",
40
+ "ts-jest": "^27.1.4",
41
+ "typescript": "^4.6.3"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/facebookincubator/memlab.git",
46
+ "directory": "packages/api"
47
+ },
48
+ "scripts": {
49
+ "build-pkg": "tsc",
50
+ "test-pkg": "jest .",
51
+ "clean-pkg": "rm -rf ./dist && rm -rf ./node_modules && rm -f ./tsconfig.tsbuildinfo"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/facebookincubator/memlab/issues"
55
+ },
56
+ "homepage": "https://github.com/facebookincubator/memlab#readme"
57
+ }