@memlab/e2e 1.0.19 → 1.0.21
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.
|
@@ -7,21 +7,24 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
10
|
+
import type { Browser, CDPSession, Page, Target } from 'puppeteer';
|
|
11
|
+
import type { AnyFunction, Nullable, OperationArgs, MemLabConfig } from '@memlab/core';
|
|
12
12
|
import { TestPlanner } from './lib/operations/TestPlanner';
|
|
13
13
|
declare type PageInteractOptions = {
|
|
14
14
|
config?: MemLabConfig;
|
|
15
15
|
testPlanner?: TestPlanner;
|
|
16
16
|
};
|
|
17
17
|
export default class E2EInteractionManager {
|
|
18
|
-
private
|
|
18
|
+
private mainThreadCdpsession;
|
|
19
19
|
private page;
|
|
20
|
+
private browser;
|
|
20
21
|
private pageHistoryLength;
|
|
21
22
|
private evalFuncAfterInitLoad;
|
|
22
23
|
private networkManager;
|
|
23
|
-
constructor(page: Page);
|
|
24
|
-
|
|
24
|
+
constructor(page: Page, browser: Browser);
|
|
25
|
+
getChosenCDPSession(): Promise<CDPSession>;
|
|
26
|
+
getMainThreadCDPSession(): Promise<CDPSession>;
|
|
27
|
+
selectCDPSession(predicate: (t: Target) => boolean): Promise<Nullable<CDPSession>>;
|
|
25
28
|
clearCDPSession(): void;
|
|
26
29
|
setEvalFuncAfterInitLoad(func: AnyFunction | null): void;
|
|
27
30
|
protected initialLoad(page: Page, url: string, opArgs?: OperationArgs): Promise<void>;
|
|
@@ -35,7 +38,7 @@ export default class E2EInteractionManager {
|
|
|
35
38
|
private writeSnapshotFileFromCDPSession;
|
|
36
39
|
private saveHeapSnapshotToFile;
|
|
37
40
|
private fullGC;
|
|
38
|
-
private
|
|
41
|
+
private forceMainThreadGC;
|
|
39
42
|
private collectMetrics;
|
|
40
43
|
}
|
|
41
44
|
export {};
|
|
@@ -30,23 +30,56 @@ const TestPlanner_1 = __importDefault(require("./lib/operations/TestPlanner"));
|
|
|
30
30
|
const NetworkManager_1 = __importDefault(require("./NetworkManager"));
|
|
31
31
|
const { logMetaData, setPermissions, logTabProgress, maybeWaitForConsoleInput, applyAsyncWithRetry, compareURL, waitExtraForTab, checkURL, injectPageReloadChecker, checkPageReload, dispatchOperation, clearConsole, getNavigationHistoryLength, checkLastSnapshotChunk, getURLParameter, } = E2EUtils_1.default;
|
|
32
32
|
class E2EInteractionManager {
|
|
33
|
-
constructor(page) {
|
|
33
|
+
constructor(page, browser) {
|
|
34
34
|
this.pageHistoryLength = [];
|
|
35
35
|
this.evalFuncAfterInitLoad = null;
|
|
36
36
|
this.page = page;
|
|
37
|
+
this.browser = browser;
|
|
37
38
|
this.networkManager = new NetworkManager_1.default(page);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
getChosenCDPSession() {
|
|
40
41
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (core_1.config.isAnalyzingMainThread) {
|
|
43
|
+
return this.getMainThreadCDPSession();
|
|
44
|
+
}
|
|
45
|
+
// get web worker thread target
|
|
46
|
+
const cdpSession = yield this.selectCDPSession(target => {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const t = target;
|
|
49
|
+
const isWorker = ((_a = t._targetInfo) === null || _a === void 0 ? void 0 : _a.type) === 'worker';
|
|
50
|
+
let isTitleMatch = true;
|
|
51
|
+
if (core_1.config.targetWorkerTitle != null) {
|
|
52
|
+
isTitleMatch = ((_b = t._targetInfo) === null || _b === void 0 ? void 0 : _b.title) === core_1.config.targetWorkerTitle;
|
|
53
|
+
}
|
|
54
|
+
return isWorker && isTitleMatch;
|
|
55
|
+
});
|
|
56
|
+
if (cdpSession == null) {
|
|
57
|
+
throw core_1.utils.haltOrThrow('web worker or main thread heap under analysis not found');
|
|
58
|
+
}
|
|
59
|
+
return cdpSession;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
getMainThreadCDPSession() {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
if (!this.mainThreadCdpsession) {
|
|
65
|
+
this.mainThreadCdpsession = yield this.page.target().createCDPSession();
|
|
66
|
+
this.networkManager.setCDPSession(this.mainThreadCdpsession);
|
|
67
|
+
}
|
|
68
|
+
return this.mainThreadCdpsession;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
selectCDPSession(predicate) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const targets = yield this.browser.targets();
|
|
74
|
+
const target = targets.find(predicate);
|
|
75
|
+
if (!target) {
|
|
76
|
+
return null;
|
|
44
77
|
}
|
|
45
|
-
return
|
|
78
|
+
return yield target.createCDPSession();
|
|
46
79
|
});
|
|
47
80
|
}
|
|
48
81
|
clearCDPSession() {
|
|
49
|
-
this.
|
|
82
|
+
this.mainThreadCdpsession = null;
|
|
50
83
|
}
|
|
51
84
|
setEvalFuncAfterInitLoad(func) {
|
|
52
85
|
this.evalFuncAfterInitLoad = func;
|
|
@@ -73,7 +106,8 @@ class E2EInteractionManager {
|
|
|
73
106
|
}
|
|
74
107
|
beforeInteractions() {
|
|
75
108
|
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
-
|
|
109
|
+
// tracking main thread for network interception
|
|
110
|
+
const session = yield this.getMainThreadCDPSession();
|
|
77
111
|
if (core_1.config.interceptScript) {
|
|
78
112
|
this.networkManager.setCDPSession(session);
|
|
79
113
|
yield this.networkManager.interceptScript();
|
|
@@ -270,7 +304,7 @@ class E2EInteractionManager {
|
|
|
270
304
|
if (core_1.config.verbose) {
|
|
271
305
|
core_1.info.lowLevel('Start tracking JS heap');
|
|
272
306
|
}
|
|
273
|
-
const session = yield this.
|
|
307
|
+
const session = yield this.getMainThreadCDPSession();
|
|
274
308
|
yield session.send('HeapProfiler.enable');
|
|
275
309
|
});
|
|
276
310
|
}
|
|
@@ -305,7 +339,7 @@ class E2EInteractionManager {
|
|
|
305
339
|
return __awaiter(this, void 0, void 0, function* () {
|
|
306
340
|
core_1.info.beginSection('heap snapshot');
|
|
307
341
|
const start = Date.now();
|
|
308
|
-
const session = yield this.
|
|
342
|
+
const session = yield this.getChosenCDPSession();
|
|
309
343
|
yield this.writeSnapshotFileFromCDPSession(file, session);
|
|
310
344
|
const spanMs = Date.now() - start;
|
|
311
345
|
if (core_1.config.verbose) {
|
|
@@ -328,12 +362,12 @@ class E2EInteractionManager {
|
|
|
328
362
|
core_1.info.overwrite('running a full GC...');
|
|
329
363
|
}
|
|
330
364
|
// force GC 6 times to release feedback_cells
|
|
331
|
-
yield this.
|
|
365
|
+
yield this.forceMainThreadGC(6);
|
|
332
366
|
});
|
|
333
367
|
}
|
|
334
|
-
|
|
368
|
+
forceMainThreadGC(repeat = 1) {
|
|
335
369
|
return __awaiter(this, void 0, void 0, function* () {
|
|
336
|
-
const client = yield this.
|
|
370
|
+
const client = yield this.getMainThreadCDPSession();
|
|
337
371
|
for (let i = 0; i < repeat; i++) {
|
|
338
372
|
yield client.send('HeapProfiler.collectGarbage');
|
|
339
373
|
// wait for a while and let GC do the job
|
|
@@ -351,7 +385,7 @@ class E2EInteractionManager {
|
|
|
351
385
|
if (!core_1.config.runningMode.shouldGetMetrics(tabInfo)) {
|
|
352
386
|
return;
|
|
353
387
|
}
|
|
354
|
-
yield this.
|
|
388
|
+
yield this.forceMainThreadGC();
|
|
355
389
|
// collect heap size
|
|
356
390
|
const builtInMetrics = yield this.page.metrics();
|
|
357
391
|
const size = core_1.utils.getReadableBytes(builtInMetrics.JSHeapUsedSize);
|
package/package.json
CHANGED
package/static/links/index.html
CHANGED
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<link rel="icon" href="/favicon.ico" />
|
|
7
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
8
|
+
<meta name="theme-color" content="#000000" />
|
|
9
|
+
<meta name="description" content="Web page for e2e tests" />
|
|
10
|
+
<title>React App</title>
|
|
11
|
+
<script>
|
|
12
|
+
// create a web worker
|
|
13
|
+
var blobURL = URL.createObjectURL(
|
|
14
|
+
new Blob(
|
|
15
|
+
[
|
|
16
|
+
'(',
|
|
17
|
+
function () {
|
|
18
|
+
function WorkerTestObject() {
|
|
19
|
+
this.idx = Math.random();
|
|
20
|
+
}
|
|
21
|
+
setInterval(() => {
|
|
22
|
+
self.holder = self.holder || [];
|
|
23
|
+
self.holder.push(new WorkerTestObject());
|
|
24
|
+
}, 5);
|
|
25
|
+
}.toString(),
|
|
26
|
+
')()',
|
|
27
|
+
],
|
|
28
|
+
{ type: 'application/javascript' },
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
var worker = new Worker(blobURL, {name: 'test-worker'});
|
|
32
|
+
URL.revokeObjectURL(blobURL);
|
|
33
|
+
</script>
|
|
34
|
+
<script defer="defer" src="min.js"></script>
|
|
35
|
+
</head>
|
|
36
|
+
|
|
37
|
+
<body>
|
|
38
|
+
<div id="root"></div>
|
|
39
|
+
</body>
|
|
40
|
+
|
|
18
41
|
</html>
|