@testim/testim-cli 3.289.0 → 3.290.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/cli.js +22390 -122
- package/cli.js.map +1 -0
- package/npm-shrinkwrap.json +1951 -203
- package/package.json +9 -5
- package/OverrideTestDataBuilder.js +0 -117
- package/agent/routers/cliJsCode/index.js +0 -13
- package/agent/routers/cliJsCode/router.js +0 -63
- package/agent/routers/cliJsCode/service.js +0 -705
- package/agent/routers/codim/router.js +0 -69
- package/agent/routers/codim/router.test.js +0 -60
- package/agent/routers/codim/service.js +0 -193
- package/agent/routers/general/index.js +0 -36
- package/agent/routers/hybrid/registerRoutes.js +0 -81
- package/agent/routers/index.js +0 -56
- package/agent/routers/playground/router.js +0 -77
- package/agent/routers/playground/service.js +0 -96
- package/agent/routers/standalone-browser/registerRoutes.js +0 -47
- package/agent/server.js +0 -150
- package/cdpTestRunner.js +0 -86
- package/chromiumInstaller.js +0 -91
- package/cli/isCiRun.js +0 -10
- package/cli/onExit.js +0 -65
- package/cli/writeStackTrace.js +0 -27
- package/cliAgentMode.js +0 -384
- package/codim/codim-cli.js +0 -91
- package/codim/codim-npm-package/index.ts +0 -427
- package/codim/codim-npm-package/package-lock.json +0 -14
- package/codim/codim-npm-package/package.json +0 -14
- package/codim/hybrid-utils.js +0 -28
- package/codim/measure-perf.js +0 -41
- package/codim/template.js/.idea/workspace.xml +0 -57
- package/codim/template.js/.vscode/launch.json +0 -53
- package/codim/template.ts/.idea/workspace.xml +0 -57
- package/codim/template.ts/.vscode/launch.json +0 -55
- package/commons/AbortError.js +0 -12
- package/commons/SeleniumPerfStats.js +0 -58
- package/commons/chrome-launcher.js +0 -15
- package/commons/chromedriverWrapper.js +0 -70
- package/commons/config.js +0 -39
- package/commons/constants.js +0 -67
- package/commons/detectDebugger.js +0 -19
- package/commons/featureAvailabilityService.js +0 -26
- package/commons/featureFlags.js +0 -132
- package/commons/getSessionPlayerRequire.js +0 -28
- package/commons/httpRequest.js +0 -261
- package/commons/httpRequestCounters.js +0 -98
- package/commons/httpRequestCounters.test.js +0 -38
- package/commons/initializeUserWithAuth.js +0 -55
- package/commons/lazyRequire.js +0 -105
- package/commons/logUtils.js +0 -15
- package/commons/logUtils.test.js +0 -21
- package/commons/logger.js +0 -178
- package/commons/mockNetworkRuleFileSchema.json +0 -140
- package/commons/npmWrapper.js +0 -174
- package/commons/npmWrapper.test.js +0 -374
- package/commons/performance-logger.js +0 -71
- package/commons/preloadTests.js +0 -29
- package/commons/prepareRunner.js +0 -85
- package/commons/prepareRunner.test.js +0 -144
- package/commons/prepareRunnerAndTestimStartUtils.js +0 -198
- package/commons/prepareRunnerAndTestimStartUtils.test.js +0 -73
- package/commons/requireWithFallback.js +0 -25
- package/commons/runnerFileCache.js +0 -204
- package/commons/socket/baseSocketServiceSocketIO.js +0 -197
- package/commons/socket/realDataService.js +0 -59
- package/commons/socket/realDataServiceSocketIO.js +0 -33
- package/commons/socket/remoteStepService.js +0 -55
- package/commons/socket/remoteStepServiceSocketIO.js +0 -61
- package/commons/socket/socketService.js +0 -175
- package/commons/socket/testResultService.js +0 -62
- package/commons/socket/testResultServiceSocketIO.js +0 -64
- package/commons/testimAnalytics.js +0 -40
- package/commons/testimCloudflare.js +0 -83
- package/commons/testimCloudflare.test.js +0 -185
- package/commons/testimCustomToken.js +0 -124
- package/commons/testimDesiredCapabilitiesBuilder.js +0 -647
- package/commons/testimNgrok.js +0 -90
- package/commons/testimNgrok.test.js +0 -140
- package/commons/testimServicesApi.js +0 -631
- package/commons/testimTunnel.js +0 -73
- package/commons/testimTunnel.test.js +0 -172
- package/commons/xhr2.js +0 -897
- package/coverage/SummaryToObjectReport.js +0 -19
- package/coverage/jsCoverage.js +0 -252
- package/credentialsManager.js +0 -142
- package/errors.js +0 -161
- package/executionQueue.js +0 -37
- package/fixLocalBuild.js +0 -24
- package/inputFileUtils.js +0 -103
- package/lib/coralogix-winston.transport.js +0 -99
- package/player/SeleniumProtocolError.js +0 -100
- package/player/WebDriverHttpRequest.js +0 -177
- package/player/WebdriverioWebDriverApi.js +0 -671
- package/player/appiumTestPlayer.js +0 -90
- package/player/chromeLauncherTestPlayer.js +0 -67
- package/player/constants.js +0 -332
- package/player/extensionTestPlayer.js +0 -32
- package/player/findElementStrategy.js +0 -154
- package/player/scripts/isElementDisplayed.js +0 -252
- package/player/seleniumTestPlayer.js +0 -140
- package/player/services/frameLocator.js +0 -170
- package/player/services/mobileFrameLocatorMock.js +0 -32
- package/player/services/playbackTimeoutCalculator.js +0 -175
- package/player/services/portSelector.js +0 -19
- package/player/services/tabService.js +0 -551
- package/player/services/tabServiceMock.js +0 -167
- package/player/services/windowCreationListener.js +0 -8
- package/player/stepActions/RefreshStepAction.js +0 -16
- package/player/stepActions/apiStepAction.js +0 -89
- package/player/stepActions/baseCliJsStepAction.js +0 -51
- package/player/stepActions/baseJsStepAction.js +0 -277
- package/player/stepActions/cliConditionStepAction.js +0 -11
- package/player/stepActions/cliJsStepAction.js +0 -11
- package/player/stepActions/dropFileStepAction.js +0 -34
- package/player/stepActions/evaluateExpressionStepAction.js +0 -52
- package/player/stepActions/extensionOnlyStepAction.js +0 -12
- package/player/stepActions/extractTextStepAction.js +0 -19
- package/player/stepActions/hoverStepAction.js +0 -55
- package/player/stepActions/inputFileStepAction.js +0 -199
- package/player/stepActions/jsCodeStepAction.js +0 -11
- package/player/stepActions/jsConditionStepAction.js +0 -11
- package/player/stepActions/locateStepAction.js +0 -159
- package/player/stepActions/mouseStepAction.js +0 -370
- package/player/stepActions/navigationStepAction.js +0 -29
- package/player/stepActions/nodePackageStepAction.js +0 -47
- package/player/stepActions/pixelValidationStepAction.js +0 -39
- package/player/stepActions/scripts/dispatchEvents.js +0 -282
- package/player/stepActions/scripts/doClick.js +0 -221
- package/player/stepActions/scripts/doDragPath.js +0 -225
- package/player/stepActions/scripts/doubleClick.js +0 -119
- package/player/stepActions/scripts/dropEvent.js +0 -63
- package/player/stepActions/scripts/focusElement.js +0 -46
- package/player/stepActions/scripts/html5dragAction.js +0 -56
- package/player/stepActions/scripts/html5dragActionV2.js +0 -312
- package/player/stepActions/scripts/runCode.js +0 -147
- package/player/stepActions/scripts/scroll.js +0 -90
- package/player/stepActions/scripts/selectOption.js +0 -51
- package/player/stepActions/scripts/setText.js +0 -415
- package/player/stepActions/scripts/wheel.js +0 -61
- package/player/stepActions/scrollStepAction.js +0 -96
- package/player/stepActions/selectOptionStepAction.js +0 -49
- package/player/stepActions/sfdcRecordedStepAction.js +0 -24
- package/player/stepActions/sfdcStepAction.js +0 -28
- package/player/stepActions/sleepStepAction.js +0 -12
- package/player/stepActions/specialKeyStepAction.js +0 -52
- package/player/stepActions/stepAction.js +0 -73
- package/player/stepActions/stepActionRegistrar.js +0 -111
- package/player/stepActions/submitStepAction.js +0 -12
- package/player/stepActions/tdkHybridStepAction.js +0 -18
- package/player/stepActions/textStepAction.js +0 -110
- package/player/stepActions/textValidationStepAction.js +0 -64
- package/player/stepActions/wheelStepAction.js +0 -41
- package/player/utils/cookieUtils.js +0 -39
- package/player/utils/eyeSdkService.js +0 -250
- package/player/utils/imageCaptureUtils.js +0 -267
- package/player/utils/screenshotUtils.js +0 -68
- package/player/utils/stepActionUtils.js +0 -90
- package/player/utils/windowUtils.js +0 -195
- package/player/webDriverUtils.js +0 -40
- package/player/webDriverUtils.test.js +0 -116
- package/player/webdriver.js +0 -976
- package/polyfills/Array.prototype.at.js +0 -13
- package/polyfills/index.js +0 -13
- package/processHandler.js +0 -79
- package/processHandler.test.js +0 -55
- package/reports/chromeReporter.js +0 -17
- package/reports/consoleReporter.js +0 -190
- package/reports/debugReporter.js +0 -82
- package/reports/jsonReporter.js +0 -55
- package/reports/junitReporter.js +0 -183
- package/reports/reporter.js +0 -166
- package/reports/reporterUtils.js +0 -54
- package/reports/teamCityReporter.js +0 -73
- package/runOptions.d.ts +0 -305
- package/runOptions.js +0 -1288
- package/runOptionsAgentFlow.js +0 -87
- package/runOptionsUtils.js +0 -60
- package/runner.js +0 -355
- package/runners/ParallelWorkerManager.js +0 -284
- package/runners/TestPlanRunner.js +0 -419
- package/runners/buildCodeTests.js +0 -159
- package/runners/runnerUtils.js +0 -81
- package/services/analyticsService.js +0 -96
- package/services/branchService.js +0 -29
- package/services/gridService.js +0 -357
- package/services/gridService.test.js +0 -357
- package/services/labFeaturesService.js +0 -64
- package/services/lambdatestService.js +0 -227
- package/services/lambdatestService.test.js +0 -353
- package/services/localRCASaver.js +0 -124
- package/stepPlayers/cliJsStepPlayback.js +0 -40
- package/stepPlayers/hybridStepPlayback.js +0 -140
- package/stepPlayers/nodePackageStepPlayback.js +0 -28
- package/stepPlayers/playwrightHybridStepPlayback.js +0 -61
- package/stepPlayers/puppeteerHybridStepPlayback.js +0 -76
- package/stepPlayers/remoteStepPlayback.js +0 -80
- package/stepPlayers/seleniumHybridStepPlayback.js +0 -84
- package/stepPlayers/tdkHybridStepPlayback.js +0 -112
- package/testRunHandler.js +0 -603
- package/testRunStatus.js +0 -567
- package/testimNpmDriver.js +0 -52
- package/utils/argsUtils.js +0 -91
- package/utils/argsUtils.test.js +0 -32
- package/utils/fsUtils.js +0 -174
- package/utils/index.js +0 -197
- package/utils/promiseUtils.js +0 -85
- package/utils/stringUtils.js +0 -98
- package/utils/stringUtils.test.js +0 -22
- package/utils/timeUtils.js +0 -25
- package/utils/utils.test.js +0 -27
- package/workers/BaseWorker.js +0 -498
- package/workers/BaseWorker.test.js +0 -186
- package/workers/WorkerAppium.js +0 -180
- package/workers/WorkerExtension.js +0 -192
- package/workers/WorkerExtensionSingleBrowser.js +0 -77
- package/workers/WorkerSelenium.js +0 -253
- package/workers/workerUtils.js +0 -20
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const fse = require('fs-extra');
|
|
9
|
-
const dataUriToBuffer = require('data-uri-to-buffer');
|
|
10
|
-
const utils = require('../../../utils');
|
|
11
|
-
const npmWrapper = require('../../../commons/npmWrapper');
|
|
12
|
-
const featureFlags = require('../../../commons/featureFlags');
|
|
13
|
-
const { spawn: threadSpawn, config } = require('threads');
|
|
14
|
-
const { TimeoutError } = require('../../../errors');
|
|
15
|
-
const { getLogger } = require('../../../commons/logger');
|
|
16
|
-
const { getS3Artifact } = require('../../../commons/testimServicesApi');
|
|
17
|
-
|
|
18
|
-
const logger = getLogger('cli-service');
|
|
19
|
-
|
|
20
|
-
/** @type {import('worker_threads') | false} */
|
|
21
|
-
let workerThreads;
|
|
22
|
-
|
|
23
|
-
config.set({
|
|
24
|
-
basepath: {
|
|
25
|
-
node: __dirname,
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
function convertWindowsBackslash(input) {
|
|
30
|
-
const isExtendedLengthPath = /^\\\\\?\\/.test(input);
|
|
31
|
-
const hasNonAscii = /[^\u0000-\u0080]+/.test(input); // eslint-disable-line no-control-regex
|
|
32
|
-
|
|
33
|
-
if (isExtendedLengthPath || hasNonAscii) {
|
|
34
|
-
return input;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return input.replace(/\\/g, '/');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @param {string} transactionId
|
|
42
|
-
* @param {any} incomingParams
|
|
43
|
-
* @param {any} context
|
|
44
|
-
* @param {any} code
|
|
45
|
-
* @param {Record<string, any>} packageLocalLocations
|
|
46
|
-
* @param {number=} timeout
|
|
47
|
-
* @param {string=} fileDataUrl
|
|
48
|
-
*/
|
|
49
|
-
function runCode(transactionId, incomingParams, context, code, packageLocalLocations = {}, timeout = undefined, fileDataUrl = undefined) {
|
|
50
|
-
const requireCode = Object.keys(packageLocalLocations).reduce((all, pMember) => {
|
|
51
|
-
all += `
|
|
52
|
-
var ${pMember} = require('${convertWindowsBackslash(packageLocalLocations[pMember])}');
|
|
53
|
-
`;
|
|
54
|
-
return all;
|
|
55
|
-
}, '');
|
|
56
|
-
|
|
57
|
-
const fileDataUrlToFileBuffer = !fileDataUrl ? 'var fileBuffer = null;' :
|
|
58
|
-
`
|
|
59
|
-
${dataUriToBuffer.toString()}
|
|
60
|
-
var fileBuffer = dataUriToBuffer('${fileDataUrl}');
|
|
61
|
-
`;
|
|
62
|
-
|
|
63
|
-
function constructWithArguments(constructor, args) {
|
|
64
|
-
function F() {
|
|
65
|
-
return constructor.apply(this, args);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
F.prototype = constructor.prototype;
|
|
69
|
-
return new F();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
//https://github.com/anseki/console-substitute
|
|
73
|
-
const consoleOverride = `
|
|
74
|
-
|
|
75
|
-
const getMessage = arguments => {
|
|
76
|
-
const args = Array.prototype.slice.call(arguments);
|
|
77
|
-
let message = args.shift() + '';
|
|
78
|
-
if (!args.length) {
|
|
79
|
-
return message;
|
|
80
|
-
}
|
|
81
|
-
message = message.replace(/%([odifs])/g, function (s, param) {
|
|
82
|
-
// Formatting is not yet supported.
|
|
83
|
-
var arg;
|
|
84
|
-
if (!args.length) {
|
|
85
|
-
return '';
|
|
86
|
-
}
|
|
87
|
-
arg = args.shift();
|
|
88
|
-
if (param === 'o') {
|
|
89
|
-
return arg + '';
|
|
90
|
-
} else if (param === 'd' || param === 'i') {
|
|
91
|
-
arg = typeof arg === 'boolean' ? (arg ? 1 : 0) : parseInt(arg, 10);
|
|
92
|
-
return isNaN(arg) ? '0' : arg + '';
|
|
93
|
-
} else if (param === 'f') {
|
|
94
|
-
arg = typeof arg === 'boolean' ? (arg ? 1 : 0) : parseFloat(arg);
|
|
95
|
-
return isNaN(arg) ? '0.000000' : arg.toFixed(6) + '';
|
|
96
|
-
} else if (param === 's') {
|
|
97
|
-
return arg + '';
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
if (message) {
|
|
101
|
-
args.unshift(message);
|
|
102
|
-
}
|
|
103
|
-
return args.join(' ').replace(/\\s*$/, ' ');
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const pushNewMessage = (method, consoleArgs) => {
|
|
107
|
-
const message = getMessage(consoleArgs);
|
|
108
|
-
progress({
|
|
109
|
-
method,
|
|
110
|
-
msg: message,
|
|
111
|
-
timestamp: Date.now()
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
["log", "info", "warn", "error", "debug"].forEach(function (method) {
|
|
116
|
-
const nativeMethod = console[method];
|
|
117
|
-
const oldMethod = nativeMethod && nativeMethod.bind(console);
|
|
118
|
-
console[method] = function () {
|
|
119
|
-
pushNewMessage(method, arguments);
|
|
120
|
-
oldMethod && oldMethod.apply(console, arguments);
|
|
121
|
-
};
|
|
122
|
-
});
|
|
123
|
-
`;
|
|
124
|
-
|
|
125
|
-
const injectCode = `
|
|
126
|
-
function injectCode(params, args, incomingParams, context, code, done) {
|
|
127
|
-
${constructWithArguments.toString()}
|
|
128
|
-
|
|
129
|
-
var resolve = function (result) {
|
|
130
|
-
done({
|
|
131
|
-
status: 'done',
|
|
132
|
-
result: result,
|
|
133
|
-
success: true
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
|
-
var reject = function (result) {
|
|
137
|
-
done({
|
|
138
|
-
status: 'failed',
|
|
139
|
-
result: result,
|
|
140
|
-
success: false
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
params.push(code);
|
|
146
|
-
var functionToRun = constructWithArguments(Function, params);
|
|
147
|
-
|
|
148
|
-
var result = functionToRun.apply(null, args);
|
|
149
|
-
if (isPromise(result)) {
|
|
150
|
-
result.then(function (res) {
|
|
151
|
-
resolve({
|
|
152
|
-
resultValue: res,
|
|
153
|
-
exports: exportedData,
|
|
154
|
-
exportsTest: exportedTestData,
|
|
155
|
-
exportsGlobal: exportedGlobalData
|
|
156
|
-
});
|
|
157
|
-
}).catch(function (err) {
|
|
158
|
-
reject({
|
|
159
|
-
resultValue: err && err.toString(),
|
|
160
|
-
exports: exportedData,
|
|
161
|
-
exportsTest: exportedTestData,
|
|
162
|
-
exportsGlobal: exportedGlobalData
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
} else {
|
|
166
|
-
resolve({
|
|
167
|
-
resultValue: result,
|
|
168
|
-
exports: exportedData,
|
|
169
|
-
exportsTest: exportedTestData,
|
|
170
|
-
exportsGlobal: exportedGlobalData
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
} catch (e) {
|
|
174
|
-
reject({
|
|
175
|
-
resultValue: e && e.toString(),
|
|
176
|
-
exports: exportedData,
|
|
177
|
-
exportsTest: exportedTestData,
|
|
178
|
-
exportsGlobal: exportedGlobalData
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
`;
|
|
183
|
-
|
|
184
|
-
const runFn = `
|
|
185
|
-
${requireCode}
|
|
186
|
-
|
|
187
|
-
${fileDataUrlToFileBuffer}
|
|
188
|
-
|
|
189
|
-
${consoleOverride}
|
|
190
|
-
|
|
191
|
-
${utils.isPromise.toString()}
|
|
192
|
-
|
|
193
|
-
const {incomingParams, context, code} = input;
|
|
194
|
-
|
|
195
|
-
var exportedData = {};
|
|
196
|
-
var exportedTestData = {};
|
|
197
|
-
var exportedGlobalData = {};
|
|
198
|
-
|
|
199
|
-
var params = ["context"]
|
|
200
|
-
.concat(incomingParams.as.functionParameters)
|
|
201
|
-
.concat(${JSON.stringify(Object.keys(packageLocalLocations))})
|
|
202
|
-
.concat(['exports', 'exportsTest', 'exportsGlobal']);
|
|
203
|
-
|
|
204
|
-
var args = [context]
|
|
205
|
-
.concat(incomingParams.as.functionArguments)
|
|
206
|
-
.concat([${Object.keys(packageLocalLocations).join(',')}])
|
|
207
|
-
.concat([exportedData, exportedTestData, exportedGlobalData]);
|
|
208
|
-
|
|
209
|
-
if(fileBuffer) {
|
|
210
|
-
params = params.concat(['fileBuffer']);
|
|
211
|
-
args = args.concat([fileBuffer]);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
${injectCode}
|
|
215
|
-
|
|
216
|
-
injectCode(params, args, incomingParams, context, code, done);
|
|
217
|
-
`;
|
|
218
|
-
|
|
219
|
-
const testimConsoleLogDataAggregates = [];
|
|
220
|
-
const thread = threadSpawn(constructWithArguments(Function, ['input', 'done', 'progress', runFn]));
|
|
221
|
-
return utils.promiseTimeout(new Promise((resolve) => {
|
|
222
|
-
thread
|
|
223
|
-
.send({ incomingParams, context, code })
|
|
224
|
-
.on('message', message => {
|
|
225
|
-
const messageWithLogs = Object.assign({}, message, { tstConsoleLogs: testimConsoleLogDataAggregates });
|
|
226
|
-
logger.debug('Run code worker response', { messageWithLogs, transactionId });
|
|
227
|
-
resolve(messageWithLogs);
|
|
228
|
-
})
|
|
229
|
-
.on('progress', (logMessage) => {
|
|
230
|
-
testimConsoleLogDataAggregates.push(logMessage);
|
|
231
|
-
})
|
|
232
|
-
.on('error', (err) => {
|
|
233
|
-
if (err.message === 'malformed data: URI') {
|
|
234
|
-
logger.error('Run code worker error', { err, transactionId, fileDataUrl });
|
|
235
|
-
} else {
|
|
236
|
-
logger.error('Run code worker error', { err, transactionId });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
resolve({
|
|
240
|
-
tstConsoleLogs: testimConsoleLogDataAggregates,
|
|
241
|
-
status: 'failed',
|
|
242
|
-
result: {
|
|
243
|
-
resultValue: err?.toString(),
|
|
244
|
-
exports: {},
|
|
245
|
-
exportsTest: {},
|
|
246
|
-
exportsGlobal: {},
|
|
247
|
-
},
|
|
248
|
-
success: false,
|
|
249
|
-
});
|
|
250
|
-
})
|
|
251
|
-
.on('exit', () => {
|
|
252
|
-
logger.debug('Run code worker has been terminated', { transactionId });
|
|
253
|
-
});
|
|
254
|
-
}), timeout)
|
|
255
|
-
.catch(err => {
|
|
256
|
-
if (!(err instanceof utils.TimeoutError)) {
|
|
257
|
-
throw err;
|
|
258
|
-
}
|
|
259
|
-
logger.warn('timeout to run code', { transactionId, err });
|
|
260
|
-
return {
|
|
261
|
-
tstConsoleLogs: testimConsoleLogDataAggregates,
|
|
262
|
-
status: 'failed',
|
|
263
|
-
result: {
|
|
264
|
-
resultValue: err?.toString(),
|
|
265
|
-
exports: {},
|
|
266
|
-
exportsTest: {},
|
|
267
|
-
exportsGlobal: {},
|
|
268
|
-
},
|
|
269
|
-
success: false,
|
|
270
|
-
};
|
|
271
|
-
})
|
|
272
|
-
.finally(() => thread?.kill());
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/** @param {string} _path */
|
|
276
|
-
function requireOrImportMethod(_path) {
|
|
277
|
-
try {
|
|
278
|
-
return { sync: true, lib: require(_path) };
|
|
279
|
-
} catch (err) {
|
|
280
|
-
if (err.code === 'ERR_REQUIRE_ESM') {
|
|
281
|
-
const pathModule = require('path');
|
|
282
|
-
|
|
283
|
-
const lib = fs.promises.readFile(`${path}${pathModule.sep}package.json`).then(file => {
|
|
284
|
-
const packageJson = JSON.parse(file);
|
|
285
|
-
const fullPath = pathModule.join(_path, packageJson.main || `${pathModule.sep}index.js`);
|
|
286
|
-
return import(fullPath);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
return { sync: false, lib };
|
|
290
|
-
}
|
|
291
|
-
throw err;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* @param {string} transactionId
|
|
297
|
-
* @param {any} incomingParams
|
|
298
|
-
* @param {any} context
|
|
299
|
-
* @param {any} code
|
|
300
|
-
* @param {number=} timeout
|
|
301
|
-
*/
|
|
302
|
-
function runCodeWithWorkerThread(
|
|
303
|
-
transactionId,
|
|
304
|
-
incomingParams,
|
|
305
|
-
context,
|
|
306
|
-
code,
|
|
307
|
-
packageLocalLocations = {},
|
|
308
|
-
timeout = undefined,
|
|
309
|
-
fileDataUrl = undefined,
|
|
310
|
-
) {
|
|
311
|
-
// technically shouldn't happen, but better safe than sorry.
|
|
312
|
-
if (!workerThreads) {
|
|
313
|
-
workerThreads = require('worker_threads');
|
|
314
|
-
}
|
|
315
|
-
const { Worker } = workerThreads;
|
|
316
|
-
const requireCode = Object.keys(packageLocalLocations).reduce((all, pMember) => {
|
|
317
|
-
all += `
|
|
318
|
-
var res = requireOrImportMethod('${convertWindowsBackslash(packageLocalLocations[pMember])}');
|
|
319
|
-
if (res.sync) {
|
|
320
|
-
var ${pMember} = res.lib;
|
|
321
|
-
} else {
|
|
322
|
-
var ${pMember} = await res.lib;
|
|
323
|
-
}
|
|
324
|
-
`;
|
|
325
|
-
return all;
|
|
326
|
-
}, '');
|
|
327
|
-
|
|
328
|
-
const fileDataUrlToFileBuffer = !fileDataUrl ? 'var fileBuffer = null;' :
|
|
329
|
-
`
|
|
330
|
-
${dataUriToBuffer.toString()}
|
|
331
|
-
var fileBuffer = dataUriToBuffer('${fileDataUrl}');
|
|
332
|
-
`;
|
|
333
|
-
|
|
334
|
-
function constructWithArguments(constructor, args) {
|
|
335
|
-
function F() {
|
|
336
|
-
return constructor.apply(this, args);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
F.prototype = constructor.prototype;
|
|
340
|
-
return new F();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
//https://github.com/anseki/console-substitute
|
|
344
|
-
// note that this method is a bit different than the one in the non-worker one.
|
|
345
|
-
const consoleOverride = `
|
|
346
|
-
const getMessage = arguments => {
|
|
347
|
-
const args = Array.prototype.slice.call(arguments);
|
|
348
|
-
let message = args.shift() + '';
|
|
349
|
-
if (!args.length) {
|
|
350
|
-
return message;
|
|
351
|
-
}
|
|
352
|
-
message = message.replace(/%([odifs])/g, function (s, param) {
|
|
353
|
-
// Formatting is not yet supported.
|
|
354
|
-
var arg;
|
|
355
|
-
if (!args.length) {
|
|
356
|
-
return '';
|
|
357
|
-
}
|
|
358
|
-
arg = args.shift();
|
|
359
|
-
if (param === 'o') {
|
|
360
|
-
return arg + '';
|
|
361
|
-
} else if (param === 'd' || param === 'i') {
|
|
362
|
-
arg = typeof arg === 'boolean' ? (arg ? 1 : 0) : parseInt(arg, 10);
|
|
363
|
-
return isNaN(arg) ? '0' : arg + '';
|
|
364
|
-
} else if (param === 'f') {
|
|
365
|
-
arg = typeof arg === 'boolean' ? (arg ? 1 : 0) : parseFloat(arg);
|
|
366
|
-
return isNaN(arg) ? '0.000000' : arg.toFixed(6) + '';
|
|
367
|
-
} else if (param === 's') {
|
|
368
|
-
return arg + '';
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
if (message) {
|
|
372
|
-
args.unshift(message);
|
|
373
|
-
}
|
|
374
|
-
return args.join(' ').replace(/\\s*$/, ' ');
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
const pushNewMessage = (method, consoleArgs) => {
|
|
378
|
-
const message = getMessage(consoleArgs);
|
|
379
|
-
parentPort.postMessage({
|
|
380
|
-
action: 'progress',
|
|
381
|
-
data: {
|
|
382
|
-
method,
|
|
383
|
-
msg: message,
|
|
384
|
-
timestamp: Date.now(),
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
["log", "info", "warn", "error", "debug"].forEach(function (method) {
|
|
390
|
-
const nativeMethod = console[method];
|
|
391
|
-
const oldMethod = nativeMethod && nativeMethod.bind(console);
|
|
392
|
-
console[method] = function () {
|
|
393
|
-
pushNewMessage(method, arguments);
|
|
394
|
-
oldMethod && oldMethod.apply(console, arguments);
|
|
395
|
-
};
|
|
396
|
-
});
|
|
397
|
-
`;
|
|
398
|
-
|
|
399
|
-
const injectCode = `
|
|
400
|
-
function injectCode(params, args, incomingParams, context, code) {
|
|
401
|
-
${constructWithArguments.toString()}
|
|
402
|
-
|
|
403
|
-
var resolve = function (result) {
|
|
404
|
-
parentPort.postMessage({
|
|
405
|
-
action: 'finish',
|
|
406
|
-
data: {
|
|
407
|
-
status: 'done',
|
|
408
|
-
result: result,
|
|
409
|
-
success: true,
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
};
|
|
413
|
-
var reject = function (result) {
|
|
414
|
-
parentPort.postMessage({
|
|
415
|
-
action: 'finish',
|
|
416
|
-
data: {
|
|
417
|
-
status: 'failed',
|
|
418
|
-
result: result,
|
|
419
|
-
success: false,
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
try {
|
|
425
|
-
params.push(code);
|
|
426
|
-
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
427
|
-
var functionToRun = constructWithArguments(AsyncFunction, params);
|
|
428
|
-
|
|
429
|
-
var result = functionToRun.apply(null, args);
|
|
430
|
-
if (isPromise(result)) {
|
|
431
|
-
result.then(function (res) {
|
|
432
|
-
resolve({
|
|
433
|
-
resultValue: res,
|
|
434
|
-
exports: exportedData,
|
|
435
|
-
exportsTest: exportedTestData,
|
|
436
|
-
exportsGlobal: exportedGlobalData
|
|
437
|
-
});
|
|
438
|
-
}).catch(function (err) {
|
|
439
|
-
reject({
|
|
440
|
-
resultValue: err && err.toString(),
|
|
441
|
-
exports: exportedData,
|
|
442
|
-
exportsTest: exportedTestData,
|
|
443
|
-
exportsGlobal: exportedGlobalData
|
|
444
|
-
});
|
|
445
|
-
});
|
|
446
|
-
} else {
|
|
447
|
-
resolve({
|
|
448
|
-
resultValue: result,
|
|
449
|
-
exports: exportedData,
|
|
450
|
-
exportsTest: exportedTestData,
|
|
451
|
-
exportsGlobal: exportedGlobalData
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
} catch (e) {
|
|
455
|
-
reject({
|
|
456
|
-
resultValue: e && e.toString(),
|
|
457
|
-
exports: exportedData,
|
|
458
|
-
exportsTest: exportedTestData,
|
|
459
|
-
exportsGlobal: exportedGlobalData
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
`;
|
|
464
|
-
|
|
465
|
-
const runFn = `
|
|
466
|
-
(async function() {
|
|
467
|
-
const { parentPort } = require('worker_threads');
|
|
468
|
-
${requireOrImportMethod}
|
|
469
|
-
|
|
470
|
-
// requireCode will set async to be true if needed.
|
|
471
|
-
${requireCode}
|
|
472
|
-
|
|
473
|
-
${fileDataUrlToFileBuffer}
|
|
474
|
-
|
|
475
|
-
${consoleOverride}
|
|
476
|
-
|
|
477
|
-
${utils.isPromise.toString()}
|
|
478
|
-
|
|
479
|
-
parentPort.once('message', input => {
|
|
480
|
-
const {incomingParams, context, code} = input;
|
|
481
|
-
|
|
482
|
-
var exportedData = {};
|
|
483
|
-
var exportedTestData = {};
|
|
484
|
-
var exportedGlobalData = {};
|
|
485
|
-
|
|
486
|
-
var params = ["context"]
|
|
487
|
-
.concat(incomingParams.as.functionParameters)
|
|
488
|
-
.concat(${JSON.stringify(Object.keys(packageLocalLocations))})
|
|
489
|
-
.concat(['exports', 'exportsTest', 'exportsGlobal']);
|
|
490
|
-
|
|
491
|
-
var args = [context]
|
|
492
|
-
.concat(incomingParams.as.functionArguments)
|
|
493
|
-
.concat([${Object.keys(packageLocalLocations).join(',')}])
|
|
494
|
-
.concat([exportedData, exportedTestData, exportedGlobalData]);
|
|
495
|
-
|
|
496
|
-
if(fileBuffer) {
|
|
497
|
-
params = params.concat(['fileBuffer']);
|
|
498
|
-
args = args.concat([fileBuffer]);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
${injectCode}
|
|
502
|
-
|
|
503
|
-
injectCode(params, args, incomingParams, context, code);
|
|
504
|
-
});
|
|
505
|
-
})();
|
|
506
|
-
`;
|
|
507
|
-
|
|
508
|
-
const testimConsoleLogDataAggregates = [];
|
|
509
|
-
const thread = new Worker(runFn, {
|
|
510
|
-
eval: true,
|
|
511
|
-
});
|
|
512
|
-
return utils.promiseTimeout(new Promise((resolve) => {
|
|
513
|
-
thread
|
|
514
|
-
.on('message', message => {
|
|
515
|
-
if (message.action === 'finish') {
|
|
516
|
-
const { data } = message;
|
|
517
|
-
const messageWithLogs = Object.assign({}, data, { tstConsoleLogs: testimConsoleLogDataAggregates });
|
|
518
|
-
logger.debug('Run code worker response', { messageWithLogs, transactionId });
|
|
519
|
-
resolve(messageWithLogs);
|
|
520
|
-
} else if (message.action === 'progress') {
|
|
521
|
-
testimConsoleLogDataAggregates.push(message.data);
|
|
522
|
-
}
|
|
523
|
-
})
|
|
524
|
-
.on('error', (err) => {
|
|
525
|
-
if (err.message === 'malformed data: URI') {
|
|
526
|
-
logger.error('Run code worker error', { err, transactionId, fileDataUrl });
|
|
527
|
-
} else {
|
|
528
|
-
logger.error('Run code worker error', { err, transactionId });
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
resolve({
|
|
532
|
-
tstConsoleLogs: testimConsoleLogDataAggregates,
|
|
533
|
-
status: 'failed',
|
|
534
|
-
result: {
|
|
535
|
-
resultValue: err?.toString(),
|
|
536
|
-
exports: {},
|
|
537
|
-
exportsTest: {},
|
|
538
|
-
exportsGlobal: {},
|
|
539
|
-
},
|
|
540
|
-
success: false,
|
|
541
|
-
});
|
|
542
|
-
})
|
|
543
|
-
.on('exit', () => {
|
|
544
|
-
logger.debug('Run code worker has been terminated', { transactionId });
|
|
545
|
-
});
|
|
546
|
-
// context can contain methods and proxies which cannot pass.
|
|
547
|
-
thread.postMessage({ incomingParams, context: JSON.parse(JSON.stringify(context)), code });
|
|
548
|
-
}), timeout)
|
|
549
|
-
.catch(err => {
|
|
550
|
-
if (!(err instanceof utils.TimeoutError)) {
|
|
551
|
-
throw err;
|
|
552
|
-
}
|
|
553
|
-
logger.warn('timeout to run code', { transactionId, err });
|
|
554
|
-
return {
|
|
555
|
-
tstConsoleLogs: testimConsoleLogDataAggregates,
|
|
556
|
-
status: 'failed',
|
|
557
|
-
result: {
|
|
558
|
-
resultValue: err?.toString(),
|
|
559
|
-
exports: {},
|
|
560
|
-
exportsTest: {},
|
|
561
|
-
exportsGlobal: {},
|
|
562
|
-
},
|
|
563
|
-
success: false,
|
|
564
|
-
};
|
|
565
|
-
})
|
|
566
|
-
.finally(() => thread?.terminate());
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/** @param {string} installFolder */
|
|
570
|
-
async function removeFolder(installFolder) {
|
|
571
|
-
try {
|
|
572
|
-
await fse.remove(installFolder);
|
|
573
|
-
} catch (err) {
|
|
574
|
-
logger.warn('failed to remove install npm packages folder', { err });
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* @param {string} stepResultId
|
|
580
|
-
* @param {string} testResultId
|
|
581
|
-
* @param {string} stepId
|
|
582
|
-
* @param {number} retryIndex
|
|
583
|
-
*/
|
|
584
|
-
function getTransactionId(stepResultId, testResultId, stepId, retryIndex) {
|
|
585
|
-
return `${testResultId}_${stepId}_${stepResultId}_${retryIndex}`;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* @param {string} stepId
|
|
590
|
-
* @param {string} testResultId
|
|
591
|
-
* @param {number} retryIndex
|
|
592
|
-
* @param {{ packageName: string; packageVersion: string }[]} packageData
|
|
593
|
-
* @param {string} stepResultId
|
|
594
|
-
* @param {number} timeout
|
|
595
|
-
*/
|
|
596
|
-
function installPackage(stepId, testResultId, retryIndex, packageData, stepResultId, timeout) {
|
|
597
|
-
const transactionId = getTransactionId(stepResultId, testResultId, stepId, retryIndex);
|
|
598
|
-
return runNpmInstall(transactionId, packageData, timeout).then(({ data }) => data);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function runCodeWithPackages(code, stepId, incomingParams, context, testResultId, retryIndex, stepResultId, timeout, fileDataUrl, s3filepath) {
|
|
602
|
-
const packageLocalLocations = (incomingParams.nodePackageParams || []).reduce((packages, data) => {
|
|
603
|
-
packages[data.paramName] = data.testimPackageLocalLocation;
|
|
604
|
-
return packages;
|
|
605
|
-
}, {});
|
|
606
|
-
const transactionId = getTransactionId(stepResultId, testResultId, stepId, retryIndex);
|
|
607
|
-
|
|
608
|
-
const getS3ArtifactPromise = s3filepath ?
|
|
609
|
-
getS3Artifact(s3filepath) :
|
|
610
|
-
Promise.resolve();
|
|
611
|
-
|
|
612
|
-
return getS3ArtifactPromise.then(s3fileDataUrl => {
|
|
613
|
-
if (s3fileDataUrl) {
|
|
614
|
-
fileDataUrl = s3fileDataUrl;
|
|
615
|
-
}
|
|
616
|
-
}).then(() => {
|
|
617
|
-
if (typeof workerThreads === 'undefined') {
|
|
618
|
-
try {
|
|
619
|
-
workerThreads = require('worker_threads');
|
|
620
|
-
} catch (err) {
|
|
621
|
-
workerThreads = false;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (Buffer.isBuffer(fileDataUrl)) { // Fix, S3 is returning a buffer and not a string on empty files
|
|
626
|
-
logger.debug('runCodeWithPackages: converted a buffer to base 64 string data URI', { fileDataUrl });
|
|
627
|
-
fileDataUrl = fileDataUrl.toString();
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
const emptyFileDataUrl = 'data:,';
|
|
631
|
-
if (fileDataUrl === 'data:') { // Fix chrome/safari bug that creates malformed data-uri for empty files
|
|
632
|
-
logger.debug('runCodeWithPackages, fileDataUrl was an empty string ', { fileDataUrl });
|
|
633
|
-
fileDataUrl = emptyFileDataUrl;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
if (workerThreads && featureFlags.flags.enableWorkerThreadsCliCodeExecution.isEnabled()) {
|
|
637
|
-
return runCodeWithWorkerThread(transactionId, incomingParams, context, code, packageLocalLocations, timeout, fileDataUrl);
|
|
638
|
-
}
|
|
639
|
-
return runCode(transactionId, incomingParams, context, code, packageLocalLocations, timeout, fileDataUrl);
|
|
640
|
-
}).then(res => Object.assign({}, res, { nodeVersion: process.version }));
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* @param {string} transactionId
|
|
645
|
-
* @param {{ packageName: string; packageVersion: string }[]} packageData
|
|
646
|
-
* @param {number} timeout
|
|
647
|
-
*/
|
|
648
|
-
async function runNpmInstall(transactionId, packageData, timeout) {
|
|
649
|
-
const packages = packageData.map(data => `${data.packageName}@${data.packageVersion}`);
|
|
650
|
-
const localPackageInstallFolder = getLocalPackageInstallFolder();
|
|
651
|
-
const installFolder = path.join(localPackageInstallFolder, `/${transactionId}`);
|
|
652
|
-
const proxyUri = global.proxyUri;
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
async function npmInstallation() {
|
|
656
|
-
let output = '';
|
|
657
|
-
try {
|
|
658
|
-
output = await npmWrapper.installPackages(installFolder, packages, proxyUri, timeout);
|
|
659
|
-
logger.info('npm package install finished', { transactionId, output, timeout });
|
|
660
|
-
if (Number(output.trim().split(' ')[1]) < packages.length) {
|
|
661
|
-
// TODO: I am not sure changing this to an error would be safe.
|
|
662
|
-
// eslint-disable-next-line no-throw-literal
|
|
663
|
-
throw 'npm package install failed, couldn\'t install all packages';
|
|
664
|
-
}
|
|
665
|
-
const packageDataWithVersions = packageData.map(pData => {
|
|
666
|
-
const version = npmWrapper.getLocallyInstalledPackageVersion(installFolder, pData.packageName);
|
|
667
|
-
const packageFullName = `${pData.packageName}@${version}`;
|
|
668
|
-
const packageLocalLocation = path.resolve(installFolder, 'node_modules', pData.packageName);
|
|
669
|
-
return Object.assign({}, pData, {
|
|
670
|
-
packageFullName,
|
|
671
|
-
packageLocalLocation,
|
|
672
|
-
});
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
return { data: packageDataWithVersions, installFolder };
|
|
676
|
-
} catch (err) {
|
|
677
|
-
logger.warn('npm package install failed', { transactionId, err });
|
|
678
|
-
throw err;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
try {
|
|
683
|
-
return await utils.promiseTimeout(npmInstallation(), timeout);
|
|
684
|
-
} catch (err) {
|
|
685
|
-
if (err instanceof TimeoutError) {
|
|
686
|
-
logger.warn('timeout to install package', { packages, transactionId, err, timeout });
|
|
687
|
-
}
|
|
688
|
-
throw err;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function getLocalPackageInstallFolder() {
|
|
693
|
-
return path.join(os.tmpdir(), '/testim_local_packages');
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function cleanLocalPackageInstallFolder() {
|
|
697
|
-
const localPackageInstallFolder = getLocalPackageInstallFolder();
|
|
698
|
-
return removeFolder(localPackageInstallFolder);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
module.exports = {
|
|
702
|
-
runCodeWithPackages,
|
|
703
|
-
installPackage,
|
|
704
|
-
cleanLocalPackageInstallFolder,
|
|
705
|
-
};
|