@testim/testim-cli 3.195.0 → 3.199.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 +962 -526
- 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/stepActionRegistrar.js +6 -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 +292 -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 +23 -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
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const Promise = require('bluebird');
|
|
5
|
-
const WindowUtils = require('../utils/mobileWindowUtils');
|
|
6
|
-
const MobileScreenshotUtils = require('../utils/mobileScreenshotUtils');
|
|
7
|
-
const ImageCaptureUtils = require('../../utils/imageCaptureUtils');
|
|
8
|
-
const guid = require('../../../utils').guid;
|
|
9
|
-
|
|
10
|
-
module.exports = function (driver) {
|
|
11
|
-
class MobileTabService {
|
|
12
|
-
constructor() {
|
|
13
|
-
this._utils = {};
|
|
14
|
-
this.sessionTabs = {};
|
|
15
|
-
this.pendingTabs = {};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
on(){}
|
|
19
|
-
|
|
20
|
-
createSesion(sessionId) {
|
|
21
|
-
this.pendingTabs[sessionId] = new Set();
|
|
22
|
-
this.sessionTabs[sessionId] = {
|
|
23
|
-
tabCount: 0,
|
|
24
|
-
tabInfos: {}
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getAllOpenTabIds(sessionId) {
|
|
29
|
-
const allTabInfos = this.getAllTabInfos(sessionId);
|
|
30
|
-
return Object.keys(allTabInfos)
|
|
31
|
-
.filter(tabId => !allTabInfos[tabId].isClosed)
|
|
32
|
-
.map(tabId => _.isNaN(Number(tabId)) ? tabId : Number(tabId));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get last tab info for pixel validation
|
|
37
|
-
*
|
|
38
|
-
* @returns last tab info
|
|
39
|
-
*/
|
|
40
|
-
getActiveTabInfo(sessionId){
|
|
41
|
-
return this.sessionTabs[sessionId].lastActiveTabInfo;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getAllTabIds(sessionId) {
|
|
45
|
-
return Object.keys(this.getAllTabInfos(sessionId)).map(id => id);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
isSessionTab(sessionId, tabId) {
|
|
49
|
-
return this.getAllTabIds(sessionId).includes(tabId);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
createSession(sessionId) {
|
|
53
|
-
this.pendingTabs[sessionId] = new Set();
|
|
54
|
-
this.sessionTabs[sessionId] = {
|
|
55
|
-
tabCount: 0,
|
|
56
|
-
tabInfos: {}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getAllTabInfoStrings(sessionId) {
|
|
61
|
-
var allIds = this.getAllTabIds(sessionId);
|
|
62
|
-
return allIds.map(tabId => {
|
|
63
|
-
var tabInfo = this.getTabInfo(sessionId, tabId);
|
|
64
|
-
return `tabId=${tabId}, url=${tabInfo.url}, order=${tabInfo.order}, isMain=${tabInfo.isMain}, openerStepId=${tabInfo.openerStepId}`;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
getAllTabInfos(sessionId) {
|
|
69
|
-
return this.sessionTabs[sessionId].tabInfos;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
addNewTab(sessionId, tabId, openerStepId) {
|
|
73
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
74
|
-
if(infos && infos[tabId]){
|
|
75
|
-
return Promise.resolve();
|
|
76
|
-
}
|
|
77
|
-
return this.addTab(sessionId, tabId, this.sessionTabs[sessionId].tabCount++, openerStepId);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
addOpenerStepId(sessionId, tabId, openerStepId) {
|
|
81
|
-
this.sessionTabs[sessionId].tabInfos[tabId].openerStepId = openerStepId;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
buildTabInfo(sessionId, tabId, order, openerStepId) {
|
|
85
|
-
return this.getTabDetails(tabId, sessionId)
|
|
86
|
-
.then(tab => {
|
|
87
|
-
var infoId = guid();
|
|
88
|
-
var missingMainTab = !this.getMainTabInfo(sessionId);
|
|
89
|
-
|
|
90
|
-
this.sessionTabs[sessionId].tabInfos[tabId] = {
|
|
91
|
-
infoId : infoId,
|
|
92
|
-
url: tab.url,
|
|
93
|
-
title: tab.title,
|
|
94
|
-
favIconUrl : tab.favIconUrl,
|
|
95
|
-
order: order,
|
|
96
|
-
from: this.getTabInfo(sessionId, tab.openerTabId),
|
|
97
|
-
isMain : missingMainTab,
|
|
98
|
-
openerStepId: openerStepId
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
return Promise.resolve(infoId);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
addTab(sessionId, id, order, openerStepId){
|
|
106
|
-
return new Promise(resolve => {
|
|
107
|
-
return this.buildTabInfo(sessionId, id, order, openerStepId)
|
|
108
|
-
.then(infoId => {
|
|
109
|
-
var _windowUtils = new WindowUtils(id, driver);
|
|
110
|
-
this._utils[infoId] = {
|
|
111
|
-
attachDebugger: () => Promise.resolve(),
|
|
112
|
-
detachDebugger: () => Promise.resolve(),
|
|
113
|
-
onDebuggerDetached: () => {},
|
|
114
|
-
tabId: id,
|
|
115
|
-
domUtils: {getDOM: () => Promise.resolve()},
|
|
116
|
-
windowUtils : _windowUtils,
|
|
117
|
-
imageCaptureUtils : new ImageCaptureUtils(id, _windowUtils, new MobileScreenshotUtils(id, driver))
|
|
118
|
-
};
|
|
119
|
-
resolve();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
getTabUtilsByTabId(tabId) {
|
|
125
|
-
const infoId = Object.keys(this._utils).find(uId => this._utils[uId].tabId === tabId);
|
|
126
|
-
return this._utils[infoId];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
getTabUtilsByTabIdAndSessionId(sessionId){
|
|
130
|
-
return this.getMainTabUtils(sessionId);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
getTabInfo(sessionId, id){
|
|
134
|
-
return this.sessionTabs[sessionId].tabInfos[id];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
getTabUtils(sessionId, tabInfo){
|
|
138
|
-
if(!tabInfo){
|
|
139
|
-
return this.getMainTabUtils(sessionId);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if(this._utils[tabInfo.infoId]){
|
|
143
|
-
return this._utils[tabInfo.infoId];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if(tabInfo.isMain){
|
|
147
|
-
return this.getMainTabUtils(sessionId);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
151
|
-
var nonMainTabs = Object.keys(infos)
|
|
152
|
-
.map(tabId => infos[tabId])
|
|
153
|
-
.filter(info => !info.isMain);
|
|
154
|
-
if(nonMainTabs.length === 1){
|
|
155
|
-
return this._utils[nonMainTabs[0].infoId];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
var sameTabs = Object.keys(sessionId)
|
|
159
|
-
.map(key => sessionId[key])
|
|
160
|
-
.filter(info => this.isSameTab(sessionId, tabInfo, info));
|
|
161
|
-
if(sameTabs.length > 0){
|
|
162
|
-
return this._utils[sameTabs[0].infoId];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// if nothing else
|
|
166
|
-
return this.getMainTabUtils(sessionId);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
getMainTabInfo(sessionId){
|
|
170
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
171
|
-
return Object.keys(infos)
|
|
172
|
-
.map(id => infos[id])
|
|
173
|
-
.find(tabInfo => tabInfo.isMain);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
getMainTabUtils(sessionId){
|
|
177
|
-
var mainTabInfo = this.getMainTabInfo(sessionId);
|
|
178
|
-
if(!mainTabInfo) {
|
|
179
|
-
return {};
|
|
180
|
-
}
|
|
181
|
-
return this.getTabUtils(sessionId, mainTabInfo);
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
removeTabInfo(sessionId, tabId){
|
|
186
|
-
let infos = this.getAllTabInfos(sessionId);
|
|
187
|
-
let info = infos[tabId];
|
|
188
|
-
if (info){
|
|
189
|
-
delete this.sessionTabs[sessionId].tabInfos[tabId];
|
|
190
|
-
delete this._utils[info.infoId];
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
getMainTabId(sessionId){
|
|
195
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
196
|
-
return Object.keys(infos).find(id => infos[id].isMain);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
isMainTabExists(sessionId){
|
|
200
|
-
var mainTabId = this.getMainTabId(sessionId);
|
|
201
|
-
if(!mainTabId){
|
|
202
|
-
return Promise.resolve(false);
|
|
203
|
-
}
|
|
204
|
-
return Promise.resolve(true);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
clearAllTabs(sessionId){
|
|
208
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
209
|
-
|
|
210
|
-
this.sessionTabs[sessionId].tabCount = 0;
|
|
211
|
-
Object.keys(infos)
|
|
212
|
-
.forEach(tabId => this.removeTabInfo(sessionId, tabId));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
clearNonMainTabs(sessionId){
|
|
216
|
-
var infos = this.getAllTabInfos(sessionId);
|
|
217
|
-
|
|
218
|
-
this.sessionTabs[sessionId].tabCount = 1;
|
|
219
|
-
Object.keys(infos)
|
|
220
|
-
.filter(tabId => !infos[tabId].isMain)
|
|
221
|
-
.forEach(tabId => this.removeTabInfo(sessionId, tabId));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
getTabDetails(){
|
|
225
|
-
return Promise.resolve({
|
|
226
|
-
title: "Dummy title",
|
|
227
|
-
url: "Dummy url"
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
isMainTabIncognito() {
|
|
232
|
-
return Promise.resolve(false);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
getTabIdByTabInfo() {
|
|
236
|
-
return Promise.resolve(0);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return new MobileTabService();
|
|
241
|
-
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const Promise = require('bluebird');
|
|
4
|
-
const utils = require('../../../utils');
|
|
5
|
-
|
|
6
|
-
class MobileScreenshotUtils {
|
|
7
|
-
constructor(tabId, driver){
|
|
8
|
-
this.tabId = tabId;
|
|
9
|
-
this.driver = driver;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
base64AddPadding(str) {
|
|
13
|
-
return str + Array((4 - str.length % 4) % 4 + 1).join('=');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
takeScreenshot() {
|
|
17
|
-
const MAX_RETRY_COUNT = 3;
|
|
18
|
-
const SCREENSHOT_RETRY_DELAY = 2000;
|
|
19
|
-
const devicePixelRatioPromise = this.currentDevicePixelRatio ? Promise.resolve(this.currentDevicePixelRatio) : this.getDevicePixelRatio();
|
|
20
|
-
const getScreenshot = () => Promise.all([devicePixelRatioPromise, this.driver.takeScreenshot()]);
|
|
21
|
-
return utils.runWithRetries(getScreenshot, MAX_RETRY_COUNT, SCREENSHOT_RETRY_DELAY)
|
|
22
|
-
.spread((devicePixelRatio, image) => {
|
|
23
|
-
const base64 = image ? image.value : '';
|
|
24
|
-
const dataUrl = "data:image/png;base64," + this.base64AddPadding(base64.replace(/[\r\n]/g, ''));
|
|
25
|
-
return Promise.resolve({
|
|
26
|
-
image: dataUrl,
|
|
27
|
-
devicePixelRatio: devicePixelRatio
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getDevicePixelRatio() {
|
|
33
|
-
return Promise.resolve(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getCurrentDevicePixelRatio() {
|
|
37
|
-
return 1;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
forcePixelRatio(forceRatio = 1){
|
|
41
|
-
this.currentDevicePixelRatio = forceRatio;
|
|
42
|
-
return Promise.resolve();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = MobileScreenshotUtils;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const Promise = require('bluebird');
|
|
4
|
-
const logger = require('../../../commons/logger').getLogger("mobile-window-utils");
|
|
5
|
-
|
|
6
|
-
function WindowUtils(id, driver){
|
|
7
|
-
this.id = id;
|
|
8
|
-
this.driver = driver;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
WindowUtils.prototype.getLocation = function(){
|
|
12
|
-
return this.driver.getUrl();
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
WindowUtils.prototype.stopListeningToScroll = function(){
|
|
16
|
-
return Promise.resolve();
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
WindowUtils.prototype.resumeListeningToScroll = function(){
|
|
20
|
-
return Promise.resolve();
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
WindowUtils.prototype.scrollToPosition = function(){
|
|
24
|
-
return Promise.resolve();
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
WindowUtils.prototype.getCurrentScrollPosition = function(){
|
|
28
|
-
return Promise.resolve();
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
WindowUtils.prototype.navigate = function(){
|
|
32
|
-
return Promise.resolve();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
WindowUtils.prototype.getViewportSize = function(){
|
|
36
|
-
return this.driver.getViewportSize();
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
WindowUtils.prototype.getFullPageSize = function(){
|
|
40
|
-
return Promise.resolve({ height : 1920, width: 1080 });
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
WindowUtils.prototype.extractToNewWindow = function(){
|
|
44
|
-
return Promise.resolve();
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
WindowUtils.prototype.setViewportSize = function() {
|
|
48
|
-
return Promise.resolve();
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
WindowUtils.prototype.validatePageIsAvailable = function(){
|
|
52
|
-
return Promise.resolve();
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
WindowUtils.prototype.focusTab = function(){
|
|
56
|
-
return Promise.resolve();
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
WindowUtils.prototype.quit = function(){
|
|
60
|
-
return undefined;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
WindowUtils.prototype.getOsAndBrowser = function() {
|
|
64
|
-
var packageInfoPromise;
|
|
65
|
-
// TODO: Add support for AUT version for iOS (https://testim.atlassian.net/browse/TES-4211)
|
|
66
|
-
if (this.driver.client && this.driver.client.desiredCapabilities && this.driver.client.desiredCapabilities.appPackage &&
|
|
67
|
-
this.driver.isAndroid()){
|
|
68
|
-
packageInfoPromise = this.driver.getPackageInfo(this.driver.client.desiredCapabilities.appPackage);
|
|
69
|
-
} else {
|
|
70
|
-
logger.warn(`getOsAndBrowser failed to extract AUT package from desiredCapabilities`);
|
|
71
|
-
packageInfoPromise = Promise.resolve({});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return Promise.all([this.driver.getDeviceInfo(), packageInfoPromise])
|
|
75
|
-
.catch(err => {
|
|
76
|
-
logger.warn(`getOsAndBrowser failed`, { err });
|
|
77
|
-
return {}
|
|
78
|
-
})
|
|
79
|
-
.spread((deviceInfo, packageInfo) => {
|
|
80
|
-
return {...packageInfo, ...deviceInfo};
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
module.exports = WindowUtils;
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const StepAction = require('../../stepAction');
|
|
4
|
-
const sessionPlayer = require('../../../../commons/getSessionPlayerRequire');
|
|
5
|
-
const mobileUtils = sessionPlayer.mobileUtils;
|
|
6
|
-
const { getElementAttributesMap, getXPathSelector } = require('appium-dom-utils');
|
|
7
|
-
const logger = require('../../../../commons/logger').getLogger("android-locate-step-action");
|
|
8
|
-
|
|
9
|
-
const { JSDOM, VirtualConsole } = require('jsdom');
|
|
10
|
-
|
|
11
|
-
function createUtils(driver) {
|
|
12
|
-
return class {
|
|
13
|
-
static getFrameIdByTestimFrameId() {}
|
|
14
|
-
|
|
15
|
-
static setElementResultDataOnContext(target, result) {
|
|
16
|
-
const { dom, element, success } = result;
|
|
17
|
-
if (!success) {
|
|
18
|
-
return Promise.resolve();
|
|
19
|
-
}
|
|
20
|
-
const selector = getXPathSelector(element);
|
|
21
|
-
if (!selector) {
|
|
22
|
-
return Promise.reject(new Error('Could not build Android selector'));
|
|
23
|
-
}
|
|
24
|
-
target.appiumSelector = selector;
|
|
25
|
-
|
|
26
|
-
const attributes = getElementAttributesMap(element);
|
|
27
|
-
delete attributes["testim_dom_element_id"];
|
|
28
|
-
target.attributes = attributes;
|
|
29
|
-
|
|
30
|
-
return driver.getElementBySelector(target.appiumSelector)
|
|
31
|
-
.then(seleniumResponse => target.seleniumElement = seleniumResponse.value);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static getElementRectangle(target) {
|
|
35
|
-
if (mobileUtils) {
|
|
36
|
-
const {attributes} = target;
|
|
37
|
-
const {left, top, right, bottom} = mobileUtils.parseAndroidRectFromString(attributes.bounds);
|
|
38
|
-
return Promise.resolve({left, top, width: right - left, height: bottom - top});
|
|
39
|
-
}
|
|
40
|
-
return driver.getElementRect(target);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static getOffsets() {
|
|
44
|
-
return Promise.resolve([]);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static htmlStringToDom(htmlString, url) {
|
|
48
|
-
|
|
49
|
-
const virtualConsole = new VirtualConsole();
|
|
50
|
-
const jsdom = new JSDOM(htmlString, {
|
|
51
|
-
virtualConsole,
|
|
52
|
-
features: {
|
|
53
|
-
FetchExternalResources: ["script", "frame", "iframe", "link", "img"]
|
|
54
|
-
},
|
|
55
|
-
contentType: "text/xml"
|
|
56
|
-
}) || {};
|
|
57
|
-
|
|
58
|
-
const {document} = jsdom.window;
|
|
59
|
-
process.nextTick(() => {
|
|
60
|
-
jsdom.window.close();
|
|
61
|
-
});
|
|
62
|
-
document.TESTIM_URL = url;
|
|
63
|
-
var elements = Array.from(document.querySelectorAll("*"));
|
|
64
|
-
elements.forEach(function (element, index) {
|
|
65
|
-
element.setAttribute("testim_dom_element_id", index);
|
|
66
|
-
});
|
|
67
|
-
return document;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static isVisible(target) {
|
|
71
|
-
if (target.seleniumElement) {
|
|
72
|
-
return Promise.resolve(true);
|
|
73
|
-
}
|
|
74
|
-
return Promise.resolve(false);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class AndroidLocateStepAction extends StepAction {
|
|
81
|
-
execute() {
|
|
82
|
-
return this.driver.getSource()
|
|
83
|
-
.then(res => Promise.resolve({html: res.value}));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static getUtils(driver) {
|
|
87
|
-
return createUtils(driver);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
static getFrameIdByTestimFrameId(...args) {
|
|
92
|
-
logger.warn('Unplanned access to getFrameIdByTestimFrameId() #Android');
|
|
93
|
-
throw new Error('Use .getUtils() instead');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
static setElementResultDataOnContext(...args) {
|
|
97
|
-
logger.warn('Unplanned access to setElementResultDataOnContext() #Android');
|
|
98
|
-
throw new Error('Use .getUtils() instead');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
static getElementRectangle(...args) {
|
|
102
|
-
logger.warn('Unplanned access to getElementRectangle() #Android');
|
|
103
|
-
throw new Error('Use .getUtils() instead');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
static getOffsets(...args) {
|
|
107
|
-
logger.warn('Unplanned access to getOffsets() #Android');
|
|
108
|
-
throw new Error('Use .getUtils() instead');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
static htmlStringToDom(...args) {
|
|
112
|
-
logger.warn('Unplanned access to htmlStringToDom() #Android');
|
|
113
|
-
throw new Error('Use .getUtils() instead');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
static isVisible(...args) {
|
|
117
|
-
logger.warn('Unplanned access to isVisible() #Android');
|
|
118
|
-
throw new Error('Use .getUtils() instead');
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
module.exports = AndroidLocateStepAction;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const StepAction = require('../../stepAction');
|
|
4
|
-
|
|
5
|
-
class AndroidLongClickStepAction extends StepAction {
|
|
6
|
-
performAction() {
|
|
7
|
-
return this.driver.longClick(this.getTarget().appiumSelector);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = AndroidLongClickStepAction;
|
|
12
|
-
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const Promise = require('bluebird');
|
|
3
|
-
const Directions = { Up: "scroll_up", Down: "scroll_down", Left: "scroll_left", Right: "scroll_right", };
|
|
4
|
-
const _ = require('lodash');
|
|
5
|
-
const SwipeStepAction = require('./androidSwipeStepAction');
|
|
6
|
-
const logger = require('../../../../commons/logger').getLogger('android-scroll-step-action');
|
|
7
|
-
const sessionPlayer = require('../../../../commons/getSessionPlayerRequire');
|
|
8
|
-
const constants = sessionPlayer.commonConstants.stepResult;
|
|
9
|
-
class AndroidScrollStepAction extends SwipeStepAction {
|
|
10
|
-
|
|
11
|
-
static getDirection(event) {
|
|
12
|
-
const { distance } = event;
|
|
13
|
-
if (Math.abs(distance.x) < Math.abs(distance.y)) {
|
|
14
|
-
// vertical
|
|
15
|
-
return distance.y > 0 ? Directions.Down : Directions.Up;
|
|
16
|
-
} else {
|
|
17
|
-
// horizontal
|
|
18
|
-
return distance.x > 0 ? Directions.Right : Directions.Left;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
static getScrollCoordinatesFromPageScroll(target, direction, offsetFromStart = 0.75) {
|
|
23
|
-
const {rect} = target;
|
|
24
|
-
const smallOffset = 5;
|
|
25
|
-
let startX, startY, endX, endY;
|
|
26
|
-
switch (direction) {
|
|
27
|
-
case Directions.Down:
|
|
28
|
-
startX = rect.left + (rect.width / 2);
|
|
29
|
-
startY = rect.top + (rect.height * offsetFromStart);
|
|
30
|
-
endX = startX;
|
|
31
|
-
endY = rect.top + smallOffset;
|
|
32
|
-
break;
|
|
33
|
-
case Directions.Up:
|
|
34
|
-
startX = rect.left + (rect.width / 2);
|
|
35
|
-
startY = rect.top + (rect.height * (1 - offsetFromStart));
|
|
36
|
-
endX = startX;
|
|
37
|
-
endY = rect.top + rect.height - smallOffset;
|
|
38
|
-
break;
|
|
39
|
-
case Directions.Right:
|
|
40
|
-
startX = rect.left + (rect.width * offsetFromStart);
|
|
41
|
-
startY = rect.top + (rect.height / 2);
|
|
42
|
-
endX = rect.left + smallOffset;
|
|
43
|
-
endY = startY;
|
|
44
|
-
break;
|
|
45
|
-
case Directions.Left:
|
|
46
|
-
startX = rect.left + (rect.width * (1 - offsetFromStart));
|
|
47
|
-
startY = rect.top + (rect.height / 2);
|
|
48
|
-
endX = rect.left + rect.width - smallOffset;
|
|
49
|
-
endY = startY;
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
return {start: {x: startX, y: startY}, end: {x: endX, y: endY}};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
scrollToElement(step, scrolledOnElementTarget, scrollDirection) {
|
|
56
|
-
const runTimeout = this.context.data.timeToPlayStep;
|
|
57
|
-
const runMaxTime = Date.now() + runTimeout;
|
|
58
|
-
return this.locateElementPlayer.findElements([this.step.scrollToElement], step, this.frameHandler)
|
|
59
|
-
.catch((err) => {
|
|
60
|
-
const { start, end } = AndroidScrollStepAction.getScrollCoordinatesFromPageScroll(scrolledOnElementTarget, scrollDirection, 0.75);
|
|
61
|
-
if (Date.now() > runMaxTime) {
|
|
62
|
-
logger.info("Scroll to element reached timeout");
|
|
63
|
-
throw Object.assign(new Error("Scroll timeout reached:" + err.message), {
|
|
64
|
-
id: this.step.id,
|
|
65
|
-
success: false,
|
|
66
|
-
shouldRetry: true,
|
|
67
|
-
isTimeout: true,
|
|
68
|
-
errorType: constants.ACTION_TIMEOUT
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return this.driver.scroll(start, end)
|
|
72
|
-
.then(() => this.scrollToElement(step, scrolledOnElementTarget, scrollDirection));
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
getScrollToElementDirection(events) {
|
|
77
|
-
const eventDirections = events.reduce((directions, event) => {
|
|
78
|
-
directions[AndroidScrollStepAction.getDirection(event)]++;
|
|
79
|
-
return directions;
|
|
80
|
-
}, {
|
|
81
|
-
[Directions.Up]: 0,
|
|
82
|
-
[Directions.Down]: 0,
|
|
83
|
-
[Directions.Left]: 0,
|
|
84
|
-
[Directions.Right]: 0
|
|
85
|
-
});
|
|
86
|
-
const maxDirectionsEvents = _.max(_.values(eventDirections));
|
|
87
|
-
|
|
88
|
-
return _.findKey(eventDirections, e => e === maxDirectionsEvents);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
static getScrollCoordinatesFromSwipe({elementPositionTouchDown, elementPositionTouchUp}, {rect}) {
|
|
92
|
-
return {
|
|
93
|
-
start: {
|
|
94
|
-
x: rect.left + rect.width * elementPositionTouchDown.percentX,
|
|
95
|
-
y: rect.top + rect.height * elementPositionTouchDown.percentY,
|
|
96
|
-
},
|
|
97
|
-
end: {
|
|
98
|
-
x: rect.left + rect.width * elementPositionTouchUp.percentX,
|
|
99
|
-
y: rect.top + rect.height * elementPositionTouchUp.percentY,
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
performAction(_, step) {
|
|
105
|
-
if (!step) {
|
|
106
|
-
step = this.step;
|
|
107
|
-
logger.warn('a falsy step object was received in performAction(). Using stepData instead', {stepType: this.step.type});
|
|
108
|
-
}
|
|
109
|
-
const target = this.getTarget();
|
|
110
|
-
if (!step.isScrollToElement) {
|
|
111
|
-
// absolute scroll
|
|
112
|
-
return Promise.each(step.events, event => {
|
|
113
|
-
let start, end;
|
|
114
|
-
if (step.isGranularScroll) {
|
|
115
|
-
({start, end} = this.getScrollCoordinatesFromSwipe(event, target));
|
|
116
|
-
} else {
|
|
117
|
-
// scroll entire page per event
|
|
118
|
-
const direction = AndroidScrollStepAction.getDirection(event);
|
|
119
|
-
({ start, end } = AndroidScrollStepAction.getScrollCoordinatesFromPageScroll(target, direction, 0.9));
|
|
120
|
-
}
|
|
121
|
-
return this.driver.scroll(start, end, step.scrollDuration);
|
|
122
|
-
}).then(() => {
|
|
123
|
-
return {success: true};
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this.step.scrollToElement.targetId = "scroll-to-element";
|
|
128
|
-
const scrollDirection = this.getScrollToElementDirection(step.events);
|
|
129
|
-
return this.scrollToElement(step, target, scrollDirection);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = AndroidScrollStepAction;
|
|
134
|
-
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const StepAction = require('../../stepAction');
|
|
4
|
-
|
|
5
|
-
const ALLOWED_KEY_CODE_NAME = ['normal', 'unspecified', 'none', 'go', 'search', 'send', 'next', 'done', 'previous'];
|
|
6
|
-
|
|
7
|
-
class AndroidSpecialKeyStepAction extends StepAction {
|
|
8
|
-
performAction() {
|
|
9
|
-
const {step} = this;
|
|
10
|
-
const action = this.getActionKeyCodeName(step.keyCodeName);
|
|
11
|
-
|
|
12
|
-
return this.driver.executeImeAction(action);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
getActionKeyCodeName(keyCodeName) {
|
|
16
|
-
const lowerKeyCodeName = keyCodeName.toLowerCase();
|
|
17
|
-
return ALLOWED_KEY_CODE_NAME.includes(lowerKeyCodeName) ? lowerKeyCodeName : 'go';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = AndroidSpecialKeyStepAction;
|
|
22
|
-
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const StepAction = require('../../stepAction');
|
|
4
|
-
|
|
5
|
-
const normalizePercent = (percent) => {
|
|
6
|
-
const percentCeiling = Math.min(percent, 0.99);
|
|
7
|
-
const percentFloor = Math.max(percentCeiling, 0.01);
|
|
8
|
-
return percentFloor;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
class AndroidSwipeStepAction extends StepAction {
|
|
12
|
-
|
|
13
|
-
static getSwipeCoordinates(target, event) {
|
|
14
|
-
const { rect } = target;
|
|
15
|
-
const { elementPositionTouchDown, elementPositionTouchUp } = event;
|
|
16
|
-
const startX = (rect.width * normalizePercent(elementPositionTouchDown.percentX)) + rect.left;
|
|
17
|
-
const startY = (rect.height * normalizePercent(elementPositionTouchDown.percentY)) + rect.top;
|
|
18
|
-
|
|
19
|
-
const endX = (rect.width * normalizePercent(elementPositionTouchUp.percentX)) + rect.left;
|
|
20
|
-
const endY = (rect.height * normalizePercent(elementPositionTouchUp.percentY)) + rect.top;
|
|
21
|
-
|
|
22
|
-
return {start: {x: startX, y: startY}, end: {x: endX, y: endY}};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
performAction() {
|
|
26
|
-
const { start, end} = AndroidSwipeStepAction.getSwipeCoordinates(this.getTarget(), this.step.events[0]);
|
|
27
|
-
return this.driver.swipe(start, end, this.step.swipeDuration);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = AndroidSwipeStepAction;
|
|
32
|
-
|