@memlab/e2e 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.
- package/README.md +11 -0
- package/dist/BaseSynthesizer.d.ts +63 -0
- package/dist/BaseSynthesizer.d.ts.map +1 -0
- package/dist/BaseSynthesizer.js +433 -0
- package/dist/E2EInteractionManager.d.ts +41 -0
- package/dist/E2EInteractionManager.d.ts.map +1 -0
- package/dist/E2EInteractionManager.js +347 -0
- package/dist/TestRunnerLoader.d.ts +16 -0
- package/dist/TestRunnerLoader.d.ts.map +1 -0
- package/dist/TestRunnerLoader.js +41 -0
- package/dist/TypesThirdParty.d.ts +25 -0
- package/dist/TypesThirdParty.d.ts.map +1 -0
- package/dist/TypesThirdParty.js +10 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/lib/E2EUtils.d.ts +59 -0
- package/dist/lib/E2EUtils.d.ts.map +1 -0
- package/dist/lib/E2EUtils.js +286 -0
- package/dist/lib/SynthesisUtils.d.ts +25 -0
- package/dist/lib/SynthesisUtils.d.ts.map +1 -0
- package/dist/lib/SynthesisUtils.js +30 -0
- package/dist/lib/operations/BackOperation.d.ts +17 -0
- package/dist/lib/operations/BackOperation.d.ts.map +1 -0
- package/dist/lib/operations/BackOperation.js +37 -0
- package/dist/lib/operations/BackspaceOperation.d.ts +18 -0
- package/dist/lib/operations/BackspaceOperation.d.ts.map +1 -0
- package/dist/lib/operations/BackspaceOperation.js +36 -0
- package/dist/lib/operations/BaseOperation.d.ts +22 -0
- package/dist/lib/operations/BaseOperation.d.ts.map +1 -0
- package/dist/lib/operations/BaseOperation.js +67 -0
- package/dist/lib/operations/CachePageContent.d.ts +22 -0
- package/dist/lib/operations/CachePageContent.d.ts.map +1 -0
- package/dist/lib/operations/CachePageContent.js +46 -0
- package/dist/lib/operations/ClickOperation.d.ts +36 -0
- package/dist/lib/operations/ClickOperation.d.ts.map +1 -0
- package/dist/lib/operations/ClickOperation.js +131 -0
- package/dist/lib/operations/CompoundOperation.d.ts +20 -0
- package/dist/lib/operations/CompoundOperation.d.ts.map +1 -0
- package/dist/lib/operations/CompoundOperation.js +39 -0
- package/dist/lib/operations/EnterOperation.d.ts +18 -0
- package/dist/lib/operations/EnterOperation.d.ts.map +1 -0
- package/dist/lib/operations/EnterOperation.js +36 -0
- package/dist/lib/operations/EscOperation.d.ts +17 -0
- package/dist/lib/operations/EscOperation.d.ts.map +1 -0
- package/dist/lib/operations/EscOperation.js +36 -0
- package/dist/lib/operations/HoverOperation.d.ts +28 -0
- package/dist/lib/operations/HoverOperation.d.ts.map +1 -0
- package/dist/lib/operations/HoverOperation.js +73 -0
- package/dist/lib/operations/InteractionUtils.d.ts +33 -0
- package/dist/lib/operations/InteractionUtils.d.ts.map +1 -0
- package/dist/lib/operations/InteractionUtils.js +207 -0
- package/dist/lib/operations/Operations.d.ts +25 -0
- package/dist/lib/operations/Operations.d.ts.map +1 -0
- package/dist/lib/operations/Operations.js +45 -0
- package/dist/lib/operations/RevertOperation.d.ts +18 -0
- package/dist/lib/operations/RevertOperation.d.ts.map +1 -0
- package/dist/lib/operations/RevertOperation.js +55 -0
- package/dist/lib/operations/RunJSCode.d.ts +25 -0
- package/dist/lib/operations/RunJSCode.d.ts.map +1 -0
- package/dist/lib/operations/RunJSCode.js +51 -0
- package/dist/lib/operations/ScrollOperation.d.ts +27 -0
- package/dist/lib/operations/ScrollOperation.d.ts.map +1 -0
- package/dist/lib/operations/ScrollOperation.js +85 -0
- package/dist/lib/operations/TargetExtraOperation.d.ts +18 -0
- package/dist/lib/operations/TargetExtraOperation.d.ts.map +1 -0
- package/dist/lib/operations/TargetExtraOperation.js +28 -0
- package/dist/lib/operations/TestPlanner.d.ts +46 -0
- package/dist/lib/operations/TestPlanner.d.ts.map +1 -0
- package/dist/lib/operations/TestPlanner.js +182 -0
- package/dist/lib/operations/TypeOperation.d.ts +29 -0
- package/dist/lib/operations/TypeOperation.d.ts.map +1 -0
- package/dist/lib/operations/TypeOperation.js +64 -0
- package/dist/lib/operations/UploadOperation.d.ts +25 -0
- package/dist/lib/operations/UploadOperation.d.ts.map +1 -0
- package/dist/lib/operations/UploadOperation.js +56 -0
- package/dist/lib/operations/WaitForElementOperation.d.ts +32 -0
- package/dist/lib/operations/WaitForElementOperation.d.ts.map +1 -0
- package/dist/lib/operations/WaitForElementOperation.js +67 -0
- package/dist/lib/operations/WaitOperation.d.ts +19 -0
- package/dist/lib/operations/WaitOperation.d.ts.map +1 -0
- package/dist/lib/operations/WaitOperation.js +42 -0
- package/dist/lib/operations/WithCachedPageContent.d.ts +26 -0
- package/dist/lib/operations/WithCachedPageContent.d.ts.map +1 -0
- package/dist/lib/operations/WithCachedPageContent.js +53 -0
- package/dist/lib/operations/XVirtualFrameBuffer.d.ts +15 -0
- package/dist/lib/operations/XVirtualFrameBuffer.d.ts.map +1 -0
- package/dist/lib/operations/XVirtualFrameBuffer.js +44 -0
- package/dist/plugins/DefaultScenarioSynthesizer.d.ts +23 -0
- package/dist/plugins/DefaultScenarioSynthesizer.d.ts.map +1 -0
- package/dist/plugins/DefaultScenarioSynthesizer.js +61 -0
- package/dist/plugins/TestSPAVisitSynthesizer.d.ts +23 -0
- package/dist/plugins/TestSPAVisitSynthesizer.d.ts.map +1 -0
- package/dist/plugins/TestSPAVisitSynthesizer.js +72 -0
- package/dist/plugins/scenarios/test-airbnb.d.ts +14 -0
- package/dist/plugins/scenarios/test-airbnb.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-airbnb.js +48 -0
- package/dist/plugins/scenarios/test-databricks.d.ts +14 -0
- package/dist/plugins/scenarios/test-databricks.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-databricks.js +40 -0
- package/dist/plugins/scenarios/test-ebay.d.ts +14 -0
- package/dist/plugins/scenarios/test-ebay.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-ebay.js +48 -0
- package/dist/plugins/scenarios/test-google-maps.d.ts +14 -0
- package/dist/plugins/scenarios/test-google-maps.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-google-maps.js +41 -0
- package/dist/plugins/scenarios/test-shopify.d.ts +14 -0
- package/dist/plugins/scenarios/test-shopify.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-shopify.js +40 -0
- package/dist/plugins/scenarios/test-youtube.d.ts +14 -0
- package/dist/plugins/scenarios/test-youtube.d.ts.map +1 -0
- package/dist/plugins/scenarios/test-youtube.js +40 -0
- package/package.json +53 -0
- package/static/links/index.html +18 -0
- package/static/links/min.js +3 -0
- package/static/links/package.json +36 -0
- package/static/links/src/App.js +48 -0
- package/static/links/src/index.js +20 -0
|
@@ -0,0 +1,347 @@
|
|
|
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 fs_1 = __importDefault(require("fs"));
|
|
25
|
+
const path_1 = __importDefault(require("path"));
|
|
26
|
+
const E2EUtils_1 = __importDefault(require("./lib/E2EUtils"));
|
|
27
|
+
const core_1 = require("@memlab/core");
|
|
28
|
+
const InteractionUtils_1 = __importDefault(require("./lib/operations/InteractionUtils"));
|
|
29
|
+
const TestPlanner_1 = __importDefault(require("./lib/operations/TestPlanner"));
|
|
30
|
+
const { logMetaData, setPermissions, logTabProgress, maybeWaitForConsoleInput, applyAsyncWithRetry, compareURL, waitExtraForTab, checkURL, injectPageReloadChecker, checkPageReload, dispatchOperation, clearConsole, getNavigationHistoryLength, checkLastSnapshotChunk, getURLParameter, } = E2EUtils_1.default;
|
|
31
|
+
class E2EInteractionManager {
|
|
32
|
+
constructor(page) {
|
|
33
|
+
this.pageHistoryLength = [];
|
|
34
|
+
this.evalFuncAfterInitLoad = null;
|
|
35
|
+
this.page = page;
|
|
36
|
+
}
|
|
37
|
+
getCDPSession() {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
if (!this.cdpsession) {
|
|
40
|
+
this.cdpsession = yield this.page.target().createCDPSession();
|
|
41
|
+
}
|
|
42
|
+
return this.cdpsession;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
clearCDPSession() {
|
|
46
|
+
this.cdpsession = null;
|
|
47
|
+
}
|
|
48
|
+
setEvalFuncAfterInitLoad(func) {
|
|
49
|
+
this.evalFuncAfterInitLoad = func;
|
|
50
|
+
}
|
|
51
|
+
initialLoad(page, url, opArgs = {}) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
if (core_1.config.verbose) {
|
|
54
|
+
core_1.info.lowLevel(`loading: ${url}`);
|
|
55
|
+
}
|
|
56
|
+
core_1.info.overwrite('Connecting to web server...');
|
|
57
|
+
yield page.goto(url, {
|
|
58
|
+
timeout: core_1.config.initialPageLoadTimeout,
|
|
59
|
+
waitUntil: 'load',
|
|
60
|
+
});
|
|
61
|
+
// wait extra 10s in continuous test env for the initial page load
|
|
62
|
+
if (core_1.config.isContinuousTest) {
|
|
63
|
+
yield InteractionUtils_1.default.waitFor(10000);
|
|
64
|
+
}
|
|
65
|
+
if (this.evalFuncAfterInitLoad) {
|
|
66
|
+
yield page.evaluate(this.evalFuncAfterInitLoad);
|
|
67
|
+
}
|
|
68
|
+
yield InteractionUtils_1.default.waitUntilLoaded(page, opArgs);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
beforeInteractions() {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
if (core_1.config.verbose) {
|
|
74
|
+
const browserVersion = yield this.page.browser().version();
|
|
75
|
+
core_1.info.lowLevel(`Browser version: ${browserVersion}`);
|
|
76
|
+
const nodeVersion = process.version;
|
|
77
|
+
core_1.info.lowLevel(`Node.js version: ${nodeVersion}`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
visitAndGetSnapshots(options = {}) {
|
|
82
|
+
var _a;
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
const visitPlan = options.testPlanner
|
|
85
|
+
? options.testPlanner.getVisitPlan()
|
|
86
|
+
: TestPlanner_1.default.getVisitPlan();
|
|
87
|
+
const runConfig = (_a = options.config) !== null && _a !== void 0 ? _a : core_1.config;
|
|
88
|
+
// notify the running mode the current visit plan
|
|
89
|
+
runConfig.runningMode.beforeRunning(visitPlan);
|
|
90
|
+
logMetaData(visitPlan);
|
|
91
|
+
yield setPermissions(this.page, TestPlanner_1.default.getOrigin());
|
|
92
|
+
yield this.beforeInteractions();
|
|
93
|
+
const baseURL = core_1.utils.normalizeBaseUrl(visitPlan.baseURL);
|
|
94
|
+
yield this.startTrackingHeap();
|
|
95
|
+
for (let i = 0; i < visitPlan.tabsOrder.length; i++) {
|
|
96
|
+
core_1.info.beginSection(`step-${i}`);
|
|
97
|
+
if (runConfig.verbose) {
|
|
98
|
+
core_1.info.lowLevel(new Date().toString());
|
|
99
|
+
}
|
|
100
|
+
const tab = visitPlan.tabsOrder[i];
|
|
101
|
+
const subUrl = tab.url.substr(tab.url.startsWith('/') ? 1 : 0);
|
|
102
|
+
const url = `${baseURL}${subUrl}` + getURLParameter(tab, visitPlan);
|
|
103
|
+
logTabProgress(i, visitPlan);
|
|
104
|
+
yield maybeWaitForConsoleInput(i + 1);
|
|
105
|
+
const opArgs = { isPageLoaded: visitPlan.isPageLoaded };
|
|
106
|
+
yield applyAsyncWithRetry(this.getPageStatistics, this, [url, tab, opArgs], {
|
|
107
|
+
retry: runConfig.interactionFailRetry,
|
|
108
|
+
});
|
|
109
|
+
// dump browser console output in a readable file
|
|
110
|
+
core_1.browserInfo.dump();
|
|
111
|
+
core_1.info.nextLine();
|
|
112
|
+
core_1.info.endSection(`step-${i}`);
|
|
113
|
+
}
|
|
114
|
+
// show progress on console
|
|
115
|
+
core_1.info.topLevel(core_1.serializer.summarizeTabsOrder(visitPlan.tabsOrder, {
|
|
116
|
+
color: true,
|
|
117
|
+
progress: visitPlan.tabsOrder.length,
|
|
118
|
+
}) + '\n');
|
|
119
|
+
// serialize the meta data again (with more runtime and browser info)
|
|
120
|
+
logMetaData(visitPlan, { final: true });
|
|
121
|
+
// dump browser console output in a readable file
|
|
122
|
+
core_1.browserInfo.dump();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
warmupInPage() {
|
|
126
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
const visitPlan = TestPlanner_1.default.getVisitPlan();
|
|
128
|
+
const baseURL = visitPlan.baseURL;
|
|
129
|
+
const len = visitPlan.tabsOrder.length;
|
|
130
|
+
const visited = Object.create(null);
|
|
131
|
+
// randomize order
|
|
132
|
+
const tabs = core_1.utils.shuffleArray(Array.from(visitPlan.tabsOrder));
|
|
133
|
+
const multipler = core_1.config.warmupRepeat;
|
|
134
|
+
for (let i = 0; i < len * multipler; ++i) {
|
|
135
|
+
const tab = tabs[i % len];
|
|
136
|
+
visited[tab.name] |= 0;
|
|
137
|
+
if (++visited[tab.name] > multipler) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// print current progress
|
|
141
|
+
if (core_1.config.isContinuousTest || core_1.config.verbose) {
|
|
142
|
+
let progress = `[${i + 1}/${len * multipler}]`;
|
|
143
|
+
progress = `${progress}: warming up web server (${tab.name})...`;
|
|
144
|
+
core_1.info.lowLevel(progress);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
core_1.info.progress(i, len * multipler, { message: 'Warming up web server' });
|
|
148
|
+
}
|
|
149
|
+
// print url
|
|
150
|
+
const urlParams = getURLParameter(tab, visitPlan);
|
|
151
|
+
const url = `${baseURL}${tab.url}${urlParams}`;
|
|
152
|
+
if (core_1.config.verbose) {
|
|
153
|
+
core_1.info.lowLevel(`url: ${url}`);
|
|
154
|
+
}
|
|
155
|
+
// warm up page
|
|
156
|
+
yield this.visitPage(url, {
|
|
157
|
+
mute: true,
|
|
158
|
+
isPageLoaded: visitPlan.isPageLoaded,
|
|
159
|
+
});
|
|
160
|
+
compareURL(this.page, url);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
visitPage(url, options = {}) {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
try {
|
|
167
|
+
yield this.page.goto(url, {
|
|
168
|
+
timeout: core_1.config.warmupPageLoadTimeout,
|
|
169
|
+
waitUntil: 'domcontentloaded',
|
|
170
|
+
});
|
|
171
|
+
yield InteractionUtils_1.default.waitUntilLoaded(this.page, options);
|
|
172
|
+
}
|
|
173
|
+
catch (ex) {
|
|
174
|
+
core_1.info.overwrite(core_1.utils.getError(ex).message);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
getPageStatistics(url, tabInfo, opArgs = {}) {
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
if (core_1.config.verbose) {
|
|
181
|
+
core_1.info.lowLevel('url: ' + url);
|
|
182
|
+
}
|
|
183
|
+
// visit the URL of the first step
|
|
184
|
+
if (tabInfo.idx === 1) {
|
|
185
|
+
yield applyAsyncWithRetry(this.initialLoad, this, [this.page, url, opArgs], {
|
|
186
|
+
retry: core_1.config.initialLoadFailRetry,
|
|
187
|
+
delayBeforeRetry: 3000,
|
|
188
|
+
});
|
|
189
|
+
// only use the interactions for steps other than the first step
|
|
190
|
+
}
|
|
191
|
+
else if (tabInfo.interactions) {
|
|
192
|
+
yield this.interactWithPage(this.page, tabInfo.interactions, opArgs);
|
|
193
|
+
}
|
|
194
|
+
if (tabInfo.type === 'final' || tabInfo.type === 'target') {
|
|
195
|
+
yield waitExtraForTab(tabInfo);
|
|
196
|
+
}
|
|
197
|
+
checkURL(this.page, url);
|
|
198
|
+
// inject marker, which checks if the page is reloaded
|
|
199
|
+
if (tabInfo.idx === 1) {
|
|
200
|
+
yield injectPageReloadChecker(this.page);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
yield checkPageReload(this.page);
|
|
204
|
+
}
|
|
205
|
+
yield this.fullGC(tabInfo);
|
|
206
|
+
// collect metrics
|
|
207
|
+
yield this.collectMetrics(tabInfo);
|
|
208
|
+
// take screenshot
|
|
209
|
+
const screenShotIdx = tabInfo.screenshot ? tabInfo.idx : 0;
|
|
210
|
+
if (core_1.config.runningMode.shouldTakeScreenShot(tabInfo) && screenShotIdx > 0) {
|
|
211
|
+
yield InteractionUtils_1.default.screenshot(this.page, screenShotIdx);
|
|
212
|
+
}
|
|
213
|
+
// take heap snapshot
|
|
214
|
+
const snapshotIdx = tabInfo.snapshot ? tabInfo.idx : 0;
|
|
215
|
+
if (core_1.config.runningMode.shouldTakeHeapSnapshot(tabInfo) && snapshotIdx > 0) {
|
|
216
|
+
const snapshotFile = path_1.default.join(core_1.config.curDataDir, `s${snapshotIdx}.heapsnapshot`);
|
|
217
|
+
yield this.saveHeapSnapshotToFile(snapshotFile);
|
|
218
|
+
}
|
|
219
|
+
if (tabInfo.postInteractions) {
|
|
220
|
+
yield this.interactWithPage(this.page, tabInfo.postInteractions, opArgs);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
interactWithPage(page, operations, opArgs = {}) {
|
|
225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
const args = Object.assign(Object.assign({}, opArgs), { pageHistoryLength: this.pageHistoryLength });
|
|
227
|
+
if (typeof operations === 'function') {
|
|
228
|
+
yield operations(page, args);
|
|
229
|
+
yield InteractionUtils_1.default.waitUntilLoaded(page, args);
|
|
230
|
+
}
|
|
231
|
+
else if (Array.isArray(operations)) {
|
|
232
|
+
for (const op of operations) {
|
|
233
|
+
yield this.interactWithPage(page, op, args);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else if (operations.kind) {
|
|
237
|
+
yield dispatchOperation(page, operations, args);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
core_1.utils.throwError(new Error('unknown operation'));
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
startTrackingHeap() {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
if (core_1.config.verbose) {
|
|
247
|
+
core_1.info.lowLevel('Start tracking JS heap');
|
|
248
|
+
}
|
|
249
|
+
const session = yield this.getCDPSession();
|
|
250
|
+
yield session.send('HeapProfiler.enable');
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
writeSnapshotFileFromCDPSession(file, session) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const writeStream = fs_1.default.createWriteStream(file, { encoding: 'UTF-8' });
|
|
256
|
+
let lastChunk = '';
|
|
257
|
+
const dataHandler = (data) => {
|
|
258
|
+
writeStream.write(data.chunk);
|
|
259
|
+
lastChunk = data.chunk;
|
|
260
|
+
};
|
|
261
|
+
const progressHander = (data) => {
|
|
262
|
+
const percent = ((100 * data.done) / data.total) | 0;
|
|
263
|
+
if (!core_1.config.isContinuousTest) {
|
|
264
|
+
core_1.info.overwrite(`heap snapshot ${percent}% complete`);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
session.on('HeapProfiler.addHeapSnapshotChunk', dataHandler);
|
|
268
|
+
session.on('HeapProfiler.reportHeapSnapshotProgress', progressHander);
|
|
269
|
+
// start taking heap snapshot
|
|
270
|
+
yield session.send('HeapProfiler.takeHeapSnapshot', {
|
|
271
|
+
reportProgress: true,
|
|
272
|
+
captureNumericValue: true,
|
|
273
|
+
});
|
|
274
|
+
checkLastSnapshotChunk(lastChunk);
|
|
275
|
+
session.removeListener('HeapProfiler.addHeapSnapshotChunk', dataHandler);
|
|
276
|
+
session.removeListener('HeapProfiler.reportHeapSnapshotProgress', progressHander);
|
|
277
|
+
writeStream.end();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
saveHeapSnapshotToFile(file) {
|
|
281
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
282
|
+
core_1.info.beginSection('heap snapshot');
|
|
283
|
+
const start = Date.now();
|
|
284
|
+
const session = yield this.getCDPSession();
|
|
285
|
+
yield this.writeSnapshotFileFromCDPSession(file, session);
|
|
286
|
+
const spanMs = Date.now() - start;
|
|
287
|
+
if (core_1.config.verbose) {
|
|
288
|
+
core_1.info.lowLevel(`duration: ${core_1.utils.getReadableTime(spanMs)}`);
|
|
289
|
+
}
|
|
290
|
+
core_1.info.overwrite('snapshot saved to disk');
|
|
291
|
+
core_1.info.endSection('heap snapshot');
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
fullGC(tabInfo) {
|
|
295
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
+
if (!core_1.config.runningMode.shouldGC(tabInfo)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (core_1.config.clearConsole) {
|
|
300
|
+
yield clearConsole(this.page);
|
|
301
|
+
core_1.info.overwrite('running a full GC (clear console)...');
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
core_1.info.overwrite('running a full GC...');
|
|
305
|
+
}
|
|
306
|
+
// force GC 6 times to release feedback_cells
|
|
307
|
+
yield this.forceGC(6);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
forceGC(repeat = 1) {
|
|
311
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
312
|
+
const client = yield this.getCDPSession();
|
|
313
|
+
for (let i = 0; i < repeat; i++) {
|
|
314
|
+
yield client.send('HeapProfiler.collectGarbage');
|
|
315
|
+
// wait for a while and let GC do the job
|
|
316
|
+
yield InteractionUtils_1.default.waitFor(200);
|
|
317
|
+
}
|
|
318
|
+
yield InteractionUtils_1.default.waitFor(core_1.config.waitAfterGC);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
collectMetrics(tabInfo) {
|
|
322
|
+
var _a;
|
|
323
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
324
|
+
// collect navigation history info
|
|
325
|
+
const historyLength = yield getNavigationHistoryLength(this.page);
|
|
326
|
+
this.pageHistoryLength.push(historyLength);
|
|
327
|
+
if (!core_1.config.runningMode.shouldGetMetrics(tabInfo)) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
yield this.forceGC();
|
|
331
|
+
// collect heap size
|
|
332
|
+
const builtInMetrics = yield this.page.metrics();
|
|
333
|
+
const size = core_1.utils.getReadableBytes(builtInMetrics.JSHeapUsedSize);
|
|
334
|
+
core_1.info.midLevel(`Heap size: ${size}`);
|
|
335
|
+
tabInfo.JSHeapUsedSize = (_a = builtInMetrics.JSHeapUsedSize) !== null && _a !== void 0 ? _a : 0;
|
|
336
|
+
// collect additional metrics
|
|
337
|
+
const metrics = yield core_1.config.runningMode.getAdditionalMetrics(this.page, tabInfo);
|
|
338
|
+
for (const key of Object.keys(metrics)) {
|
|
339
|
+
if (Object.prototype.hasOwnProperty.call(tabInfo, key)) {
|
|
340
|
+
core_1.info.warning(`overwriting metrics: ${key}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
tabInfo.metrics = metrics;
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
exports.default = E2EInteractionManager;
|
|
@@ -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 { E2EScenarioSynthesizerConstructor } from '@memlab/core';
|
|
11
|
+
declare class TestRunnerLoader {
|
|
12
|
+
load(): E2EScenarioSynthesizerConstructor[];
|
|
13
|
+
}
|
|
14
|
+
declare const _default: TestRunnerLoader;
|
|
15
|
+
export default _default;
|
|
16
|
+
//# sourceMappingURL=TestRunnerLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestRunnerLoader.d.ts","sourceRoot":"","sources":["../src/TestRunnerLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAC,iCAAiC,EAAC,MAAM,cAAc,CAAC;AAIpE,cAAM,gBAAgB;IACpB,IAAI,IAAI,iCAAiC,EAAE;CAuB5C;;AAED,wBAAsC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const core_1 = require("@memlab/core");
|
|
17
|
+
const BaseSynthesizer_1 = __importDefault(require("./BaseSynthesizer"));
|
|
18
|
+
class TestRunnerLoader {
|
|
19
|
+
load() {
|
|
20
|
+
const ret = [];
|
|
21
|
+
const dir = path_1.default.join(__dirname, 'plugins');
|
|
22
|
+
core_1.fileManager.iterateAllFiles(dir, (file) => {
|
|
23
|
+
if (!file.endsWith('Synthesizer.js')) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
27
|
+
const importedEntity = require(file);
|
|
28
|
+
if (!importedEntity.default) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const constructor = importedEntity.default;
|
|
32
|
+
if (typeof constructor !== 'function' ||
|
|
33
|
+
!(constructor.prototype instanceof BaseSynthesizer_1.default)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
ret.push(constructor);
|
|
37
|
+
});
|
|
38
|
+
return ret;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.default = new TestRunnerLoader();
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
declare module 'xvfb' {
|
|
11
|
+
interface XvfbParams {
|
|
12
|
+
silent: boolean;
|
|
13
|
+
xvfb_args: string[];
|
|
14
|
+
timeout: number;
|
|
15
|
+
}
|
|
16
|
+
export default class Xvfb {
|
|
17
|
+
constructor(params: XvfbParams);
|
|
18
|
+
start: (callback: (error: Error) => unknown | null) => void;
|
|
19
|
+
stop: (callback: (error: Error) => unknown | null) => void;
|
|
20
|
+
startSync: () => void;
|
|
21
|
+
stopSync: () => void;
|
|
22
|
+
display: () => string;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=TypesThirdParty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TypesThirdParty.d.ts","sourceRoot":"","sources":["../src/TypesThirdParty.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,UAAU;QAClB,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB;IACD,MAAM,CAAC,OAAO,OAAO,IAAI;oBACX,MAAM,EAAE,UAAU;QAC9B,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;QAC5D,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;QAC3D,SAAS,EAAE,MAAM,IAAI,CAAC;QACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;QACrB,OAAO,EAAE,MAAM,MAAM,CAAC;KACvB;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
export { default as defaultTestPlanner } from './lib/operations/TestPlanner';
|
|
11
|
+
export * from './lib/operations/TestPlanner';
|
|
12
|
+
export { default as Xvfb } from './lib/operations/XVirtualFrameBuffer';
|
|
13
|
+
export { default as E2EInteractionManager } from './E2EInteractionManager';
|
|
14
|
+
export { default as BaseSynthesizer } from './BaseSynthesizer';
|
|
15
|
+
export { default as E2EUtils } from './lib/E2EUtils';
|
|
16
|
+
//# 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,OAAO,EAAC,OAAO,IAAI,kBAAkB,EAAC,MAAM,8BAA8B,CAAC;AAC3E,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAC,OAAO,IAAI,IAAI,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAC,OAAO,IAAI,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAC,OAAO,IAAI,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAC,OAAO,IAAI,QAAQ,EAAC,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.E2EUtils = exports.BaseSynthesizer = exports.E2EInteractionManager = exports.Xvfb = exports.defaultTestPlanner = void 0;
|
|
30
|
+
var TestPlanner_1 = require("./lib/operations/TestPlanner");
|
|
31
|
+
Object.defineProperty(exports, "defaultTestPlanner", { enumerable: true, get: function () { return __importDefault(TestPlanner_1).default; } });
|
|
32
|
+
__exportStar(require("./lib/operations/TestPlanner"), exports);
|
|
33
|
+
var XVirtualFrameBuffer_1 = require("./lib/operations/XVirtualFrameBuffer");
|
|
34
|
+
Object.defineProperty(exports, "Xvfb", { enumerable: true, get: function () { return __importDefault(XVirtualFrameBuffer_1).default; } });
|
|
35
|
+
var E2EInteractionManager_1 = require("./E2EInteractionManager");
|
|
36
|
+
Object.defineProperty(exports, "E2EInteractionManager", { enumerable: true, get: function () { return __importDefault(E2EInteractionManager_1).default; } });
|
|
37
|
+
var BaseSynthesizer_1 = require("./BaseSynthesizer");
|
|
38
|
+
Object.defineProperty(exports, "BaseSynthesizer", { enumerable: true, get: function () { return __importDefault(BaseSynthesizer_1).default; } });
|
|
39
|
+
var E2EUtils_1 = require("./lib/E2EUtils");
|
|
40
|
+
Object.defineProperty(exports, "E2EUtils", { enumerable: true, get: function () { return __importDefault(E2EUtils_1).default; } });
|
|
@@ -0,0 +1,59 @@
|
|
|
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 { AnyOptions, AnyValue, E2EOperation, E2EStepInfo, IE2EScenarioVisitPlan, IScenario, Nullable, OperationArgs } from '@memlab/core';
|
|
11
|
+
import type { CDPSession, Page } from 'puppeteer';
|
|
12
|
+
declare type ExceptionHandler = (ex: Error) => void;
|
|
13
|
+
declare function checkLastSnapshotChunk(chunk: string): void;
|
|
14
|
+
declare function getURLParameter(tab: E2EStepInfo, visitPlan: IE2EScenarioVisitPlan): string;
|
|
15
|
+
declare function compareURL(page: Page, url: string): void;
|
|
16
|
+
declare function logTabProgress(i: number, visitPlan: IE2EScenarioVisitPlan): void;
|
|
17
|
+
declare function serializeVisitPlan(visitPlan: IE2EScenarioVisitPlan): void;
|
|
18
|
+
declare function logMetaData(visitPlan: IE2EScenarioVisitPlan, opt?: AnyOptions & {
|
|
19
|
+
final?: boolean;
|
|
20
|
+
}): void;
|
|
21
|
+
declare function setPermissions(page: Page, origin: Nullable<string>): Promise<void>;
|
|
22
|
+
declare function checkURL(page: Page, intendedURL: string): void;
|
|
23
|
+
declare function injectPageReloadChecker(page: Page): Promise<void>;
|
|
24
|
+
declare function checkPageReload(page: Page): Promise<void>;
|
|
25
|
+
declare function maybeWaitForConsoleInput(stepId: number): Promise<void>;
|
|
26
|
+
declare function applyAsyncWithGuard(f: (...args: AnyValue[]) => Promise<AnyValue>, self: AnyValue, args: AnyValue[], exceptionHandler?: ExceptionHandler): Promise<AnyValue>;
|
|
27
|
+
declare function applyAsyncWithRetry(f: (...args: AnyValue[]) => Promise<AnyValue>, self: AnyValue, args: AnyValue[], options?: AnyOptions & {
|
|
28
|
+
retry?: number;
|
|
29
|
+
delayBeforeRetry?: number;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
declare function clearConsole(page: Page): Promise<void>;
|
|
32
|
+
declare function dispatchOperation(page: Page, operation: E2EOperation, opArgs: OperationArgs): Promise<void>;
|
|
33
|
+
declare function waitExtraForTab(tabInfo: E2EStepInfo): Promise<void>;
|
|
34
|
+
declare function getNavigationHistoryLength(page: Page): Promise<number>;
|
|
35
|
+
declare function startTrackingHeapAllocation(page: Page, file: string): Promise<CDPSession>;
|
|
36
|
+
declare function getScenarioAppName(scenario?: Nullable<IScenario>): string;
|
|
37
|
+
declare const _default: {
|
|
38
|
+
applyAsyncWithGuard: typeof applyAsyncWithGuard;
|
|
39
|
+
applyAsyncWithRetry: typeof applyAsyncWithRetry;
|
|
40
|
+
checkLastSnapshotChunk: typeof checkLastSnapshotChunk;
|
|
41
|
+
checkPageReload: typeof checkPageReload;
|
|
42
|
+
checkURL: typeof checkURL;
|
|
43
|
+
clearConsole: typeof clearConsole;
|
|
44
|
+
compareURL: typeof compareURL;
|
|
45
|
+
dispatchOperation: typeof dispatchOperation;
|
|
46
|
+
getNavigationHistoryLength: typeof getNavigationHistoryLength;
|
|
47
|
+
getScenarioAppName: typeof getScenarioAppName;
|
|
48
|
+
getURLParameter: typeof getURLParameter;
|
|
49
|
+
injectPageReloadChecker: typeof injectPageReloadChecker;
|
|
50
|
+
logMetaData: typeof logMetaData;
|
|
51
|
+
logTabProgress: typeof logTabProgress;
|
|
52
|
+
maybeWaitForConsoleInput: typeof maybeWaitForConsoleInput;
|
|
53
|
+
serializeVisitPlan: typeof serializeVisitPlan;
|
|
54
|
+
setPermissions: typeof setPermissions;
|
|
55
|
+
startTrackingHeapAllocation: typeof startTrackingHeapAllocation;
|
|
56
|
+
waitExtraForTab: typeof waitExtraForTab;
|
|
57
|
+
};
|
|
58
|
+
export default _default;
|
|
59
|
+
//# sourceMappingURL=E2EUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2EUtils.d.ts","sourceRoot":"","sources":["../../src/lib/E2EUtils.ts"],"names":[],"mappings":"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"}
|