@testim/testim-cli 3.196.0 → 3.200.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 +960 -524
- package/package.json +3 -1
- package/player/stepActions/baseJsStepAction.js +5 -1
- package/player/stepActions/pixelValidationStepAction.js +28 -0
- package/player/stepActions/salesforceAutoLoginStepAction.js +5 -3
- package/player/stepActions/stepActionRegistrar.js +4 -48
- package/player/utils/eyeSdkService.js +230 -0
- package/reports/consoleReporter.js +29 -44
- 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 +288 -67
- package/runners/runnerUtils.js +73 -0
- package/services/analyticsService.js +94 -0
- package/services/gridService.js +24 -20
- package/services/gridService.test.js +21 -21
- package/stepPlayers/hybridStepPlayback.js +4 -1
- package/stepPlayers/tdkHybridStepPlayback.js +1 -0
- package/testRunHandler.js +23 -5
- package/testRunStatus.js +18 -27
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testim/testim-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.200.0",
|
|
4
4
|
"description": "Command line interface for running Testing on your CI",
|
|
5
5
|
"author": "Oren Rubin",
|
|
6
6
|
"contributors": [{
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"multer": "1.4.2"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@applitools/eyes-sdk-core": "12.23.12",
|
|
52
|
+
"@applitools/visual-grid-client": "15.8.31",
|
|
51
53
|
"@testim/coralogix-logger": "1.1.27-beta",
|
|
52
54
|
"@testim/webdriverio": "0.0.3",
|
|
53
55
|
"abort-controller": "3.0.0",
|
|
@@ -44,6 +44,7 @@ class BaseJsStepAction extends StepAction {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
executeGetStatus(transactionId) {
|
|
47
|
+
// eslint-disable-next-line prefer-arrow-callback
|
|
47
48
|
return this.driver.executeJS(function (transactionId) {
|
|
48
49
|
const sessionItem = 'data-testim-' + transactionId;
|
|
49
50
|
try {
|
|
@@ -139,12 +140,15 @@ class BaseJsStepAction extends StepAction {
|
|
|
139
140
|
|
|
140
141
|
executeInAut(eventMessage) {
|
|
141
142
|
const useExperimentalPreCompilation = featureFlags.flags.experimentalPreCodeCompilation.isEnabled();
|
|
143
|
+
const experimentalAsyncCustomCode = featureFlags.flags.experimentalAsyncCustomCode.isEnabled();
|
|
142
144
|
const rawParams = this.constructJSFunParams(eventMessage);
|
|
143
145
|
const hasLocateParams = rawParams.function.args.some(x => Boolean(x && x.locatedElement));
|
|
144
146
|
let funcToRunString = 'undefined';
|
|
145
147
|
if (useExperimentalPreCompilation) {
|
|
146
148
|
const paramNames = rawParams.function.params.slice(0, -1);
|
|
147
|
-
funcToRunString = `function(${paramNames.join(',')}) {
|
|
149
|
+
funcToRunString = (experimentalAsyncCustomCode && !this.driver.isIE()) ? `async function(${paramNames.join(',')}) {
|
|
150
|
+
${eventMessage.code}
|
|
151
|
+
};` : `function(${paramNames.join(',')}) {
|
|
148
152
|
${eventMessage.code}
|
|
149
153
|
};`;
|
|
150
154
|
// remove code from call.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const StepAction = require('./stepAction');
|
|
4
|
+
const { eyeSdkService } = require('../utils/eyeSdkService');
|
|
5
|
+
const logger = require('../../commons/logger').getLogger('pixel-validation-step-action');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PixelValidationStepAction extends StepAction {
|
|
9
|
+
async performAction() {
|
|
10
|
+
const { shouldUseVisualGrid, applitoolsSdkConfig: config } = this.context;
|
|
11
|
+
this.runContext = this.context.getRunContext(undefined);
|
|
12
|
+
const eyeManager = await eyeSdkService.getManager(shouldUseVisualGrid, this.context.config.applitoolsConcurrency || 5);
|
|
13
|
+
const targetElementData = this.getTarget() || {};
|
|
14
|
+
try {
|
|
15
|
+
const openedEye = await eyeManager.openEyes({ driver: this.driver.client, config });
|
|
16
|
+
const region = (this.step.action === 'element' && targetElementData.seleniumElement) || undefined;
|
|
17
|
+
await openedEye.check({ settings: { region, fully: this.step.action === 'stitched' } });
|
|
18
|
+
const eyesResults = await openedEye.close();
|
|
19
|
+
|
|
20
|
+
return { isApplitoolsSdkResult: true, success: true, eyesResults };
|
|
21
|
+
} catch (err) {
|
|
22
|
+
logger.error('Applitools SDK step failed', { err, info: err.info });
|
|
23
|
+
return { isApplitoolsSdkResult: true, success: false, err };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = PixelValidationStepAction;
|
|
@@ -7,20 +7,22 @@ class SalesforceAutoLoginStepAction extends NavigationStepAction {
|
|
|
7
7
|
try {
|
|
8
8
|
salesforceUrl = await this.updateBaseUrl(salesforceUrl);
|
|
9
9
|
await this.driver.url(salesforceUrl);
|
|
10
|
-
await Promise.delay(
|
|
10
|
+
await Promise.delay(5000); // wait a little for the page to load (fixes screenshots and clicking on elements in username verification screen)
|
|
11
11
|
let newUrl = await this.driver.getUrl();
|
|
12
12
|
// Verify username screen
|
|
13
13
|
const isUsernameVerificationNeeded = newUrl.includes(this.step.USERNAME_VERIFICATION_PATH_ID);
|
|
14
|
+
const timeout = this.context.data.timeToPlayStep + 3000;
|
|
14
15
|
if (isUsernameVerificationNeeded) {
|
|
15
16
|
await this.driver.executeCodeAsync(`
|
|
16
17
|
function ${this.step.handleUsernameVerificationAUTFunc.toString()}
|
|
17
18
|
handleUsernameVerificationAUTFunc();
|
|
18
19
|
var done = arguments[1];
|
|
19
20
|
done();
|
|
20
|
-
|
|
21
|
-
await Promise.delay(
|
|
21
|
+
`, timeout);
|
|
22
|
+
await Promise.delay(5000);
|
|
22
23
|
newUrl = await this.driver.getUrl(); // If we managed to continue correctly we want to get the new redirected url
|
|
23
24
|
}
|
|
25
|
+
await Promise.delay(1500);
|
|
24
26
|
return {
|
|
25
27
|
success: true,
|
|
26
28
|
newUrl,
|
|
@@ -21,6 +21,8 @@ const RefreshStepAction = require('./RefreshStepAction');
|
|
|
21
21
|
const ApiStepAction = require('./apiStepAction');
|
|
22
22
|
const ExtractTextStepAction = require('./extractTextStepAction');
|
|
23
23
|
const TdkHybridStepAction = require('./tdkHybridStepAction');
|
|
24
|
+
const PixelValidationStepAction = require('./pixelValidationStepAction');
|
|
25
|
+
|
|
24
26
|
const SalesforceAutoLoginStepAction = require('./salesforceAutoLoginStepAction');
|
|
25
27
|
|
|
26
28
|
const CliJsStepAction = require('./cliJsStepAction');
|
|
@@ -28,19 +30,6 @@ const CliConditionStepAction = require('./cliConditionStepAction');
|
|
|
28
30
|
const NodePackageStepAction = require('./nodePackageStepAction');
|
|
29
31
|
const ExtensionOnlyStepAction = require('./extensionOnlyStepAction');
|
|
30
32
|
|
|
31
|
-
const MobileTapStepAction = require('./mobile/androidTapStepAction');
|
|
32
|
-
const MobileGlobalActionStepAction = require('./mobile/androidGlobalActionStepAction');
|
|
33
|
-
const MobileTextChangeStepAction = require('./mobile/androidTextChangeStepAction');
|
|
34
|
-
|
|
35
|
-
const AndroidLocateStepAction = require('./mobile/android/androidLocateStepAction');
|
|
36
|
-
const AndroidLongClickStepAction = require('./mobile/android/androidLongClickStepAction');
|
|
37
|
-
const AndroidScrollStepAction = require('./mobile/android/androidScrollStepAction');
|
|
38
|
-
const AndroidSwipeStepAction = require('./mobile/android/androidSwipeStepAction');
|
|
39
|
-
const AndroidSpecialKeyStepAction = require('./mobile/android/androidSpecialKeyStepAction');
|
|
40
|
-
|
|
41
|
-
const IosLocateStepAction = require('./mobile/ios/iosLocateStepAction');
|
|
42
|
-
const IosScrollStepAction = require('./mobile/ios/iosScrollStepAction');
|
|
43
|
-
|
|
44
33
|
function register(stepActionByType, stepActionFactory) {
|
|
45
34
|
Object.keys(stepActionByType).forEach(type => {
|
|
46
35
|
stepActionFactory.registerStepAction(type, stepActionByType[type]);
|
|
@@ -77,6 +66,8 @@ module.exports = function (driver, stepActionFactory, runMode) {
|
|
|
77
66
|
'api-action': ApiStepAction,
|
|
78
67
|
'api-code-step': JsCodeStepAction,
|
|
79
68
|
'extract-text': ExtractTextStepAction,
|
|
69
|
+
'simple-ui-verification': PixelValidationStepAction,
|
|
70
|
+
'wait-for-simple-ui-verification': PixelValidationStepAction,
|
|
80
71
|
|
|
81
72
|
'cli-validation-download-file': ExtensionOnlyStepAction,
|
|
82
73
|
'cli-wait-for-download-file': ExtensionOnlyStepAction,
|
|
@@ -97,44 +88,9 @@ module.exports = function (driver, stepActionFactory, runMode) {
|
|
|
97
88
|
'salesforce-autologin': SalesforceAutoLoginStepAction,
|
|
98
89
|
};
|
|
99
90
|
|
|
100
|
-
const ANDROID_STEP_ACTION_MAPPING = {
|
|
101
|
-
locate: AndroidLocateStepAction,
|
|
102
|
-
'android-tap': MobileTapStepAction,
|
|
103
|
-
'android-long-click': AndroidLongClickStepAction,
|
|
104
|
-
'android-device-key': MobileGlobalActionStepAction,
|
|
105
|
-
'android-key-board-special-keys': AndroidSpecialKeyStepAction,
|
|
106
|
-
text: MobileTextChangeStepAction,
|
|
107
|
-
'android-swipe': AndroidSwipeStepAction,
|
|
108
|
-
'android-scroll': AndroidScrollStepAction,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const IOS_STEP_ACTION_MAPPING = {
|
|
112
|
-
locate: IosLocateStepAction,
|
|
113
|
-
'ios-tap': MobileTapStepAction,
|
|
114
|
-
'ios-device-key': MobileGlobalActionStepAction,
|
|
115
|
-
text: MobileTextChangeStepAction,
|
|
116
|
-
'ios-scroll': IosScrollStepAction,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
91
|
register(STEP_ACTION_MAPPING, stepActionFactory);
|
|
120
92
|
if (stepActionFactory.registerLocateStepActionUtils) {
|
|
121
93
|
stepActionFactory.registerLocateStepActionUtils(LocateStepAction.getUtils(driver));
|
|
122
94
|
}
|
|
123
|
-
|
|
124
|
-
// override android actions
|
|
125
|
-
if (runMode === 'android') {
|
|
126
|
-
register(ANDROID_STEP_ACTION_MAPPING, stepActionFactory);
|
|
127
|
-
if (stepActionFactory.registerLocateStepActionUtils) {
|
|
128
|
-
stepActionFactory.registerLocateStepActionUtils(AndroidLocateStepAction.getUtils(driver));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// override ios actions
|
|
133
|
-
if (runMode === 'ios') {
|
|
134
|
-
register(IOS_STEP_ACTION_MAPPING, stepActionFactory);
|
|
135
|
-
if (stepActionFactory.registerLocateStepActionUtils) {
|
|
136
|
-
stepActionFactory.registerLocateStepActionUtils(IosLocateStepAction.getUtils(driver));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
95
|
};
|
|
140
96
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// https://github.com/applitools/eyes.sdk.javascript1/blob/master/packages/eyes-webdriverio-4/src/spec-driver.ts
|
|
2
|
+
|
|
3
|
+
const { makeSDK } = require('@applitools/eyes-sdk-core');
|
|
4
|
+
const { W3C_ELEMENT_ID } = require('../constants');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
|
|
7
|
+
const LEGACY_ELEMENT_ID = 'ELEMENT';
|
|
8
|
+
|
|
9
|
+
function extractElementId(element) {
|
|
10
|
+
if (_.has(element, 'elementId')) {
|
|
11
|
+
return element.elementId;
|
|
12
|
+
}
|
|
13
|
+
if (_.has(element, W3C_ELEMENT_ID)) {
|
|
14
|
+
return element[W3C_ELEMENT_ID];
|
|
15
|
+
}
|
|
16
|
+
if (_.has(element, LEGACY_ELEMENT_ID)) {
|
|
17
|
+
return element[LEGACY_ELEMENT_ID];
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
/** implements the ?? capability for backward compatibility */
|
|
22
|
+
function getValueOrFallbackIfNullOrUndefined(value, fallback) {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {import('@applitools/types').SpecDriver} SpecDriver
|
|
31
|
+
* @implements {SpecDriver}
|
|
32
|
+
*/
|
|
33
|
+
class EyesSpec {
|
|
34
|
+
// #region UTILITY
|
|
35
|
+
isDriver(driver) {
|
|
36
|
+
return Boolean(driver && driver.getPrototype && driver.desiredCapabilities && driver.requestHandler);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isElement(element) {
|
|
40
|
+
if (!element) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const elementToCheck = element.value || element;
|
|
44
|
+
return Boolean(elementToCheck[W3C_ELEMENT_ID] || elementToCheck[LEGACY_ELEMENT_ID]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isSelector(selector) {
|
|
48
|
+
return _.isString(selector);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
transformDriver(driver) {
|
|
52
|
+
return new Proxy(driver, {
|
|
53
|
+
get: (target, key) => {
|
|
54
|
+
if (key === 'then') {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return Reflect.get(target, key);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
transformElement(element) {
|
|
63
|
+
const elementId = extractElementId(element.value || element);
|
|
64
|
+
return { [W3C_ELEMENT_ID]: elementId, [LEGACY_ELEMENT_ID]: elementId };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
transformSelector(selector) {
|
|
68
|
+
if (!_.has(selector, 'selector')) {
|
|
69
|
+
return selector;
|
|
70
|
+
}
|
|
71
|
+
if (!_.has(selector, 'type')) {
|
|
72
|
+
return selector.selector;
|
|
73
|
+
}
|
|
74
|
+
if (selector.type === 'css') {
|
|
75
|
+
return `css selector:${selector.selector}`;
|
|
76
|
+
}
|
|
77
|
+
return `${selector.type}:${selector.selector}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
extractSelector(element) {
|
|
81
|
+
return _.has(element, 'selector') ? element.selector : undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isStaleElementError(error, selector) {
|
|
85
|
+
if (!error) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const errOrResult = error.originalError || error;
|
|
89
|
+
return errOrResult instanceof Error ?
|
|
90
|
+
errOrResult.seleniumStack && errOrResult.seleniumStack.type === 'StaleElementReference' :
|
|
91
|
+
errOrResult.value && errOrResult.selector && errOrResult.selector === selector;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
isEqualElements(_browser, element1, element2) {
|
|
95
|
+
if (!element1 || !element2) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const elementId1 = extractElementId(element1);
|
|
99
|
+
const elementId2 = extractElementId(element2);
|
|
100
|
+
return elementId1 === elementId2;
|
|
101
|
+
}
|
|
102
|
+
// #endregion
|
|
103
|
+
|
|
104
|
+
// #region COMMANDS
|
|
105
|
+
async executeScript(driver, script, arg) {
|
|
106
|
+
const { value } = await driver.execute(script, arg);
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async mainContext(driver) {
|
|
111
|
+
await driver.frame(null);
|
|
112
|
+
return driver;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async parentContext(driver) {
|
|
116
|
+
await driver.frameParent();
|
|
117
|
+
return driver;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async childContext(driver, element) {
|
|
121
|
+
await driver.frame(element);
|
|
122
|
+
return driver;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async findElement(driver, selector, parent) {
|
|
126
|
+
const { value } = parent ? await driver.elementIdElement(extractElementId(parent), selector) : await driver.element(selector);
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async findElements(driver, selector, parent) {
|
|
131
|
+
const { value } = parent ? await driver.elementIdElements(extractElementId(parent), selector) : await driver.elements(selector);
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async getWindowSize(driver) {
|
|
136
|
+
const { value: size } = await driver.windowHandleSize();
|
|
137
|
+
return { width: size.width, height: size.height };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async setWindowSize(driver, size) {
|
|
141
|
+
await driver.windowHandlePosition({ x: 0, y: 0 });
|
|
142
|
+
await driver.windowHandleSize(size);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async getDriverInfo(driver) {
|
|
146
|
+
const desiredCapabilities = driver.desiredCapabilities;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
sessionId: driver.requestHandler.sessionID || driver.sessionId,
|
|
150
|
+
isMobile: driver.isMobile,
|
|
151
|
+
isNative: driver.isMobile && !desiredCapabilities.browserName,
|
|
152
|
+
deviceName: desiredCapabilities.deviceName,
|
|
153
|
+
platformName: desiredCapabilities.platformName || desiredCapabilities.platform,
|
|
154
|
+
platformVersion: desiredCapabilities.platformVersion,
|
|
155
|
+
browserName: getValueOrFallbackIfNullOrUndefined(desiredCapabilities.browserName, desiredCapabilities.name),
|
|
156
|
+
browserVersion: getValueOrFallbackIfNullOrUndefined(desiredCapabilities.browserVersion, desiredCapabilities.version),
|
|
157
|
+
pixelRatio: desiredCapabilities.pixelRatio,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async getTitle(driver) {
|
|
162
|
+
return driver.getTitle();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getUrl(driver) {
|
|
166
|
+
return driver.getUrl();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async visit(driver, url) {
|
|
170
|
+
await driver.url(url);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async takeScreenshot(driver) {
|
|
174
|
+
return driver.saveScreenshot();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async click(driver, element) {
|
|
178
|
+
if (this.isSelector(element)) {
|
|
179
|
+
element = await this.findElement(driver, element);
|
|
180
|
+
}
|
|
181
|
+
await driver.elementIdClick(extractElementId(element));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async hover(driver, element, offset) {
|
|
185
|
+
if (this.isSelector(element)) {
|
|
186
|
+
element = await this.findElement(driver, element);
|
|
187
|
+
}
|
|
188
|
+
await driver.moveTo(extractElementId(element), offset && offset.x, offset && offset.y);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async type(driver, element, keys) {
|
|
192
|
+
if (this.isSelector(element)) {
|
|
193
|
+
element = await this.findElement(driver, element);
|
|
194
|
+
} else {
|
|
195
|
+
driver.elementIdValue(extractElementId(element), keys);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async scrollIntoView(driver, element, align = false) {
|
|
200
|
+
if (this.isSelector(element)) {
|
|
201
|
+
element = await this.findElement(driver, element);
|
|
202
|
+
}
|
|
203
|
+
await driver.execute('arguments[0].scrollIntoView(arguments[1])', element, align);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async waitUntilDisplayed(driver, selector, timeout) {
|
|
207
|
+
await driver.waitForVisible(selector, timeout);
|
|
208
|
+
}
|
|
209
|
+
// #endregion
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
class EyeSdkService {
|
|
213
|
+
constructor() {
|
|
214
|
+
/**
|
|
215
|
+
* @typedef {import('@applitools/types').Core} Core
|
|
216
|
+
* @type {Core}
|
|
217
|
+
*/
|
|
218
|
+
this.sdk = makeSDK({
|
|
219
|
+
name: 'Testim.io',
|
|
220
|
+
version: '4.0.0',
|
|
221
|
+
spec: new EyesSpec(),
|
|
222
|
+
VisualGridClient: require('@applitools/visual-grid-client'),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
getManager(useVisualGrid, concurrency) {
|
|
226
|
+
return this.sdk.makeManager({ type: useVisualGrid ? 'vg' : 'classic', concurrency });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
exports.eyeSdkService = new EyeSdkService();
|
|
@@ -7,6 +7,8 @@ const constants = require('../commons/constants');
|
|
|
7
7
|
const featureAvailabilityService = require('../commons/featureAvailabilityService');
|
|
8
8
|
const { getAbortedTests, getFailedTests, getPassedTests, getFailureEvaluatingCount, getSkippedCount } = require('./reporterUtils');
|
|
9
9
|
|
|
10
|
+
const colorize = { success: chalk.green, warn: chalk.yellow, error: chalk.red };
|
|
11
|
+
|
|
10
12
|
class ConsoleReporter {
|
|
11
13
|
constructor(options, branchToUse) {
|
|
12
14
|
this.options = options;
|
|
@@ -16,12 +18,8 @@ class ConsoleReporter {
|
|
|
16
18
|
this.branchToUse = branchToUse;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.log(`W:${workerId} ${message}`);
|
|
22
|
-
} else {
|
|
23
|
-
console.log(message);
|
|
24
|
-
}
|
|
21
|
+
toWorkerIdPrefix(workerId) {
|
|
22
|
+
return this.config.showWorkerNames ? `W:${workerId}` : '';
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
printWorkerDivider() {
|
|
@@ -32,7 +30,7 @@ class ConsoleReporter {
|
|
|
32
30
|
const type = isCodeMode ? 'File' : 'Test';
|
|
33
31
|
const testIdLabel = test.isTestsContainer ? '' : `(${test.testId})`;
|
|
34
32
|
const testUrlLabel = test.isTestsContainer ? '' : `url: ${chalk.underline(utils.getTestUrl(this.options.editorUrl, this.options.project, test.testId, test.resultId, this.branchToUse))}`;
|
|
35
|
-
this.
|
|
33
|
+
console.log(this.toWorkerIdPrefix(workerId), `${type} "${test.name}" started ${testIdLabel} ${testUrlLabel}`.trim());
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
onTestFinished(test, workerId, isRerun, isCodeMode) {
|
|
@@ -42,7 +40,9 @@ class ConsoleReporter {
|
|
|
42
40
|
}
|
|
43
41
|
const testStatus = test.success ? constants.runnerTestStatus.PASSED : constants.runnerTestStatus.FAILED;
|
|
44
42
|
const testIdLabel = test.isTestsContainer ? ' ' : `(${test.testId})`;
|
|
45
|
-
|
|
43
|
+
const color = colorize[test.success ? 'success' : 'error'];
|
|
44
|
+
|
|
45
|
+
console.log(color(this.toWorkerIdPrefix(workerId), `Test "${test.name}" finished status: ${testStatus} ${testIdLabel} duration: ${utils.getDuration(test.duration)}`));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
printAllFailedTests(failedTests) {
|
|
@@ -54,8 +54,8 @@ class ConsoleReporter {
|
|
|
54
54
|
}
|
|
55
55
|
return `${failedTest.name} : ${testUrl}`;
|
|
56
56
|
});
|
|
57
|
-
console.log('Failed runs are:');
|
|
58
|
-
console.log(failedTestStrings.join('\n\r'));
|
|
57
|
+
console.log(colorize.error('Failed runs are:'));
|
|
58
|
+
console.log(colorize.error(failedTestStrings.join('\n\r')));
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -79,15 +79,20 @@ class ConsoleReporter {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const planName = this.buildTestPlanName(isAnonymous, testPlanName, isCodeMode);
|
|
82
|
+
|
|
83
|
+
let message;
|
|
84
|
+
const color = colorize[failed ? 'error' : 'success'];
|
|
85
|
+
|
|
82
86
|
if (isCodeMode || planName.trim() === '' || planName.trim() === 'Anonymous') {
|
|
83
|
-
|
|
84
|
-
console.log(`Tests completed. PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (Execution ID: ${executionId})`);
|
|
85
|
-
this.printWorkerDivider();
|
|
87
|
+
message = `Tests completed. PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (Execution ID: ${executionId})`;
|
|
86
88
|
} else {
|
|
87
|
-
|
|
88
|
-
console.log(`Test plan${planName} completed PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (${executionId})`);
|
|
89
|
-
this.printWorkerDivider();
|
|
89
|
+
message = `Test plan${planName} completed PASSED: ${passed} FAILED: ${failed}${failedEvaluatingString} ABORTED: ${aborted}${skippedString} Duration: ${utils.getDuration(duration)} (${executionId})`;
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
this.printWorkerDivider();
|
|
93
|
+
console.log(color(message));
|
|
94
|
+
this.printWorkerDivider();
|
|
95
|
+
|
|
91
96
|
this.printAllFailedTests(failedTests);
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -138,47 +143,27 @@ class ConsoleReporter {
|
|
|
138
143
|
onGetSlot(workerId, browser) {
|
|
139
144
|
const gridNameOrId = this.options.grid || this.options.gridId;
|
|
140
145
|
if (gridNameOrId) {
|
|
141
|
-
this.
|
|
146
|
+
console.log(this.toWorkerIdPrefix(workerId), `Get ${chalk.underline(browser)} slot from ${chalk.underline(gridNameOrId)}`);
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
onGetSession(workerId, testName, mode) {
|
|
146
|
-
|
|
147
|
-
this.printWorkerMessage(workerId, `Get device to run ${chalk.underline(testName)}`);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
this.printWorkerMessage(workerId, `Get browser to run ${chalk.underline(testName)}`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
onUploadApk(apkLocation, vendorAppId) {
|
|
154
|
-
if (vendorAppId) {
|
|
155
|
-
console.log(`Use existing app id: ${vendorAppId}`);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
console.log(`Upload apk file: ${apkLocation}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
onUploadApkFinished(appId) {
|
|
162
|
-
console.log(`Finished to upload apk file id: ${appId}`);
|
|
151
|
+
console.log(this.toWorkerIdPrefix(workerId), `Get browser to run ${chalk.underline(testName)}`);
|
|
163
152
|
}
|
|
164
153
|
|
|
165
154
|
onWaitToTestStart(workerId) {
|
|
166
|
-
this.
|
|
155
|
+
console.log(this.toWorkerIdPrefix(workerId), 'Wait for test start');
|
|
167
156
|
}
|
|
168
157
|
|
|
169
158
|
onWaitToTestComplete(workerId, isCodeMode, debuggerAddress) {
|
|
170
159
|
const type = isCodeMode ? 'file' : 'test';
|
|
171
|
-
this.
|
|
160
|
+
console.log(this.toWorkerIdPrefix(workerId), `Wait for ${type} complete`);
|
|
172
161
|
if (debuggerAddress && isCodeMode) {
|
|
173
162
|
// TODO(Benji) decide with Amitai what we want to do with this
|
|
174
|
-
this.
|
|
163
|
+
console.log(this.toWorkerIdPrefix(workerId), `Chrome Debugger available at ${debuggerAddress}`);
|
|
175
164
|
}
|
|
176
165
|
}
|
|
177
166
|
|
|
178
|
-
onWaitForDevice(workerId, executionId) {
|
|
179
|
-
this.printWorkerMessage(workerId, `Device is being prepared for execution ${executionId} (this may take a few minutes)`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
167
|
onGetBrowserFailure(workerId, projectId, attempt) {
|
|
183
168
|
if (attempt !== 2) {
|
|
184
169
|
return; // we want to try 2 times before showing the message once
|
|
@@ -186,11 +171,11 @@ class ConsoleReporter {
|
|
|
186
171
|
// heuristic, show the message on the same attempt
|
|
187
172
|
const gridNameOrId = this.options.grid || this.options.gridId;
|
|
188
173
|
if (gridNameOrId) { // if the user passes a grid or a gridId - show those
|
|
189
|
-
this.
|
|
174
|
+
console.log(colorize.warn(this.toWorkerIdPrefix(workerId), `It is taking us some time to get a browser from the grid ${gridNameOrId}`));
|
|
190
175
|
} else if (this.options.usingLocalChromeDriver) {
|
|
191
|
-
this.
|
|
176
|
+
console.log(colorize.warn(this.toWorkerIdPrefix(workerId), 'We are having issues starting ChromeDriver for you locally'));
|
|
192
177
|
} else if (this.options.host) {
|
|
193
|
-
this.
|
|
178
|
+
console.log(colorize.warn(this.toWorkerIdPrefix(workerId), `We are having issues reaching your Selenium grid at ${this.options.host}:${this.options.port || 4444}`));
|
|
194
179
|
} else {
|
|
195
180
|
// in other cases - print nothing
|
|
196
181
|
}
|
package/reports/reporter.js
CHANGED
|
@@ -58,14 +58,11 @@ addHook('onGetBrowserSuccess');
|
|
|
58
58
|
addHook('onTestPlanStarted');
|
|
59
59
|
addHook('onGetSlot');
|
|
60
60
|
addHook('onGetSession');
|
|
61
|
-
addHook('onUploadApkFinished');
|
|
62
|
-
addHook('onUploadApk');
|
|
63
61
|
addHook('onTestFinished');
|
|
64
62
|
addHook('onTestFailed');
|
|
65
63
|
addHook('onTestPassed');
|
|
66
64
|
addHook('onTestStarted');
|
|
67
65
|
addHook('onTestIgnored');
|
|
68
|
-
addHook('onWaitForDevice');
|
|
69
66
|
addHook('onWaitToTestStart');
|
|
70
67
|
addHook('onWaitToTestComplete');
|
|
71
68
|
|
|
@@ -128,24 +125,6 @@ Reporter.prototype.onGetSession = function (workerId, testName, mode) {
|
|
|
128
125
|
});
|
|
129
126
|
};
|
|
130
127
|
|
|
131
|
-
Reporter.prototype.onUploadApkFinished = function (appId) {
|
|
132
|
-
return Promise.each(this.reporters, reporter => {
|
|
133
|
-
if (reporter && reporter.onUploadApkFinished) {
|
|
134
|
-
return reporter.onUploadApkFinished(appId);
|
|
135
|
-
}
|
|
136
|
-
return undefined;
|
|
137
|
-
});
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
Reporter.prototype.onUploadApk = function (apkLocation, vendorAppId) {
|
|
141
|
-
return Promise.each(this.reporters, reporter => {
|
|
142
|
-
if (reporter && reporter.onUploadApk) {
|
|
143
|
-
return reporter.onUploadApk(apkLocation, vendorAppId);
|
|
144
|
-
}
|
|
145
|
-
return undefined;
|
|
146
|
-
});
|
|
147
|
-
};
|
|
148
|
-
|
|
149
128
|
Reporter.prototype.onWaitToTestComplete = function (workerId, isCodeMode, debuggerAddress) {
|
|
150
129
|
return Promise.each(this.reporters, reporter => {
|
|
151
130
|
if (reporter && reporter.onWaitToTestComplete) {
|