@testim/testim-cli 3.197.0 → 3.201.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/OverrideTestDataBuilder.js +1 -1
- package/commons/featureFlags.js +0 -3
- package/commons/npmWrapper.js +46 -14
- package/commons/npmWrapper.test.js +182 -6
- package/executionQueue.js +2 -2
- package/npm-shrinkwrap.json +476 -148
- package/package.json +6 -6
- package/player/stepActions/pixelValidationStepAction.js +2 -2
- package/player/stepActions/salesforceAutoLoginStepAction.js +3 -3
- package/player/stepActions/stepActionRegistrar.js +2 -1
- package/player/utils/eyeSdkService.js +8 -2
- package/reports/consoleReporter.js +29 -24
- package/reports/junitReporter.js +0 -3
- package/runner.js +1 -10
- package/runners/ParallelWorkerManager.js +2 -2
- package/runners/TestPlanRunner.js +18 -16
- package/services/gridService.js +0 -4
- package/stepPlayers/hybridStepPlayback.js +4 -1
- package/stepPlayers/tdkHybridStepPlayback.js +1 -0
- package/testRunHandler.js +30 -7
- package/testRunStatus.js +6 -19
- package/workers/BaseWorker.js +1 -0
- package/workers/WorkerSelenium.js +3 -0
|
@@ -32,7 +32,7 @@ class OverrideTestDataBuilder {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
if (this.isObjectNotArray(params) && typeof params.overrideAllTestsData !== 'undefined') {
|
|
35
|
-
if (
|
|
35
|
+
if (_.isObject(params.overrideAllTestsData) && !_.isEmpty(params.overrideAllTestsData)) {
|
|
36
36
|
const testNames = this.testInfoList.map(test => test.name);
|
|
37
37
|
testNames.forEach(testName => this.overrideSingeTest(testName, params.overrideAllTestsData));
|
|
38
38
|
delete params.overrideAllTestsData;
|
package/commons/featureFlags.js
CHANGED
|
@@ -37,7 +37,6 @@ class FeatureFlagsService {
|
|
|
37
37
|
useSafariWebdriverVisibilityChecks: new Rox.Flag(),
|
|
38
38
|
useClickimVisibilityChecks: new Rox.Flag(),
|
|
39
39
|
useIEWebdriverVisibilityChecks: new Rox.Flag(),
|
|
40
|
-
enableTDKRun: new Rox.Flag(true),
|
|
41
40
|
runGetElementCodeInAut: new Rox.Flag(),
|
|
42
41
|
enableNpmPackageInstallUsingNpmCli: new Rox.Flag(),
|
|
43
42
|
enableFrameSwitchOptimization: new Rox.Flag(),
|
|
@@ -47,8 +46,6 @@ class FeatureFlagsService {
|
|
|
47
46
|
warnOnBadNetwork: new Rox.Flag(false),
|
|
48
47
|
overrideAzureStorageUrl: new Rox.Flag(),
|
|
49
48
|
useJsInputCodeInSafari: new Rox.Flag(),
|
|
50
|
-
testNameTestDataInJunitReport: new Rox.Flag(),
|
|
51
|
-
countRetries: new Rox.Flag(),
|
|
52
49
|
autoSaveDownloadFileFireFox: new Rox.Flag(true),
|
|
53
50
|
safariSelectOptionDispatchEventOnSelectElement: new Rox.Flag(true),
|
|
54
51
|
experimentalPreCodeCompilation: new Rox.Flag(false),
|
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
|
});
|
package/executionQueue.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const TestRun = require('./testRunHandler.js');
|
|
4
4
|
|
|
5
5
|
class ExecutionQueue {
|
|
6
|
-
constructor(executionId, testList, options, branchToUse, testStatus) {
|
|
7
|
-
this._waitingTests = testList.map(testInfo => new TestRun(executionId, testInfo, options, branchToUse, testStatus));
|
|
6
|
+
constructor(executionId, executionName, testList, options, branchToUse, testStatus) {
|
|
7
|
+
this._waitingTests = testList.map(testInfo => new TestRun(executionId, executionName, testInfo, options, branchToUse, testStatus));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
getNext() {
|