@testim/testim-cli 3.194.0 → 3.198.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 +1 -1
- package/cli/onExit.js +12 -1
- package/cli.js +5 -1
- package/commons/constants.js +0 -25
- package/commons/featureFlags.js +2 -0
- package/commons/npmWrapper.js +46 -14
- package/commons/npmWrapper.test.js +182 -6
- package/commons/socket/testResultService.js +4 -14
- package/commons/testimAnalytics.js +0 -1
- package/commons/testimDesiredCapabilitiesBuilder.js +0 -94
- package/commons/testimServicesApi.js +9 -79
- package/executionQueue.js +7 -4
- package/npm-shrinkwrap.json +959 -523
- package/package.json +3 -1
- package/player/stepActions/baseJsStepAction.js +5 -1
- package/player/stepActions/pixelValidationStepAction.js +28 -0
- package/player/stepActions/salesforceApexActionStepAction.js +9 -0
- package/player/stepActions/salesforceAutoLoginStepAction.js +5 -3
- package/player/stepActions/scripts/focusElement.js +5 -3
- package/player/stepActions/stepActionRegistrar.js +6 -48
- package/player/utils/eyeSdkService.js +230 -0
- package/reports/consoleReporter.js +0 -20
- package/reports/reporter.js +0 -21
- package/runOptions.js +13 -89
- package/runner.js +3 -44
- package/runners/{strategies/LocalStrategy.js → ParallelWorkerManager.js} +59 -68
- package/runners/TestPlanRunner.js +286 -67
- package/runners/runnerUtils.js +73 -0
- package/services/analyticsService.js +94 -0
- package/services/gridService.js +24 -16
- package/services/gridService.test.js +21 -21
- package/services/lambdatestService.js +1 -1
- package/testRunHandler.js +16 -2
- package/testRunStatus.js +17 -13
- package/utils.js +5 -5
- package/workers/BaseWorker.js +39 -39
- package/workers/BaseWorker.test.js +1 -1
- package/workers/WorkerExtensionSingleBrowser.js +6 -3
- package/commons/apkUploader/apkUploader.js +0 -46
- package/commons/apkUploader/apkUploaderFactory.js +0 -68
- package/commons/apkUploader/deviceFarmApkUploader.js +0 -41
- package/commons/apkUploader/saucelabsApkUploader.js +0 -36
- package/commons/apkUploader/testObjectApkUploader.js +0 -34
- package/player/mobile/mobileTestPlayer.js +0 -80
- package/player/mobile/mobileWebDriver.js +0 -155
- package/player/mobile/services/frameLocatorMock.js +0 -18
- package/player/mobile/services/mobilePortSelector.js +0 -22
- package/player/mobile/services/mobileTabService.js +0 -241
- package/player/mobile/utils/mobileScreenshotUtils.js +0 -46
- package/player/mobile/utils/mobileWindowUtils.js +0 -84
- package/player/stepActions/mobile/android/androidLocateStepAction.js +0 -122
- package/player/stepActions/mobile/android/androidLongClickStepAction.js +0 -12
- package/player/stepActions/mobile/android/androidScrollStepAction.js +0 -134
- package/player/stepActions/mobile/android/androidSpecialKeyStepAction.js +0 -22
- package/player/stepActions/mobile/android/androidSwipeStepAction.js +0 -32
- package/player/stepActions/mobile/androidGlobalActionStepAction.js +0 -12
- package/player/stepActions/mobile/androidTapStepAction.js +0 -19
- package/player/stepActions/mobile/androidTextChangeStepAction.js +0 -23
- package/player/stepActions/mobile/ios/iosLocateStepAction.js +0 -124
- package/player/stepActions/mobile/ios/iosScrollStepAction.js +0 -76
- package/runners/AnonymousTestPlanRunner.js +0 -106
- package/runners/BaseRunner.js +0 -42
- package/runners/BaseTestPlanRunner.js +0 -194
- package/runners/DeviceFarmRemoteRunner.js +0 -50
- package/runners/SchedulerRemoteRunner.js +0 -47
- package/runners/strategies/BaseStrategy.js +0 -86
- package/runners/strategies/DeviceFarmStrategy.js +0 -195
- package/runners/strategies/LocalDeviceFarmStrategy.js +0 -12
- package/runners/strategies/LocalTestStrategy.js +0 -14
- package/runners/strategies/Strategy.js +0 -17
- package/workers/WorkerAppium.js +0 -70
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
# TESTIM.IO
|
|
3
|
-
## Web
|
|
3
|
+
## Web Test Automation Solution. Built for agile teams. Testim is a cloud service that instantly enables Test Automation to make your Continuous Delivery ready.
|
|
4
4
|
|
|
5
5
|
For more information please check out https://testim.io and https://help.testim.io/docs
|
|
6
6
|
|
package/cli/onExit.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const constants = require('../commons/constants');
|
|
4
|
-
const utils = require('../utils');
|
|
5
4
|
const { requireWithFallback } = require('../commons/requireWithFallback');
|
|
6
5
|
const { isCi } = require('./isCiRun');
|
|
7
6
|
const { writeStackTrace } = require('./writeStackTrace');
|
|
8
7
|
const logger = require('../commons/logger').getLogger('process-handler');
|
|
9
8
|
|
|
9
|
+
let exitCodeIgnoreFailingTests = false;
|
|
10
|
+
|
|
10
11
|
function getExitCode(result) {
|
|
11
12
|
if (result instanceof Error) {
|
|
12
13
|
return 1;
|
|
13
14
|
}
|
|
15
|
+
|
|
16
|
+
if (exitCodeIgnoreFailingTests) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
result = result || {};
|
|
15
21
|
const hasFailedTests = Object.values(result).some(
|
|
16
22
|
({ runnerStatus, success, testStatus, status }) => {
|
|
@@ -32,9 +38,13 @@ function closeChromeDriverIfRunning() {
|
|
|
32
38
|
try {
|
|
33
39
|
const chromedriver = requireWithFallback('chromedriver');
|
|
34
40
|
chromedriver.stop();
|
|
41
|
+
// eslint-disable-next-line no-empty
|
|
35
42
|
} catch (err) { }
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
module.exports.ignoreFailingTestsInExitCode = function () {
|
|
46
|
+
exitCodeIgnoreFailingTests = true;
|
|
47
|
+
};
|
|
38
48
|
|
|
39
49
|
|
|
40
50
|
module.exports.onExit = async function onExit(exitValue) {
|
|
@@ -42,6 +52,7 @@ module.exports.onExit = async function onExit(exitValue) {
|
|
|
42
52
|
if (!isCi) {
|
|
43
53
|
writeStackTrace(exitValue);
|
|
44
54
|
} else {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
45
56
|
console.error(exitValue, exitValue && exitValue.stack);
|
|
46
57
|
}
|
|
47
58
|
}
|
package/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ require('./bluebirdConfig.js');
|
|
|
7
7
|
const options = require('./runOptions');
|
|
8
8
|
const EventEmitter = require('events');
|
|
9
9
|
const logger = require('./commons/logger').getLogger('cli-entry');
|
|
10
|
-
const { onExit } = require('./cli/onExit');
|
|
10
|
+
const { onExit, ignoreFailingTestsInExitCode } = require('./cli/onExit');
|
|
11
11
|
const testRunner = require('./runner');
|
|
12
12
|
const prepareRunner = require('./commons/prepareRunner');
|
|
13
13
|
const { CLI_MODE } = require('./commons/constants');
|
|
@@ -92,6 +92,10 @@ function main() {
|
|
|
92
92
|
options.localRCASaver = `http://localhost:${port}`;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
if (options.exitCodeIgnoreFailingTests) {
|
|
96
|
+
ignoreFailingTestsInExitCode();
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
perf.log('right before testRunner.init/prepareRunner.prepare');
|
|
96
100
|
return Promise.all([
|
|
97
101
|
testRunner.init(options),
|
package/commons/constants.js
CHANGED
|
@@ -33,29 +33,6 @@ module.exports = {
|
|
|
33
33
|
test: {
|
|
34
34
|
HIDDEN_PARAM: 'TST_HIDDEN_PARAM',
|
|
35
35
|
},
|
|
36
|
-
deviceFarm: {
|
|
37
|
-
status: {
|
|
38
|
-
INITIALIZING: 'INITIALIZING',
|
|
39
|
-
PENDING: 'PENDING',
|
|
40
|
-
PENDING_CONCURRENCY: 'PENDING_CONCURRENCY',
|
|
41
|
-
PENDING_DEVICE: 'PENDING_DEVICE',
|
|
42
|
-
PROCESSING: 'PROCESSING',
|
|
43
|
-
SCHEDULING: 'SCHEDULING',
|
|
44
|
-
PREPARING: 'PREPARING',
|
|
45
|
-
RUNNING: 'RUNNING',
|
|
46
|
-
COMPLETED: 'COMPLETED',
|
|
47
|
-
STOPPING: 'STOPPING',
|
|
48
|
-
},
|
|
49
|
-
result: {
|
|
50
|
-
PENDING: 'PENDING',
|
|
51
|
-
PASSED: 'PASSED',
|
|
52
|
-
WARNED: 'WARNED',
|
|
53
|
-
FAILED: 'FAILED',
|
|
54
|
-
SKIPPED: 'SKIPPED',
|
|
55
|
-
ERRORED: 'ERRORED',
|
|
56
|
-
STOPPED: 'STOPPED',
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
36
|
socketEventTypes: {
|
|
60
37
|
TEST_RESULT_CREATED: 'test-result-created',
|
|
61
38
|
TEST_RESULT_UPDATED: 'test-result-updated',
|
|
@@ -64,8 +41,6 @@ module.exports = {
|
|
|
64
41
|
CLI_MODE: {
|
|
65
42
|
EXTENSION: 'extension',
|
|
66
43
|
SELENIUM: 'selenium',
|
|
67
|
-
APPIUM: 'appium',
|
|
68
|
-
DEVICE_FARM_APPIUM: 'device-farm-appium',
|
|
69
44
|
},
|
|
70
45
|
sessionType: {
|
|
71
46
|
CODELESS: 'codeless',
|
package/commons/featureFlags.js
CHANGED
|
@@ -52,6 +52,8 @@ class FeatureFlagsService {
|
|
|
52
52
|
autoSaveDownloadFileFireFox: new Rox.Flag(true),
|
|
53
53
|
safariSelectOptionDispatchEventOnSelectElement: new Rox.Flag(true),
|
|
54
54
|
experimentalPreCodeCompilation: new Rox.Flag(false),
|
|
55
|
+
/** Enables using top level await inside custom actions for non-IE browsers */
|
|
56
|
+
experimentalAsyncCustomCode: new Rox.Flag(),
|
|
55
57
|
useSameBrowserForMultiTests: new LabFeatureFlag('labs'),
|
|
56
58
|
highSpeedMode: new LabFeatureFlag(),
|
|
57
59
|
usePortedHtml5DragDrop: new Rox.Flag(),
|
package/commons/npmWrapper.js
CHANGED
|
@@ -9,7 +9,7 @@ const Promise = require('bluebird');
|
|
|
9
9
|
const fse = require('fs-extra');
|
|
10
10
|
const logger = require('./logger').getLogger('cli-service');
|
|
11
11
|
const { requireWithFallback } = require('./requireWithFallback');
|
|
12
|
-
const
|
|
12
|
+
const fs = require('fs');
|
|
13
13
|
|
|
14
14
|
async function getLatestPackageVersion(packageName) {
|
|
15
15
|
const result = await exec(`npm view ${packageName} version`);
|
|
@@ -40,6 +40,16 @@ function getLocallyInstalledPackageVersion(rootPath, packageName) {
|
|
|
40
40
|
return require(path.join(rootPath, `./node_modules/${packageName}/package.json`)).version;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// this is not exactly correct, but it's good enough.
|
|
44
|
+
async function fileExists(path) {
|
|
45
|
+
try {
|
|
46
|
+
await fs.promises.access(path);
|
|
47
|
+
return true;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
async function installPackageLocally(rootPath, packageName, execOptions) {
|
|
44
54
|
function getPathWithMissingPermissions(error) {
|
|
45
55
|
const pathRegex = /EACCES[^']+'(.+)'/;
|
|
@@ -50,26 +60,48 @@ async function installPackageLocally(rootPath, packageName, execOptions) {
|
|
|
50
60
|
return regexResult[1];
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
// this is here because our shrinkwrap blocks our lazy deps for some reason
|
|
64
|
+
const oldShrinkwrap = path.join(rootPath, 'npm-shrinkwrap.json');
|
|
65
|
+
const newShrinkwrap = path.join(rootPath, 'npm-shrinkwrap-dummy.json');
|
|
66
|
+
let renamed = false;
|
|
67
|
+
try {
|
|
68
|
+
try {
|
|
69
|
+
if (await fileExists(oldShrinkwrap)) {
|
|
70
|
+
await fs.promises.rename(oldShrinkwrap, newShrinkwrap);
|
|
71
|
+
renamed = true;
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// ignore error
|
|
75
|
+
}
|
|
76
|
+
return await exec(`npm i ${packageName} --no-save --no-package-lock --no-prune --prefer-offline --no-audit --progress=false`, { ...execOptions, cwd: rootPath }).catch(err => {
|
|
77
|
+
const pathWithMissingPermissions = getPathWithMissingPermissions(err);
|
|
78
|
+
if (pathWithMissingPermissions) {
|
|
79
|
+
logger.info('Failed to install package due to insufficient write access', {
|
|
80
|
+
...additionalLogDetails(),
|
|
81
|
+
package: packageName,
|
|
82
|
+
path: pathWithMissingPermissions,
|
|
83
|
+
});
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.error(`
|
|
63
86
|
|
|
64
87
|
Testim failed installing the package ${packageName} due to insufficient permissions.
|
|
65
88
|
This is probably due to an installation of @testim/testim-cli with sudo, and running it without sudo.
|
|
66
89
|
Testim had missing write access to ${pathWithMissingPermissions}
|
|
67
90
|
|
|
68
91
|
`);
|
|
69
|
-
|
|
92
|
+
throw new NpmPermissionsError(pathWithMissingPermissions);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
});
|
|
96
|
+
} finally {
|
|
97
|
+
if (renamed) {
|
|
98
|
+
try {
|
|
99
|
+
await fs.promises.rename(newShrinkwrap, oldShrinkwrap);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
// ignore error
|
|
102
|
+
}
|
|
70
103
|
}
|
|
71
|
-
|
|
72
|
-
});
|
|
104
|
+
}
|
|
73
105
|
}
|
|
74
106
|
|
|
75
107
|
const localNpmLocation = path.resolve(require.resolve('npm'), '../../bin/npm-cli.js');
|
|
@@ -5,8 +5,8 @@ const { sinon, expect } = require('../../test/utils/testUtils');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const { NpmPermissionsError } = require('../errors');
|
|
7
7
|
const path = require('path');
|
|
8
|
+
|
|
8
9
|
const fs = require('fs');
|
|
9
|
-
const { getCliLocation } = require('../utils');
|
|
10
10
|
|
|
11
11
|
describe('npmWrapper', () => {
|
|
12
12
|
describe('installPackageLocally', () => {
|
|
@@ -22,17 +22,22 @@ describe('npmWrapper', () => {
|
|
|
22
22
|
|
|
23
23
|
let fakeChildProcess;
|
|
24
24
|
let fakeLogger;
|
|
25
|
-
|
|
25
|
+
let fakeFS;
|
|
26
26
|
let originalConsole;
|
|
27
27
|
beforeEach(() => {
|
|
28
28
|
fakeChildProcess = { exec: sinon.stub() };
|
|
29
29
|
fakeLogger = { warn: sinon.stub(), info: sinon.stub() };
|
|
30
|
-
|
|
30
|
+
fakeFS = {
|
|
31
|
+
promises: {
|
|
32
|
+
access: sinon.stub().rejects(new Error()),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
31
35
|
originalConsole = global.console;
|
|
32
36
|
global.console = { log: sinon.stub(), error: sinon.stub() };
|
|
33
37
|
|
|
34
38
|
npmWrapper = proxyquire('./npmWrapper', {
|
|
35
39
|
child_process: fakeChildProcess,
|
|
40
|
+
fs: fakeFS,
|
|
36
41
|
'./logger': { getLogger: () => fakeLogger },
|
|
37
42
|
});
|
|
38
43
|
});
|
|
@@ -65,7 +70,7 @@ describe('npmWrapper', () => {
|
|
|
65
70
|
|
|
66
71
|
stubExecRejection(execErr);
|
|
67
72
|
|
|
68
|
-
expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
73
|
+
await expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
69
74
|
});
|
|
70
75
|
|
|
71
76
|
it('should throw an error if an error which isnt related to permissions occurred', async () => {
|
|
@@ -73,7 +78,7 @@ describe('npmWrapper', () => {
|
|
|
73
78
|
|
|
74
79
|
stubExecRejection(execErr);
|
|
75
80
|
|
|
76
|
-
expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
81
|
+
await expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
77
82
|
});
|
|
78
83
|
|
|
79
84
|
it('should throw an error if stderr includes "EACCES", but the path was not specified', async () => {
|
|
@@ -81,7 +86,7 @@ describe('npmWrapper', () => {
|
|
|
81
86
|
|
|
82
87
|
stubExecRejection(execErr);
|
|
83
88
|
|
|
84
|
-
expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
89
|
+
await expect(npmWrapper.installPackageLocally('/some/dir', 'some-package')).to.be.rejectedWith(execErr);
|
|
85
90
|
});
|
|
86
91
|
|
|
87
92
|
[
|
|
@@ -194,5 +199,176 @@ Testim had missing write access to ${expectedPath}
|
|
|
194
199
|
expect(fs.existsSync(unexpectedDir), 'expected the package not to be installed - this could be a problem with the test itself, and not the tested class').to.be.false;
|
|
195
200
|
}).timeout(20000);
|
|
196
201
|
});
|
|
202
|
+
|
|
203
|
+
describe('shirnkwrap handling', () => {
|
|
204
|
+
let fakeChildProcess;
|
|
205
|
+
let fakeLogger;
|
|
206
|
+
let originalConsole;
|
|
207
|
+
const shrinkwrapPath = '/some/dir/npm-shrinkwrap.json';
|
|
208
|
+
const shrinkwrapDummyPath = '/some/dir/npm-shrinkwrap-dummy.json';
|
|
209
|
+
beforeEach(() => {
|
|
210
|
+
fakeChildProcess = { exec: sinon.stub() };
|
|
211
|
+
fakeLogger = { warn: sinon.stub(), info: sinon.stub() };
|
|
212
|
+
originalConsole = global.console;
|
|
213
|
+
global.console = { log: sinon.stub(), error: sinon.stub() };
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
afterEach(() => {
|
|
217
|
+
global.console = originalConsole;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('does not call rename if access fails', async () => {
|
|
221
|
+
const fakeFS = {
|
|
222
|
+
promises: {
|
|
223
|
+
access: sinon.stub(),
|
|
224
|
+
rename: sinon.stub(),
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
fakeFS.promises.access.rejects();
|
|
228
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
229
|
+
child_process: fakeChildProcess,
|
|
230
|
+
fs: fakeFS,
|
|
231
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
fakeChildProcess.exec.yields(undefined, []); //resolve without errors
|
|
235
|
+
const cwd = '/some/dir';
|
|
236
|
+
const pkg = 'some-package';
|
|
237
|
+
await npmWrapper.installPackageLocally(cwd, pkg);
|
|
238
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
239
|
+
sinon.assert.notCalled(fakeFS.promises.rename);
|
|
240
|
+
expect(fakeFS.promises.access.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('calls rename once if rename fails on the first time', async () => {
|
|
244
|
+
const fakeFS = {
|
|
245
|
+
promises: {
|
|
246
|
+
access: sinon.stub().resolves(),
|
|
247
|
+
rename: sinon.stub().rejects(),
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
252
|
+
child_process: fakeChildProcess,
|
|
253
|
+
fs: fakeFS,
|
|
254
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
fakeChildProcess.exec.yields(undefined, []); //resolve without errors
|
|
258
|
+
const cwd = '/some/dir';
|
|
259
|
+
const pkg = 'some-package';
|
|
260
|
+
await npmWrapper.installPackageLocally(cwd, pkg);
|
|
261
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
262
|
+
sinon.assert.calledOnce(fakeFS.promises.rename);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('calls rename twice if first is success', async () => {
|
|
266
|
+
const fakeFS = {
|
|
267
|
+
promises: {
|
|
268
|
+
access: sinon.stub().resolves(),
|
|
269
|
+
rename: sinon.stub().resolves(),
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
273
|
+
child_process: fakeChildProcess,
|
|
274
|
+
fs: fakeFS,
|
|
275
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
fakeChildProcess.exec.yields(undefined, []); //resolve without errors
|
|
279
|
+
const cwd = '/some/dir';
|
|
280
|
+
const pkg = 'some-package';
|
|
281
|
+
|
|
282
|
+
await npmWrapper.installPackageLocally(cwd, pkg);
|
|
283
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
284
|
+
sinon.assert.calledTwice(fakeFS.promises.rename);
|
|
285
|
+
expect(fakeFS.promises.access.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
286
|
+
expect(fakeFS.promises.rename.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
287
|
+
expect(fakeFS.promises.rename.getCall(0).args[1]).to.be.equal(shrinkwrapDummyPath);
|
|
288
|
+
expect(fakeFS.promises.rename.getCall(1).args[0]).to.be.equal(shrinkwrapDummyPath);
|
|
289
|
+
expect(fakeFS.promises.rename.getCall(1).args[1]).to.be.equal(shrinkwrapPath);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('doesn\'t throw if first rename fails', async () => {
|
|
293
|
+
const fakeFS = {
|
|
294
|
+
promises: {
|
|
295
|
+
access: sinon.stub().resolves(true),
|
|
296
|
+
rename: sinon.stub().rejects(),
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
300
|
+
child_process: fakeChildProcess,
|
|
301
|
+
fs: fakeFS,
|
|
302
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
fakeChildProcess.exec.yields(undefined, []); //resolve without errors
|
|
306
|
+
const cwd = '/some/dir';
|
|
307
|
+
const pkg = 'some-package';
|
|
308
|
+
await npmWrapper.installPackageLocally(cwd, pkg);
|
|
309
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
310
|
+
sinon.assert.calledOnce(fakeFS.promises.rename);
|
|
311
|
+
expect(fakeFS.promises.access.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
312
|
+
expect(fakeFS.promises.rename.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
313
|
+
expect(fakeFS.promises.rename.getCall(0).args[1]).to.be.equal(shrinkwrapDummyPath);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('doesn\'t throw is second rename fails', async () => {
|
|
317
|
+
const fakeFS = {
|
|
318
|
+
promises: {
|
|
319
|
+
access: sinon.stub().resolves(),
|
|
320
|
+
rename: sinon.stub().onFirstCall().resolves().onSecondCall()
|
|
321
|
+
.rejects(),
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
326
|
+
child_process: fakeChildProcess,
|
|
327
|
+
fs: fakeFS,
|
|
328
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
fakeChildProcess.exec.yields(undefined, []); //resolve without errors
|
|
332
|
+
const cwd = '/some/dir';
|
|
333
|
+
const pkg = 'some-package';
|
|
334
|
+
await npmWrapper.installPackageLocally(cwd, pkg);
|
|
335
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
336
|
+
sinon.assert.calledTwice(fakeFS.promises.rename);
|
|
337
|
+
expect(fakeFS.promises.access.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
338
|
+
expect(fakeFS.promises.rename.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
339
|
+
expect(fakeFS.promises.rename.getCall(0).args[1]).to.be.equal(shrinkwrapDummyPath);
|
|
340
|
+
expect(fakeFS.promises.rename.getCall(1).args[0]).to.be.equal(shrinkwrapDummyPath);
|
|
341
|
+
expect(fakeFS.promises.rename.getCall(1).args[1]).to.be.equal(shrinkwrapPath);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('calls rename even if exec fails', async () => {
|
|
345
|
+
const fakeFS = {
|
|
346
|
+
promises: {
|
|
347
|
+
access: sinon.stub().resolves(),
|
|
348
|
+
rename: sinon.stub().onFirstCall().resolves().onSecondCall()
|
|
349
|
+
.rejects(),
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
fakeChildProcess.exec.throws();
|
|
354
|
+
|
|
355
|
+
const npmWrapper = proxyquire('./npmWrapper', {
|
|
356
|
+
child_process: fakeChildProcess,
|
|
357
|
+
fs: fakeFS,
|
|
358
|
+
'./logger': { getLogger: () => fakeLogger },
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const cwd = '/some/dir';
|
|
362
|
+
const pkg = 'some-package';
|
|
363
|
+
await expect(npmWrapper.installPackageLocally(cwd, pkg)).to.be.rejected;
|
|
364
|
+
sinon.assert.calledOnce(fakeFS.promises.access);
|
|
365
|
+
sinon.assert.calledTwice(fakeFS.promises.rename);
|
|
366
|
+
expect(fakeFS.promises.access.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
367
|
+
expect(fakeFS.promises.rename.getCall(0).args[0]).to.be.equal(shrinkwrapPath);
|
|
368
|
+
expect(fakeFS.promises.rename.getCall(0).args[1]).to.be.equal(shrinkwrapDummyPath);
|
|
369
|
+
expect(fakeFS.promises.rename.getCall(1).args[0]).to.be.equal(shrinkwrapDummyPath);
|
|
370
|
+
expect(fakeFS.promises.rename.getCall(1).args[1]).to.be.equal(shrinkwrapPath);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
197
373
|
});
|
|
198
374
|
});
|
|
@@ -2,11 +2,9 @@ const testResultServiceSocketIO = require('./testResultServiceSocketIO');
|
|
|
2
2
|
const socketService = require('./socketService');
|
|
3
3
|
|
|
4
4
|
const { EventEmitter } = require('events');
|
|
5
|
-
|
|
6
|
-
const testimServicesApi = require('../testimServicesApi');
|
|
7
5
|
const featureFlags = require('../featureFlags');
|
|
8
6
|
|
|
9
|
-
const {socketEventTypes} = require('../constants');
|
|
7
|
+
const { socketEventTypes } = require('../constants');
|
|
10
8
|
const Promise = require('bluebird');
|
|
11
9
|
|
|
12
10
|
class TestResultService extends EventEmitter {
|
|
@@ -22,9 +20,9 @@ class TestResultService extends EventEmitter {
|
|
|
22
20
|
joinToTestResult(resultId, testId) {
|
|
23
21
|
//TODO - Consider unifying the joinToTestResult and listenToTestResult flows
|
|
24
22
|
if (featureFlags.flags.useNewWSCLI.isEnabled()) {
|
|
25
|
-
return socketService.addFilter(`${resultId}:testResult`, {resultId, testId}, [
|
|
23
|
+
return socketService.addFilter(`${resultId}:testResult`, { resultId, testId }, [
|
|
26
24
|
socketEventTypes.TEST_RESULT_UPDATED,
|
|
27
|
-
socketEventTypes.TEST_RESULT_CREATED
|
|
25
|
+
socketEventTypes.TEST_RESULT_CREATED,
|
|
28
26
|
]);
|
|
29
27
|
}
|
|
30
28
|
testResultServiceSocketIO.joinRoom(resultId, testId);
|
|
@@ -54,17 +52,9 @@ class TestResultService extends EventEmitter {
|
|
|
54
52
|
testResultServiceSocketIO.listenToTestResult(resultId, testId, onTestResultStatus);
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
clearTestResult(projectId, resultId, testId, testResult) {
|
|
58
|
-
return testimServicesApi.clearTestResult(projectId, resultId, testId, testResult);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
updateTestResult(projectId, resultId, testId, testResult, remoteRunId) {
|
|
62
|
-
return testimServicesApi.updateTestResult(projectId, resultId, testId, testResult, remoteRunId);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
55
|
getSocket() {
|
|
66
56
|
if (featureFlags.flags.useNewWSCLI.isEnabled()) {
|
|
67
|
-
return;
|
|
57
|
+
return undefined;
|
|
68
58
|
}
|
|
69
59
|
return testResultServiceSocketIO.getSocket();
|
|
70
60
|
}
|
|
@@ -5,7 +5,6 @@ const config = require('./config');
|
|
|
5
5
|
const Analytics = require('analytics-node');
|
|
6
6
|
|
|
7
7
|
const analytics = new Analytics('sJOSIGKa5x5rJEGsaOlCjrgozAf7FnVY', { flushAt: 1 });
|
|
8
|
-
const testimCustomToken = require('./testimCustomToken');
|
|
9
8
|
|
|
10
9
|
function identify(data) {
|
|
11
10
|
if (config.IS_ON_PREM) {
|
|
@@ -9,7 +9,6 @@ const featureFlags = require('./featureFlags');
|
|
|
9
9
|
const { CLI_MODE, mobileWeb, gridTypes } = require('./constants');
|
|
10
10
|
const config = require('./config');
|
|
11
11
|
const utils = require('../utils');
|
|
12
|
-
const { getAppCaps } = require('./apkUploader/apkUploaderFactory');
|
|
13
12
|
const LambdatestService = require('../services/lambdatestService');
|
|
14
13
|
const logger = require('./logger').getLogger('testim-desired-capabilities-builder');
|
|
15
14
|
|
|
@@ -577,99 +576,6 @@ function buildSeleniumOptions(browserOptions, testName, testRunConfig, gridInfo,
|
|
|
577
576
|
return opts;
|
|
578
577
|
}
|
|
579
578
|
|
|
580
|
-
function buildTestObject(browserOptions, testName, appCaps) {
|
|
581
|
-
if (!_.isEmpty(browserOptions.testobjectSauce)) {
|
|
582
|
-
return Object.assign({
|
|
583
|
-
idleTimeout: 30,
|
|
584
|
-
testobject_test_name: testName,
|
|
585
|
-
testobject_session_creation_timeout: String(browserOptions.browserTimeout),
|
|
586
|
-
deviceName: null,
|
|
587
|
-
}, appCaps, browserOptions.testobjectSauce);
|
|
588
|
-
}
|
|
589
|
-
return {};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function buildSaucelabsMobile(browserOptions, testName, appCaps) {
|
|
593
|
-
if (!_.isEmpty(browserOptions.saucelabs)) {
|
|
594
|
-
return Object.assign({
|
|
595
|
-
deviceName: null,
|
|
596
|
-
name: testName,
|
|
597
|
-
idleTimeout: 30,
|
|
598
|
-
browserName: '',
|
|
599
|
-
}, appCaps, browserOptions.saucelabs);
|
|
600
|
-
}
|
|
601
|
-
return {};
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function buildAppiumOptions(browserOptions, gridInfo, nativeAppData, testName) {
|
|
605
|
-
const { getSessionTimeout, getSessionRetries, driverRequestTimeout, driverRequestRetries } = browserOptions;
|
|
606
|
-
const opts = {
|
|
607
|
-
host: gridInfo.host,
|
|
608
|
-
port: gridInfo.port || 4444,
|
|
609
|
-
path: gridInfo.path || '/wd/hub',
|
|
610
|
-
protocol: gridInfo.protocol || 'http',
|
|
611
|
-
logLevel: LOG_LEVEL,
|
|
612
|
-
connectionRetryTimeout: driverRequestTimeout,
|
|
613
|
-
connectionRetryCount: driverRequestRetries,
|
|
614
|
-
getSessionTimeout,
|
|
615
|
-
getSessionRetries,
|
|
616
|
-
desiredCapabilities: {
|
|
617
|
-
browserName: '',
|
|
618
|
-
noReset: false,
|
|
619
|
-
fullReset: false,
|
|
620
|
-
},
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
if (browserOptions.projectData.type === 'android') {
|
|
624
|
-
Object.assign(opts.desiredCapabilities, {
|
|
625
|
-
appPackage: nativeAppData.packageName,
|
|
626
|
-
appActivity: nativeAppData.activity,
|
|
627
|
-
platformName: 'Android',
|
|
628
|
-
automationName: 'uiautomator2',
|
|
629
|
-
autoLaunch: false,
|
|
630
|
-
ignoreUnimportantViews: false,
|
|
631
|
-
disableWindowAnimation: browserOptions.disableWindowAnimation,
|
|
632
|
-
});
|
|
633
|
-
} else if (browserOptions.projectData.type === 'ios') {
|
|
634
|
-
Object.assign(opts.desiredCapabilities, {
|
|
635
|
-
bundleId: nativeAppData.packageName,
|
|
636
|
-
platformName: 'iOS',
|
|
637
|
-
automationName: 'XCUITest',
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (browserOptions.deviceUdid) {
|
|
642
|
-
opts.desiredCapabilities.udid = browserOptions.deviceUdid;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
if (browserOptions.deviceName) {
|
|
646
|
-
opts.desiredCapabilities.deviceName = browserOptions.deviceName;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (gridInfo.user && gridInfo.key && gridInfo.type === 'saucelabs') {
|
|
650
|
-
browserOptions.saucelabs = browserOptions.saucelabs || {};
|
|
651
|
-
browserOptions.saucelabs.username = browserOptions.saucelabs.username || gridInfo.user;
|
|
652
|
-
browserOptions.saucelabs.accessKey = browserOptions.saucelabs.accessKey || gridInfo.key;
|
|
653
|
-
} else if (gridInfo.key && gridInfo.type === 'testobject') {
|
|
654
|
-
browserOptions.testobjectSauce = browserOptions.testobjectSauce || {};
|
|
655
|
-
browserOptions.testobjectSauce.testobjectApiKey = browserOptions.testobjectSauce.testobjectApiKey || gridInfo.key;
|
|
656
|
-
} else if (gridInfo.key && gridInfo.type === 'perfecto') {
|
|
657
|
-
browserOptions.perfecto = browserOptions.perfecto || {};
|
|
658
|
-
browserOptions.perfecto.securityToken = gridInfo.key;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const appCaps = getAppCaps(gridInfo);
|
|
662
|
-
|
|
663
|
-
Object.assign(
|
|
664
|
-
opts.desiredCapabilities,
|
|
665
|
-
buildTestObject(browserOptions, testName, appCaps),
|
|
666
|
-
buildSaucelabsMobile(browserOptions, testName, appCaps)
|
|
667
|
-
);
|
|
668
|
-
|
|
669
|
-
return opts;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
579
|
module.exports = {
|
|
673
580
|
buildSeleniumOptions,
|
|
674
|
-
buildAppiumOptions,
|
|
675
581
|
};
|