@applitools/driver 1.9.2 → 1.9.5

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.
@@ -33,9 +33,13 @@ function parseCapabilities(capabilities, customConfig) {
33
33
  info.orientation = (_m = ((_l = capabilities.deviceOrientation) !== null && _l !== void 0 ? _l : capabilities.orientation)) === null || _m === void 0 ? void 0 : _m.toLowerCase();
34
34
  }
35
35
  if (info.isNative) {
36
+ info.displaySize = extractDisplaySize(capabilities);
36
37
  info.pixelRatio = capabilities.pixelRatio;
37
38
  info.statusBarHeight = capabilities.statBarHeight;
38
- info.displaySize = extractDisplaySize(capabilities);
39
+ if (info.displaySize && capabilities.viewportRect) {
40
+ info.navigationBarHeight =
41
+ info.displaySize.height - (capabilities.viewportRect.top + capabilities.viewportRect.height);
42
+ }
39
43
  }
40
44
  return info;
41
45
  }
package/dist/driver.js CHANGED
@@ -184,11 +184,11 @@ class Driver {
184
184
  // navigation bar height is replaced with the width in landscape orientation on android (due to the bug in appium)
185
185
  if (orientation === 'landscape')
186
186
  barsSize.navigationBarHeight = barsSize.navigationBarWidth;
187
- // when status bar is overlapping content on android it returns status bar height equal to viewport height
187
+ // when status bar is overlapping content on android it returns status bar height equal to display height
188
188
  if (barsSize.statusBarHeight < this._driverInfo.displaySize.height) {
189
189
  this._driverInfo.statusBarHeight = Math.max((_u = this._driverInfo.statusBarHeight) !== null && _u !== void 0 ? _u : 0, barsSize.statusBarHeight);
190
190
  }
191
- // when navigation bar is invisible on android it returns navigation bar height equal to viewport height
191
+ // when navigation bar is invisible on android it returns navigation bar height equal to display height
192
192
  if (barsSize.navigationBarHeight < this._driverInfo.displaySize.height) {
193
193
  this._driverInfo.navigationBarHeight = Math.max((_v = this._driverInfo.navigationBarHeight) !== null && _v !== void 0 ? _v : 0, barsSize.navigationBarHeight);
194
194
  }
package/dist/element.js CHANGED
@@ -278,18 +278,24 @@ class Element {
278
278
  });
279
279
  }
280
280
  async getTouchPadding() {
281
+ var _a, _b;
281
282
  if (this._state.touchPadding == null) {
282
283
  if (this.driver.isWeb)
283
284
  this._state.touchPadding = 0;
284
285
  else if (this.driver.isIOS)
285
286
  this._state.touchPadding = 10;
286
287
  else if (this.driver.isAndroid) {
287
- const touchPadding = await this.getAttribute('contentSize')
288
- .then(data => JSON.parse(data).touchPadding)
289
- .catch(err => {
290
- this._logger.warn(`Unable to get the attribute 'contentSize' when looking up 'touchPadding' due to the following error: '${err.message}'`);
291
- });
292
- this._state.touchPadding = touchPadding !== null && touchPadding !== void 0 ? touchPadding : 20;
288
+ if (((_a = this.driver.helper) === null || _a === void 0 ? void 0 : _a.name) === 'android') {
289
+ this._state.touchPadding = await ((_b = this.driver.helper) === null || _b === void 0 ? void 0 : _b.getTouchPadding());
290
+ }
291
+ if (!this._state.touchPadding) {
292
+ const touchPadding = await this.getAttribute('contentSize')
293
+ .then(data => JSON.parse(data).touchPadding)
294
+ .catch(err => {
295
+ this._logger.warn(`Unable to get the attribute 'contentSize' when looking up 'touchPadding' due to the following error: '${err.message}'`);
296
+ });
297
+ this._state.touchPadding = touchPadding !== null && touchPadding !== void 0 ? touchPadding : 20;
298
+ }
293
299
  this._logger.log('Touch padding set:', this._state.touchPadding);
294
300
  }
295
301
  }
@@ -311,8 +317,11 @@ class Element {
311
317
  async getAttribute(name) {
312
318
  var _a;
313
319
  // we assumes that attributes are not changed during the session
314
- if ((_a = this._state.attributes) === null || _a === void 0 ? void 0 : _a[name])
320
+ if ((_a = this._state.attributes) === null || _a === void 0 ? void 0 : _a[name]) {
321
+ if (this._state.attributes[name] instanceof Error)
322
+ throw this._state.attributes[name];
315
323
  return this._state.attributes[name];
324
+ }
316
325
  const value = await this.withRefresh(async () => {
317
326
  var _a;
318
327
  var _b;
@@ -322,17 +331,24 @@ class Element {
322
331
  }
323
332
  else {
324
333
  this._logger.log(`Extracting "${name}" attribute of native element with selector`, this.selector);
325
- const value = await this._spec.getElementAttribute(this.driver.target, this.target, name);
326
334
  (_a = (_b = this._state).attributes) !== null && _a !== void 0 ? _a : (_b.attributes = {});
327
- this._state.attributes[name] = value;
328
- if (this.driver.isAndroid && name === 'contentSize') {
329
- // android has a bug when after extracting 'contentSize' attribute the element is being scrolled by undetermined number of pixels
330
- this._logger.log('Stabilizing android scroll offset');
331
- const originalScrollOffset = await this.getScrollOffset();
332
- await this.scrollTo({ x: 0, y: 0 }, { force: true });
333
- await this.scrollTo(originalScrollOffset);
335
+ try {
336
+ this._state.attributes[name] = await this._spec.getElementAttribute(this.driver.target, this.target, name);
337
+ return this._state.attributes[name];
338
+ }
339
+ catch (err) {
340
+ this._state.attributes[name] = err;
341
+ throw err;
342
+ }
343
+ finally {
344
+ if (this.driver.isAndroid && name === 'contentSize') {
345
+ // android has a bug when after extracting 'contentSize' attribute the element is being scrolled by undetermined number of pixels
346
+ this._logger.log('Stabilizing android scroll offset');
347
+ const originalScrollOffset = await this.getScrollOffset();
348
+ await this.scrollTo({ x: 0, y: 0 }, { force: true });
349
+ await this.scrollTo(originalScrollOffset);
350
+ }
334
351
  }
335
- return value;
336
352
  }
337
353
  });
338
354
  this._logger.log(`Extracted element "${name}" attribute:`, value);
@@ -345,6 +361,7 @@ class Element {
345
361
  }
346
362
  async scrollTo(offset, options) {
347
363
  return this.withRefresh(async () => {
364
+ var _a;
348
365
  offset = utils.geometry.round({ x: Math.max(offset.x, 0), y: Math.max(offset.y, 0) });
349
366
  if (this.driver.isWeb) {
350
367
  let actualOffset = await this.context.execute(snippets.scrollTo, [this, offset]);
@@ -364,22 +381,29 @@ class Element {
364
381
  y: Math.round(scrollableRegion.height * (contentSize.height / scrollableRegion.height - 1)),
365
382
  };
366
383
  const requiredOffset = { x: Math.min(offset.x, maxOffset.x), y: Math.min(offset.y, maxOffset.y) };
384
+ if (((_a = this.driver.helper) === null || _a === void 0 ? void 0 : _a.name) === 'android' && utils.geometry.equals(requiredOffset, { x: 0, y: 0 })) {
385
+ await this.driver.helper.scrollToTop(this);
386
+ this._state.scrollOffset = requiredOffset;
387
+ return this._state.scrollOffset;
388
+ }
367
389
  let effectiveRegion = scrollableRegion;
368
390
  let remainingOffset = utils.geometry.equals(requiredOffset, { x: 0, y: 0 })
369
391
  ? { x: -maxOffset.x, y: -maxOffset.y } // if it has to be scrolled to the very beginning, then scroll maximum amount of pixels
370
392
  : utils.geometry.offsetNegative(requiredOffset, currentScrollOffset);
371
393
  if (this.driver.isAndroid) {
372
- remainingOffset = utils.geometry.scale(remainingOffset, this.driver.pixelRatio);
373
- effectiveRegion = utils.geometry.scale(scrollableRegion, this.driver.pixelRatio);
394
+ remainingOffset = utils.geometry.round(utils.geometry.scale(remainingOffset, this.driver.pixelRatio));
395
+ effectiveRegion = utils.geometry.round(utils.geometry.scale(effectiveRegion, this.driver.pixelRatio));
374
396
  }
375
- const actions = [];
376
- const touchPadding = await this.getTouchPadding();
377
397
  const isPager = await this.isPager();
378
- const xPadding = Math.max(Math.floor(effectiveRegion.width * 0.07), touchPadding);
398
+ const touchPadding = await this.getTouchPadding();
399
+ const actions = [];
400
+ // horizontal scrolling
401
+ const xPadding = touchPadding + 3;
379
402
  const yTrack = Math.floor(effectiveRegion.y + effectiveRegion.height / 2); // center
380
403
  const xLeft = effectiveRegion.y + xPadding;
381
404
  const xDirection = remainingOffset.x > 0 ? 'right' : 'left';
382
405
  const xGap = xDirection === 'right' ? -touchPadding : touchPadding;
406
+ const xCompensation = xDirection === 'right' ? -1 : 1;
383
407
  let xRemaining = Math.abs(remainingOffset.x);
384
408
  if (isPager) {
385
409
  const xPages = Math.floor(xRemaining / effectiveRegion.width);
@@ -397,28 +421,44 @@ class Element {
397
421
  { action: 'release' },
398
422
  ]);
399
423
  }
400
- else {
424
+ else if (this.driver.isAndroid) {
401
425
  actions.push([
402
- { action: 'press', y: yTrack, x: xStart },
403
426
  // move through scrolling gap (actual scrolling will be triggered only after that)
427
+ { action: 'press', y: yTrack, x: xStart - xGap },
428
+ { action: 'wait', ms: 100 },
429
+ { action: 'moveTo', y: yTrack, x: xStart + xCompensation },
404
430
  { action: 'wait', ms: 100 },
405
- { action: 'moveTo', y: yTrack, x: xStart + xGap },
431
+ { action: 'moveTo', y: yTrack, x: xStart },
406
432
  // perform actual scrolling
407
433
  { action: 'wait', ms: 100 },
408
- { action: 'moveTo', y: yTrack, x: xEnd + xGap },
434
+ { action: 'moveTo', y: yTrack, x: xEnd },
435
+ { action: 'release' },
436
+ ]);
437
+ }
438
+ else if (this.driver.isIOS) {
439
+ actions.push([
440
+ // move through scrolling gap (actual scrolling will be triggered only after that)
441
+ { action: 'press', y: yTrack, x: xStart - xGap },
442
+ { action: 'wait', ms: 100 },
443
+ { action: 'moveTo', y: yTrack, x: xStart },
444
+ // perform actual scrolling
445
+ { action: 'wait', ms: 100 },
446
+ { action: 'moveTo', y: yTrack, x: xEnd },
409
447
  // prevent inertial scrolling after release
410
448
  { action: 'wait', ms: 100 },
411
- { action: 'moveTo', y: yTrack + 1, x: xEnd + xGap },
449
+ { action: 'moveTo', y: yTrack + 1, x: xEnd },
412
450
  { action: 'release' },
413
451
  ]);
414
452
  }
415
453
  xRemaining -= xRight - xLeft;
416
454
  }
417
- const yPadding = Math.max(Math.floor(effectiveRegion.height * 0.07), touchPadding);
455
+ // vertical scrolling
456
+ const yPadding = Math.max(Math.floor(effectiveRegion.height * 0.05), touchPadding + 3);
418
457
  const xTrack = Math.floor(effectiveRegion.x + 5); // a little bit off left border
419
458
  const yBottom = effectiveRegion.y + effectiveRegion.height - yPadding;
420
459
  const yDirection = remainingOffset.y > 0 ? 'down' : 'up';
421
460
  const yGap = yDirection === 'down' ? -touchPadding : touchPadding;
461
+ const yCompensation = yDirection === 'down' ? -1 : 1;
422
462
  let yRemaining = Math.abs(remainingOffset.y);
423
463
  if (isPager) {
424
464
  const yPages = Math.floor(yRemaining / effectiveRegion.height);
@@ -436,18 +476,32 @@ class Element {
436
476
  { action: 'release' },
437
477
  ]);
438
478
  }
439
- else {
479
+ else if (this.driver.isAndroid) {
480
+ actions.push([
481
+ // move through scrolling gap (actual scrolling will be triggered only after that)
482
+ { action: 'press', x: xTrack, y: yStart - yGap },
483
+ { action: 'wait', ms: 100 },
484
+ { action: 'moveTo', x: xTrack, y: yStart + yCompensation },
485
+ { action: 'wait', ms: 100 },
486
+ { action: 'moveTo', x: xTrack, y: yStart },
487
+ // perform actual scrolling
488
+ { action: 'wait', ms: 100 },
489
+ { action: 'moveTo', x: xTrack, y: yEnd },
490
+ { action: 'release' },
491
+ ]);
492
+ }
493
+ else if (this.driver.isIOS) {
440
494
  actions.push([
441
- { action: 'press', x: xTrack, y: yStart },
442
495
  // move through scrolling gap (actual scrolling will be triggered only after that)
496
+ { action: 'press', x: xTrack, y: yStart - yGap },
443
497
  { action: 'wait', ms: 100 },
444
- { action: 'moveTo', x: xTrack, y: yStart + yGap },
498
+ { action: 'moveTo', x: xTrack, y: yStart },
445
499
  // perform actual scrolling
446
500
  { action: 'wait', ms: 100 },
447
- { action: 'moveTo', x: xTrack, y: yEnd + yGap },
501
+ { action: 'moveTo', x: xTrack, y: yEnd },
448
502
  // prevent inertial scrolling after release
449
503
  { action: 'wait', ms: 100 },
450
- { action: 'moveTo', x: xTrack + 1, y: yEnd + yGap },
504
+ { action: 'moveTo', x: xTrack + 1, y: yEnd },
451
505
  { action: 'release' },
452
506
  ]);
453
507
  }
@@ -1,12 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.HelperAndroid = void 0;
27
+ const utils = __importStar(require("@applitools/utils"));
4
28
  class HelperAndroid {
5
29
  constructor(options) {
6
30
  this._spec = options.spec;
7
31
  this._element = options.element;
8
32
  this._legacy = options.legacy;
9
33
  this._logger = options.logger;
34
+ this.name = this._legacy ? 'android-legacy' : 'android';
10
35
  }
11
36
  static async make(options) {
12
37
  const { spec, driver, logger } = options;
@@ -31,10 +56,10 @@ class HelperAndroid {
31
56
  return resourceId.split('/')[1];
32
57
  }
33
58
  async getContentSize(element) {
34
- let contentHeight;
59
+ let contentHeightString;
35
60
  if (this._legacy) {
36
61
  await this._element.click();
37
- contentHeight = await this._element.getText();
62
+ contentHeightString = await this._element.getText();
38
63
  }
39
64
  else {
40
65
  const elementId = await this._getElementId(element);
@@ -42,11 +67,14 @@ class HelperAndroid {
42
67
  return null;
43
68
  await this._element.type(`offset;${elementId};0;0;0`);
44
69
  await this._element.click();
45
- contentHeight = await this._element.getText();
70
+ contentHeightString = await this._element.getText();
46
71
  await this._element.type('');
47
72
  }
48
73
  const region = await this._spec.getElementRegion(this._element.driver.target, element.target);
49
- return { width: region.width, height: Number(contentHeight) };
74
+ const contentHeight = Number(contentHeightString);
75
+ if (Number.isNaN(contentHeight))
76
+ return utils.geometry.size(region);
77
+ return { width: region.width, height: contentHeight };
50
78
  }
51
79
  async getRegion(element) {
52
80
  if (this._legacy)
@@ -64,5 +92,37 @@ class HelperAndroid {
64
92
  return null;
65
93
  return region;
66
94
  }
95
+ async scrollToTop(element) {
96
+ if (this._legacy)
97
+ return null;
98
+ const elementId = await this._getElementId(element);
99
+ if (!elementId)
100
+ return null;
101
+ await this._element.type(`moveToTop;${elementId};0;-1`);
102
+ await this._element.click();
103
+ await this._element.type('');
104
+ }
105
+ async scrollBy(element, offset) {
106
+ if (this._legacy)
107
+ return null;
108
+ const elementId = await this._getElementId(element);
109
+ if (!elementId)
110
+ return null;
111
+ await this._element.type(`scroll;${elementId};${offset.y};0;0`);
112
+ await this._element.click();
113
+ await this._element.type('');
114
+ }
115
+ async getTouchPadding() {
116
+ if (this._legacy)
117
+ return null;
118
+ await this._element.type(`getTouchPadding;0;0;0;0`);
119
+ await this._element.click();
120
+ const touchPaddingString = await this._element.getText();
121
+ await this._element.type('');
122
+ const touchPadding = Number(touchPaddingString);
123
+ if (Number.isNaN(touchPadding))
124
+ return null;
125
+ return touchPadding;
126
+ }
67
127
  }
68
128
  exports.HelperAndroid = HelperAndroid;
@@ -7,6 +7,7 @@ class HelperIOS {
7
7
  this._element = options.element;
8
8
  this._spec = options.spec;
9
9
  this._logger = options.logger;
10
+ this.name = 'ios';
10
11
  }
11
12
  static async make(options) {
12
13
  const { spec, driver, logger } = options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/driver",
3
- "version": "1.9.2",
3
+ "version": "1.9.5",
4
4
  "description": "Applitools universal framework wrapper",
5
5
  "keywords": [
6
6
  "applitools",
@@ -24,6 +24,9 @@
24
24
  "name": "Applitools Team",
25
25
  "email": "team@applitools.com"
26
26
  },
27
+ "aliases": [
28
+ "driver"
29
+ ],
27
30
  "exports": {
28
31
  ".": {
29
32
  "types": "./types/index.d.ts",
@@ -73,13 +76,13 @@
73
76
  }
74
77
  },
75
78
  "dependencies": {
76
- "@applitools/logger": "1.1.10",
77
- "@applitools/snippets": "2.2.3",
78
- "@applitools/types": "1.4.7",
79
- "@applitools/utils": "1.3.6"
79
+ "@applitools/logger": "1.1.13",
80
+ "@applitools/snippets": "2.4.3",
81
+ "@applitools/types": "1.5.4",
82
+ "@applitools/utils": "1.3.9"
80
83
  },
81
84
  "devDependencies": {
82
- "@applitools/bongo": "^2.1.4",
85
+ "@applitools/bongo": "^2.1.5",
83
86
  "@types/mocha": "^9.1.1",
84
87
  "@types/node": "^17.0.31",
85
88
  "@typescript-eslint/eslint-plugin": "^5.22.0",
@@ -5,7 +5,7 @@ export declare type ElementState<TElement> = {
5
5
  contentSize?: types.Size;
6
6
  scrollOffset?: types.Location;
7
7
  transforms?: any;
8
- attributes?: Record<string, string>;
8
+ attributes?: Record<string, string | Error>;
9
9
  touchPadding?: number;
10
10
  containedElements?: Map<TElement, boolean>;
11
11
  };
@@ -12,6 +12,7 @@ export declare class HelperAndroid<TDriver, TContext, TElement, TSelector> {
12
12
  private readonly _element;
13
13
  private readonly _legacy;
14
14
  private _logger;
15
+ readonly name: 'android' | 'android-legacy';
15
16
  constructor(options: {
16
17
  spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
17
18
  element: Element<TDriver, TContext, TElement, TSelector>;
@@ -21,4 +22,7 @@ export declare class HelperAndroid<TDriver, TContext, TElement, TSelector> {
21
22
  _getElementId(element: Element<TDriver, TContext, TElement, TSelector>): Promise<string>;
22
23
  getContentSize(element: Element<TDriver, TContext, TElement, TSelector>): Promise<types.Size>;
23
24
  getRegion(element: Element<TDriver, TContext, TElement, TSelector>): Promise<types.Region>;
25
+ scrollToTop(element: Element<TDriver, TContext, TElement, TSelector>): Promise<void>;
26
+ scrollBy(element: Element<TDriver, TContext, TElement, TSelector>, offset: types.Location): Promise<void>;
27
+ getTouchPadding(): Promise<number>;
24
28
  }
@@ -12,6 +12,7 @@ export declare class HelperIOS<TDriver, TContext, TElement, TSelector> {
12
12
  private readonly _element;
13
13
  private readonly _spec;
14
14
  private _logger;
15
+ readonly name: 'ios';
15
16
  constructor(options: {
16
17
  driver: Driver<TDriver, TContext, TElement, TSelector>;
17
18
  element: Element<TDriver, TContext, TElement, TSelector>;