@applitools/driver 1.3.2 → 1.4.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/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
3
3
  ## Unreleased
4
4
 
5
5
 
6
+ ## 1.4.0 - 2021/12/16
7
+
8
+ - improve native apps scrolling automation
9
+ - fix ios safe area related issues
10
+ - add helper library abstraction to cover appium edge cases
11
+ - made `setViewportSize` more reliable in worst case scenario and faster in best case scenario
12
+ - updated to @applitools/types@1.0.22 (from 1.0.21)
13
+
14
+ ## 1.3.5 - 2021/11/23
15
+
16
+ - updated to @applitools/types@1.0.21 (from 1.0.20)
17
+
18
+ ## 1.3.4 - 2021/11/18
19
+
20
+ - fix capabilities parsing for native apps
21
+
22
+ ## 1.3.3 - 2021/11/14
23
+
24
+ - do not throw if `getCookies` method is missed in spec driver
25
+
6
26
  ## 1.3.2 - 2021/11/14
7
27
 
8
28
  - adjust scrolling algorithm on native devices
@@ -4,7 +4,9 @@ exports.parseCapabilities = void 0;
4
4
  function parseCapabilities(capabilities) {
5
5
  var _a, _b, _c, _d, _e, _f, _g, _h;
6
6
  const info = {
7
- browserName: ((_a = capabilities.browserName) !== null && _a !== void 0 ? _a : (_b = capabilities.desired) === null || _b === void 0 ? void 0 : _b.browserName) || undefined,
7
+ browserName: !capabilities.app && !capabilities.bundleId
8
+ ? ((_a = capabilities.browserName) !== null && _a !== void 0 ? _a : (_b = capabilities.desired) === null || _b === void 0 ? void 0 : _b.browserName) || undefined
9
+ : undefined,
8
10
  browserVersion: ((_c = capabilities.browserVersion) !== null && _c !== void 0 ? _c : capabilities.version) || undefined,
9
11
  platformName: ((_e = (_d = capabilities.platformName) !== null && _d !== void 0 ? _d : capabilities.platform) !== null && _e !== void 0 ? _e : (_f = capabilities.desired) === null || _f === void 0 ? void 0 : _f.platformName) || undefined,
10
12
  platformVersion: capabilities.platformVersion || undefined,
package/dist/context.js CHANGED
@@ -438,11 +438,11 @@ class Context {
438
438
  return region;
439
439
  }
440
440
  async getCookies() {
441
- var _a, _b;
441
+ var _a, _b, _c;
442
442
  if (this.driver.isNative)
443
443
  return [];
444
444
  await this.focus();
445
- return (_b = (_a = this._spec) === null || _a === void 0 ? void 0 : _a.getCookies(this.target, true)) !== null && _b !== void 0 ? _b : [];
445
+ return (_c = (_b = (_a = this._spec).getCookies) === null || _b === void 0 ? void 0 : _b.call(_a, this.target, true)) !== null && _c !== void 0 ? _c : [];
446
446
  }
447
447
  async preserveInnerOffset() {
448
448
  this._state.innerOffset = await this.getInnerOffset();
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkSpecDriver = void 0;
7
+ const strict_1 = __importDefault(require("assert/strict"));
8
+ const snippets = require('@applitools/snippets');
9
+ async function checkSpecDriver(options) {
10
+ const { spec, driver } = options;
11
+ const context = extractContext(driver);
12
+ const tests = {
13
+ 'execute script with echo snippet': async () => {
14
+ const arg = [1, '23', false, { a: 4, b: [5] }, null];
15
+ const result = await spec.executeScript(context, 'return arguments[0]', arg);
16
+ strict_1.default.deepEqual(result, arg, 'script returns an array of json data');
17
+ },
18
+ 'execute script with functional script': async () => {
19
+ const arg = { a: 2, b: 1, c: 7 };
20
+ function script(arg) {
21
+ return arg;
22
+ }
23
+ const result = await spec.executeScript(context, script, arg);
24
+ strict_1.default.deepEqual(result, arg, 'script returns an array of json data from functional script');
25
+ },
26
+ 'execute script with return value of dom element': async () => {
27
+ const element = await spec.executeScript(context, 'return document.documentElement');
28
+ const isHtmlElement = await spec.executeScript(context, 'return arguments[0] === document.documentElement', element);
29
+ strict_1.default.ok(isHtmlElement, 'script returns an element and could be executed with an element');
30
+ },
31
+ 'execute script with nested element references': async () => {
32
+ const elements = await spec.executeScript(context, 'return [{html: document.documentElement, body: document.body}]');
33
+ const isElements = await spec.executeScript(context, 'return arguments[0][0].html === document.documentElement && arguments[0][0].html === document.documentElement', elements);
34
+ strict_1.default.ok(isElements, 'script returns elements inside nested structure and could be executed with a nested structure of elements');
35
+ },
36
+ 'find element with string selector': async () => {
37
+ const selector = transformSelector('html>body>h1');
38
+ const element = await spec.findElement(context, selector);
39
+ const isWantedElement = await spec.executeScript(context, `return arguments[0] === document.querySelector("${selector}")`, element);
40
+ strict_1.default.ok(isWantedElement, `returns element by string selector - "${selector}"`);
41
+ },
42
+ 'find element with spec selector': async () => {
43
+ const cssSelector = transformSelector({ type: 'css', selector: 'html>body>h1' });
44
+ const xpathSelector = transformSelector({ type: 'xpath', selector: '//html/body/h1' });
45
+ const verificationScript = `return arguments[0] === document.querySelector('html>body>h1')`;
46
+ const cssElement = await spec.findElement(context, cssSelector);
47
+ const isCssElement = await spec.executeScript(context, verificationScript, cssElement);
48
+ strict_1.default.ok(isCssElement, `returns element by spec selector - ${JSON.stringify(cssSelector)}`);
49
+ const xpathElement = await spec.findElement(context, xpathSelector);
50
+ const isXpathElement = await spec.executeScript(context, verificationScript, xpathElement);
51
+ strict_1.default.ok(isXpathElement, `returns element by spec selector - ${JSON.stringify(xpathSelector)}`);
52
+ },
53
+ 'find element with unresolvable selector': async () => {
54
+ const selector = transformSelector('unresolvable_selector');
55
+ const element = await spec.findElement(context, selector);
56
+ strict_1.default.equal(element, null, `returns null by unresolvable selector - "${selector}"`);
57
+ },
58
+ 'find elements with string selector': async () => {
59
+ const selector = transformSelector('html p');
60
+ const elements = await spec.findElements(context, selector);
61
+ const isExpectedElements = await spec.executeScript(context, `var expected = arguments[0]; return Array.prototype.every.call(document.querySelectorAll("${selector}"), function(element, index) { return element === expected[index] })`, elements);
62
+ strict_1.default.ok(isExpectedElements, `returns elements by string selector - "${selector}"`);
63
+ },
64
+ 'find elements with spec selector': async () => {
65
+ const cssSelector = transformSelector({ type: 'css', selector: 'html p' });
66
+ const xpathSelector = transformSelector({ type: 'xpath', selector: '//html//p' });
67
+ const verificationScript = `var expected = arguments[0]; return Array.prototype.every.call(document.querySelectorAll('html p'), function(element, index) { return element === expected[index] })`;
68
+ const cssElements = await spec.findElements(context, cssSelector);
69
+ const isCssElements = await spec.executeScript(context, verificationScript, cssElements);
70
+ strict_1.default.ok(isCssElements, `returns elements by spec selector - ${JSON.stringify(cssSelector)}`);
71
+ const xpathElements = await spec.findElements(context, xpathSelector);
72
+ const isXpathElements = await spec.executeScript(context, verificationScript, xpathElements);
73
+ strict_1.default.ok(isXpathElements, `returns element by spec selector - ${JSON.stringify(xpathSelector)}`);
74
+ },
75
+ 'find elements with unresolvable selector': async () => {
76
+ const selector = transformSelector('unresolvable_selector');
77
+ const element = await spec.findElements(context, selector);
78
+ strict_1.default.deepEqual(element, [], `returns empty array by unresolvable selector - "${selector}"`);
79
+ },
80
+ 'child context': async () => {
81
+ const element = await spec.findElement(context, transformSelector('[name="frame1"]'));
82
+ const childContext = await spec.childContext(context, element);
83
+ const inFrame = await spec.executeScript(childContext, 'return window.frameElement.name === "frame1"');
84
+ strict_1.default.ok(inFrame, 'returns or switches to a child context');
85
+ strict_1.default.ok(typeof spec.mainContext === 'function', 'spec.mainContext also needs to be implemented in order to test spec.childContext');
86
+ await spec.mainContext(context);
87
+ },
88
+ 'is equal elements': async () => {
89
+ if (!spec.isEqualElements)
90
+ return { skipped: true };
91
+ const htmlEl = await spec.findElement(context, transformSelector('html'));
92
+ const htmlEl2 = await spec.executeScript(context, 'return document.documentElement');
93
+ strict_1.default.ok(await spec.isEqualElements(context, htmlEl, htmlEl2), 'elements should be equal');
94
+ const bodyEl = await spec.executeScript(context, 'return document.body');
95
+ strict_1.default.ok(!(await spec.isEqualElements(context, htmlEl, bodyEl)), 'elements should not be equal');
96
+ strict_1.default.ok(!(await spec.isEqualElements(context, htmlEl, undefined)), 'isEqualElements should return false if one of the arguments is falsy');
97
+ strict_1.default.ok(!(await spec.isEqualElements(context, undefined, htmlEl)), 'isEqualElements should return false if one of the arguments is falsy');
98
+ },
99
+ 'main context': async () => {
100
+ const mainDocument1 = await spec.findElement(context, transformSelector('html'));
101
+ const childContext1 = await spec.childContext(context, await spec.findElement(context, transformSelector('[name="frame1"]')));
102
+ const childContext2 = await spec.childContext(childContext1, await spec.findElement(childContext1, transformSelector('[name="frame1-1"]')));
103
+ const frameDocument = await spec.findElement(childContext2, transformSelector('html'));
104
+ strict_1.default.ok(!(await isEqualElements(childContext2, mainDocument1, frameDocument)));
105
+ const mainContext = await spec.mainContext(childContext2);
106
+ const mainDocument2 = await spec.findElement(mainContext, transformSelector('html'));
107
+ strict_1.default.ok(await isEqualElements(mainContext, mainDocument2, mainDocument1));
108
+ },
109
+ 'parent context': async () => {
110
+ const parentContext1 = await spec.childContext(context, await spec.findElement(context, transformSelector('[name="frame1"]')));
111
+ const parentDocument1 = await spec.findElement(parentContext1, transformSelector('html'));
112
+ const frameContext = await spec.childContext(parentContext1, await spec.findElement(parentContext1, transformSelector('[name="frame1-1"]')));
113
+ const frameDocument = await spec.findElement(frameContext, transformSelector('html'));
114
+ strict_1.default.ok(!(await isEqualElements(frameContext, parentDocument1, frameDocument)));
115
+ const parentContext2 = await spec.parentContext(frameContext);
116
+ const parentDocument2 = await spec.findElement(parentContext2, transformSelector('html'));
117
+ strict_1.default.ok(await isEqualElements(parentContext2, parentDocument2, parentDocument1));
118
+ await spec.mainContext(context);
119
+ },
120
+ 'get title': async () => {
121
+ const title = await spec.getTitle(driver);
122
+ strict_1.default.equal(title, 'Cross SDK test', 'returns title of the current page');
123
+ },
124
+ 'get url': async () => {
125
+ const url = await spec.getUrl(driver);
126
+ strict_1.default.equal(url, 'https://applitools.github.io/demo/TestPages/FramesTestPage/', 'returns url of the current page');
127
+ },
128
+ 'is driver': async () => {
129
+ strict_1.default.ok(await spec.isDriver(driver), 'driver should be considered a driver :)');
130
+ strict_1.default.ok(!(await spec.isDriver(undefined)), 'undefined should not be considered a driver');
131
+ strict_1.default.ok(!(await spec.isDriver(3)), 'number should not be considered a driver');
132
+ strict_1.default.ok(!(await spec.isDriver('str')), 'string should not be considered a driver');
133
+ },
134
+ 'is element': async () => {
135
+ const el = await spec.findElement(context, transformSelector('html'));
136
+ strict_1.default.ok(await spec.isElement(el), 'element should be considered an element :)');
137
+ strict_1.default.ok(!(await spec.isElement(undefined)), 'undefined should not be considered an element');
138
+ strict_1.default.ok(!(await spec.isElement(3)), 'number should not be considered an element');
139
+ strict_1.default.ok(!(await spec.isElement('str')), 'str should not be considered an element');
140
+ },
141
+ // 'is selector': async () => {}, // hard to test this
142
+ // 'set window size': async () => {}, // hard to test this
143
+ // 'get window size': async () => {}, // hard to test this
144
+ };
145
+ const report = [];
146
+ await spec.visit(driver, 'https://applitools.github.io/demo/TestPages/FramesTestPage/');
147
+ for (const [test, check] of Object.entries(tests)) {
148
+ try {
149
+ const result = (await check()) || { success: true };
150
+ report.push(Object.assign({ test }, result));
151
+ }
152
+ catch (error) {
153
+ report.push({ test, error: { message: error.message, expected: error.expected, actual: error.actual } });
154
+ }
155
+ }
156
+ return report;
157
+ function isEqualElements(context, element1, element2) {
158
+ var _a, _b;
159
+ return ((_b = (_a = spec.isEqualElements) === null || _a === void 0 ? void 0 : _a.call(spec, context, element1, element2)) !== null && _b !== void 0 ? _b : spec.executeScript(context, snippets.isEqualElements, [element1, element2]).catch(() => false));
160
+ }
161
+ function extractContext(driver) {
162
+ var _a, _b;
163
+ return (_b = (_a = spec.extractContext) === null || _a === void 0 ? void 0 : _a.call(spec, driver)) !== null && _b !== void 0 ? _b : driver;
164
+ }
165
+ function transformSelector(selector) {
166
+ var _a, _b;
167
+ return (_b = (_a = spec.transformSelector) === null || _a === void 0 ? void 0 : _a.call(spec, selector)) !== null && _b !== void 0 ? _b : selector;
168
+ }
169
+ }
170
+ exports.checkSpecDriver = checkSpecDriver;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./check-spec-driver"), exports);
package/dist/driver.js CHANGED
@@ -22,6 +22,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.Driver = void 0;
23
23
  const utils = __importStar(require("@applitools/utils"));
24
24
  const context_1 = require("./context");
25
+ const helper_ios_1 = require("./helper-ios");
26
+ const helper_android_1 = require("./helper-android");
25
27
  const utils_1 = require("./utils");
26
28
  const user_agent_1 = require("./user-agent");
27
29
  const capabilities_1 = require("./capabilities");
@@ -59,6 +61,9 @@ class Driver {
59
61
  get mainContext() {
60
62
  return this._mainContext;
61
63
  }
64
+ get helper() {
65
+ return this._helper;
66
+ }
62
67
  get features() {
63
68
  var _a;
64
69
  return (_a = this._driverInfo) === null || _a === void 0 ? void 0 : _a.features;
@@ -140,7 +145,7 @@ class Driver {
140
145
  const userAgentInfo = user_agent_1.parseUserAgent(this._driverInfo.userAgent);
141
146
  this._driverInfo.browserName = (_g = userAgentInfo.browserName) !== null && _g !== void 0 ? _g : this._driverInfo.browserName;
142
147
  this._driverInfo.browserVersion = (_h = userAgentInfo.browserVersion) !== null && _h !== void 0 ? _h : this._driverInfo.browserVersion;
143
- if (!this._driverInfo.isMobile) {
148
+ if (this._driverInfo.isMobile) {
144
149
  (_j = (_u = this._driverInfo).platformName) !== null && _j !== void 0 ? _j : (_u.platformName = userAgentInfo.platformName);
145
150
  (_k = (_v = this._driverInfo).platformVersion) !== null && _k !== void 0 ? _k : (_v.platformVersion = userAgentInfo.platformVersion);
146
151
  }
@@ -153,24 +158,48 @@ class Driver {
153
158
  (_p = (_x = this._driverInfo.features).allCookies) !== null && _p !== void 0 ? _p : (_x.allCookies = /chrome/i.test(this._driverInfo.browserName) && !this._driverInfo.isMobile);
154
159
  }
155
160
  else {
156
- if (this.isNative) {
157
- const barsHeight = await ((_r = (_q = this._spec).getBarsHeight) === null || _r === void 0 ? void 0 : _r.call(_q, this.target).catch(() => undefined));
158
- if (barsHeight) {
159
- this._driverInfo.statusBarHeight = Math.max(barsHeight.statusBarHeight, driverInfo.statusBarHeight);
160
- this._driverInfo.navigationBarHeight = Math.max(barsHeight.navigationBarHeight, driverInfo.navigationBarHeight);
161
- }
162
- if (this.isAndroid) {
163
- this._driverInfo.statusBarHeight /= this.pixelRatio;
164
- this._driverInfo.navigationBarHeight /= this.pixelRatio;
161
+ const barsHeight = await ((_r = (_q = this._spec).getBarsHeight) === null || _r === void 0 ? void 0 : _r.call(_q, this.target).catch(() => undefined));
162
+ const displaySize = await this.getDisplaySize();
163
+ // calculate status and navigation bars sizes
164
+ if (barsHeight) {
165
+ // when status bar is overlapping content on android it returns status bar height equal to viewport height
166
+ if (this.isAndroid && barsHeight.statusBarHeight / this.pixelRatio < displaySize.height) {
167
+ this._driverInfo.statusBarHeight = Math.max(this._driverInfo.statusBarHeight, barsHeight.statusBarHeight);
165
168
  }
169
+ this._driverInfo.navigationBarHeight = Math.max(this._driverInfo.navigationBarHeight, barsHeight.navigationBarHeight);
170
+ }
171
+ if (this.isAndroid) {
172
+ this._driverInfo.statusBarHeight /= this.pixelRatio;
173
+ this._driverInfo.navigationBarHeight /= this.pixelRatio;
166
174
  }
175
+ // calculate viewport size
167
176
  if (!this._driverInfo.viewportSize) {
168
- const displaySize = await this.getDisplaySize();
169
177
  this._driverInfo.viewportSize = {
170
178
  width: displaySize.width,
171
179
  height: displaySize.height - this._driverInfo.statusBarHeight,
172
180
  };
173
181
  }
182
+ // calculate safe area
183
+ if (this.isIOS && !this._driverInfo.safeArea) {
184
+ this._driverInfo.safeArea = Object.assign({ x: 0, y: 0 }, displaySize);
185
+ const topElement = await this.element({ type: 'class name', selector: 'XCUIElementTypeNavigationBar' });
186
+ if (topElement) {
187
+ const topRegion = await this._spec.getElementRegion(this.target, topElement.target);
188
+ const topOffset = topRegion.y + topRegion.height;
189
+ this._driverInfo.safeArea.y = topOffset;
190
+ this._driverInfo.safeArea.height -= topOffset;
191
+ }
192
+ const bottomElement = await this.element({ type: 'class name', selector: 'XCUIElementTypeTabBar' });
193
+ if (bottomElement) {
194
+ const bottomRegion = await this._spec.getElementRegion(this.target, bottomElement.target);
195
+ const bottomOffset = bottomRegion.height;
196
+ this._driverInfo.safeArea.height -= bottomOffset;
197
+ }
198
+ }
199
+ // init helper lib
200
+ this._helper = this.isIOS
201
+ ? await helper_ios_1.HelperIOS.make({ spec: this._spec, driver: this, logger: this._logger })
202
+ : await helper_android_1.HelperAndroid.make({ spec: this._spec, driver: this, logger: this._logger });
174
203
  }
175
204
  this._logger.log('Combined driver info', this._driverInfo);
176
205
  return this;
@@ -339,7 +368,13 @@ class Driver {
339
368
  if (this.isWeb || !utils.types.has(this._driverInfo, ['viewportSize', 'statusBarHeight']))
340
369
  return region;
341
370
  const scaledRegion = this.isAndroid ? utils.geometry.scale(region, 1 / this.pixelRatio) : region;
342
- return utils.geometry.offsetNegative(scaledRegion, { x: 0, y: this.statusBarHeight });
371
+ const safeRegion = this.isIOS ? utils.geometry.intersect(scaledRegion, this._driverInfo.safeArea) : scaledRegion;
372
+ const offsetRegion = utils.geometry.offsetNegative(safeRegion, { x: 0, y: this.statusBarHeight });
373
+ if (offsetRegion.y < 0) {
374
+ offsetRegion.height += offsetRegion.y;
375
+ offsetRegion.y = 0;
376
+ }
377
+ return offsetRegion;
343
378
  }
344
379
  async getRegionInViewport(context, region) {
345
380
  await context.focus();
@@ -411,9 +446,12 @@ class Driver {
411
446
  };
412
447
  this._logger.log(`Attempt #${attempt} to set viewport size by setting window size to`, requiredWindowSize);
413
448
  await this._spec.setWindowSize(this.target, requiredWindowSize);
414
- await utils.general.sleep(3000);
415
- currentWindowSize = requiredWindowSize;
449
+ const prevViewportSize = currentViewportSize;
416
450
  currentViewportSize = await this.getViewportSize();
451
+ if (utils.geometry.equals(currentViewportSize, prevViewportSize)) {
452
+ currentViewportSize = await this.getViewportSize();
453
+ }
454
+ currentWindowSize = requiredWindowSize;
417
455
  if (utils.geometry.equals(currentViewportSize, requiredViewportSize))
418
456
  return;
419
457
  this._logger.log(`Attempt #${attempt} to set viewport size failed. Current viewport:`, currentViewportSize);
@@ -434,10 +472,10 @@ class Driver {
434
472
  return orientation;
435
473
  }
436
474
  async getCookies() {
437
- var _a, _b;
475
+ var _a, _b, _c;
438
476
  if (this.isNative || !this.features.allCookies)
439
477
  return [];
440
- return (_b = (_a = this._spec) === null || _a === void 0 ? void 0 : _a.getCookies(this.target)) !== null && _b !== void 0 ? _b : [];
478
+ return (_c = (_b = (_a = this._spec).getCookies) === null || _b === void 0 ? void 0 : _b.call(_a, this.target)) !== null && _c !== void 0 ? _c : [];
441
479
  }
442
480
  async getTitle() {
443
481
  if (this.isNative)
package/dist/element.js CHANGED
@@ -18,6 +18,17 @@ var __importStar = (this && this.__importStar) || function (mod) {
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
21
+ var __rest = (this && this.__rest) || function (s, e) {
22
+ var t = {};
23
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
24
+ t[p] = s[p];
25
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
26
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
27
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
28
+ t[p[i]] = s[p[i]];
29
+ }
30
+ return t;
31
+ };
21
32
  Object.defineProperty(exports, "__esModule", { value: true });
22
33
  exports.Element = void 0;
23
34
  const utils = __importStar(require("@applitools/utils"));
@@ -112,10 +123,7 @@ class Element {
112
123
  return this.context.execute(snippets.getElementRect, [this, true]);
113
124
  }
114
125
  else {
115
- this._logger.log('Extracting region of native element with selector', this.selector);
116
- const region = await this._spec.getElementRegion(this.driver.target, this.target);
117
- this._logger.log('Extracted native region', region);
118
- return this.driver.normalizeRegion(region);
126
+ return this.getRegion();
119
127
  }
120
128
  });
121
129
  this._logger.log('Extracted client region', region);
@@ -125,7 +133,7 @@ class Element {
125
133
  if (this._state.contentSize)
126
134
  return this._state.contentSize;
127
135
  const size = await this.withRefresh(async () => {
128
- var _a;
136
+ var _a, _b, _c;
129
137
  if (this.driver.isWeb) {
130
138
  this._logger.log('Extracting content size of web element with selector', this.selector);
131
139
  return this.context.execute(snippets.getElementContentSize, [this]);
@@ -133,81 +141,37 @@ class Element {
133
141
  else {
134
142
  this._logger.log('Extracting content size of native element with selector', this.selector);
135
143
  try {
144
+ const _d = await this.getAttribute('contentSize')
145
+ .then(data => {
146
+ const contentSize = JSON.parse(data);
147
+ return {
148
+ touchPadding: contentSize.touchPadding,
149
+ x: contentSize.left,
150
+ y: contentSize.top,
151
+ width: contentSize.width,
152
+ height: (this.driver.isAndroid ? contentSize.height : 0) + contentSize.scrollableOffset,
153
+ };
154
+ })
155
+ .catch(() => {
156
+ return this._spec.getElementRegion(this.driver.target, this.target);
157
+ }), { touchPadding } = _d, contentRegion = __rest(_d, ["touchPadding"]);
158
+ this._logger.log('Extracted native content size attribute', contentRegion);
159
+ const contentSize = await ((_a = this.driver.helper) === null || _a === void 0 ? void 0 : _a.getContentSize(this));
160
+ this._logger.log('Extracted native content size with helper library', contentSize);
161
+ this._state.contentSize = {
162
+ width: Math.max((_b = contentSize === null || contentSize === void 0 ? void 0 : contentSize.width) !== null && _b !== void 0 ? _b : 0, contentRegion.width),
163
+ height: Math.max((_c = contentSize === null || contentSize === void 0 ? void 0 : contentSize.height) !== null && _c !== void 0 ? _c : 0, contentRegion.height),
164
+ };
165
+ this._touchPadding = touchPadding !== null && touchPadding !== void 0 ? touchPadding : this._touchPadding;
136
166
  if (this.driver.isAndroid) {
137
- const className = await this.getAttribute('className');
138
- if ([
139
- 'android.widget.ListView',
140
- 'android.widget.GridView',
141
- 'android.support.v7.widget.RecyclerView',
142
- // 'androidx.recyclerview.widget.RecyclerView',
143
- 'androidx.viewpager2.widget.ViewPager2',
144
- ].includes(className)) {
145
- this._logger.log('Trying to extract content size using android helper library');
146
- const helperElement = await this.driver.element({
147
- type: '-android uiautomator',
148
- selector: 'new UiSelector().description("EyesAppiumHelper")',
149
- });
150
- if (helperElement) {
151
- const elementRegion = await this._spec.getElementRegion(this.driver.target, this.target);
152
- await helperElement.click();
153
- const info = await this._spec.getElementText(this.driver.target, helperElement.target);
154
- this._state.contentSize = utils.geometry.scale({ width: elementRegion.width, height: Number(info) }, 1 / this.driver.pixelRatio);
155
- }
156
- else {
157
- this._logger.log('Helper library for android was not detected');
158
- }
159
- }
160
- }
161
- else if (this.driver.isIOS) {
162
- const type = await this.getAttribute('type');
163
- if (type === 'XCUIElementTypeScrollView') {
164
- const elementRegion = await this._spec.getElementRegion(this.driver.target, this.target);
165
- const [childElement] = await this.driver.elements({
166
- type: 'xpath',
167
- selector: '//XCUIElementTypeScrollView[1]/*', // We cannot be sure that our element is the first one
168
- });
169
- const childElementRegion = await this._spec.getElementRegion(this.driver.target, childElement.target);
170
- this._state.contentSize = {
171
- width: elementRegion.width,
172
- height: childElementRegion.y + childElementRegion.height - elementRegion.y,
173
- };
174
- }
175
- else if (type === 'XCUIElementTypeCollectionView') {
176
- this._logger.log('Trying to extract content size using ios helper library');
177
- const helperElement = await this.driver.element({
178
- type: 'name',
179
- selector: 'applitools_grab_scrollable_data_button',
180
- });
181
- if (helperElement) {
182
- const helperElementRegion = await this._spec.getElementRegion(this.driver.target, helperElement.target);
183
- await this._spec.performAction(this.driver.target, [
184
- { action: 'tap', x: helperElementRegion.x, y: helperElementRegion.y },
185
- { action: 'wait', ms: 1000 },
186
- { action: 'release' },
187
- ]);
188
- const infoElement = await this.driver.element({ type: 'name', selector: 'applitools_content_size_label' });
189
- const info = await this._spec.getElementText(this.driver.target, infoElement.target);
190
- if (info) {
191
- const [_, width, height] = info.match(/\{(\d+),\s?(\d+)\}/);
192
- this._state.contentSize = { width: Number(width), height: Number(height) };
193
- }
194
- }
195
- else {
196
- this._logger.log('Helper library for ios was not detected');
197
- }
198
- }
167
+ this._state.contentSize = utils.geometry.scale(this._state.contentSize, 1 / this.driver.pixelRatio);
199
168
  }
200
- if (!this._state.contentSize) {
201
- const data = JSON.parse(await this.getAttribute('contentSize'));
202
- this._logger.log('Extracted native content size attribute', data);
203
- this._state.contentSize = this.driver.isIOS
204
- ? { width: data.width, height: data.scrollableOffset }
205
- : utils.geometry.scale({ width: data.width, height: data.height + data.scrollableOffset }, 1 / this.driver.pixelRatio);
206
- this._touchPadding = (_a = data.touchPadding) !== null && _a !== void 0 ? _a : this._touchPadding;
169
+ if (contentRegion.y < this.driver.statusBarHeight) {
170
+ this._state.contentSize.height -= this.driver.statusBarHeight - contentRegion.y;
207
171
  }
172
+ // android has a bug when after extracting 'contentSize' attribute the element is being scrolled by undetermined number of pixels
208
173
  if (this.driver.isAndroid) {
209
174
  this._logger.log('Stabilizing android scroll offset');
210
- // android has a bug when after extracting 'contentSize' attribute the element is being scrolled by undetermined number of pixels
211
175
  const originalScrollOffset = await this.getScrollOffset();
212
176
  this._state.scrollOffset = { x: -1, y: -1 };
213
177
  await this.scrollTo({ x: 0, y: 0 });
@@ -216,6 +180,7 @@ class Element {
216
180
  return this._state.contentSize;
217
181
  }
218
182
  catch (err) {
183
+ console.log(err);
219
184
  this._logger.warn('Failed to extract content size, extracting client size instead');
220
185
  this._logger.error(err);
221
186
  return utils.geometry.size(await this.getClientRegion());
@@ -260,7 +225,7 @@ class Element {
260
225
  if (this.driver.isWeb)
261
226
  this._touchPadding = 0;
262
227
  else if (this.driver.isIOS)
263
- this._touchPadding = 14;
228
+ this._touchPadding = 10;
264
229
  else if (this.driver.isAndroid) {
265
230
  const { touchPadding } = JSON.parse(await this.getAttribute('contentSize'));
266
231
  this._touchPadding = touchPadding !== null && touchPadding !== void 0 ? touchPadding : 0;
@@ -268,6 +233,19 @@ class Element {
268
233
  }
269
234
  return this._touchPadding;
270
235
  }
236
+ async getText() {
237
+ const text = await this.withRefresh(async () => {
238
+ if (this.driver.isWeb) {
239
+ return '';
240
+ }
241
+ else {
242
+ this._logger.log('Extracting text of native element with selector', this.selector);
243
+ return this._spec.getElementText(this.driver.target, this.target);
244
+ }
245
+ });
246
+ this._logger.log('Extracted element text', text);
247
+ return text;
248
+ }
271
249
  async getAttribute(name) {
272
250
  if (this.driver.isWeb) {
273
251
  const properties = await this.context.execute(snippets.getElementProperties, [this, [name]]);
@@ -284,7 +262,7 @@ class Element {
284
262
  }
285
263
  async scrollTo(offset) {
286
264
  return this.withRefresh(async () => {
287
- offset = { x: Math.round(offset.x), y: Math.round(offset.y) };
265
+ offset = utils.geometry.round(offset);
288
266
  if (this.driver.isWeb) {
289
267
  let actualOffset = await this.context.execute(snippets.scrollTo, [this, offset]);
290
268
  // iOS has an issue when scroll offset is read immediately after it is been set it will always return the exact value that was set
@@ -297,62 +275,69 @@ class Element {
297
275
  if (utils.geometry.equals(offset, currentScrollOffset))
298
276
  return currentScrollOffset;
299
277
  const contentSize = await this.getContentSize();
300
- const scrollableRegion = await this._spec.getElementRegion(this.driver.target, this.target);
301
- const scaledScrollableRegion = this.driver.isAndroid
302
- ? utils.geometry.scale(scrollableRegion, 1 / this.driver.pixelRatio)
278
+ const scrollableRegion = await this.getClientRegion();
279
+ const effectiveRegion = this.driver.isAndroid
280
+ ? utils.geometry.scale(scrollableRegion, this.driver.pixelRatio)
303
281
  : scrollableRegion;
304
282
  const maxOffset = {
305
- x: Math.round(scaledScrollableRegion.width * (contentSize.width / scaledScrollableRegion.width - 1)),
306
- y: Math.round(scaledScrollableRegion.height * (contentSize.height / scaledScrollableRegion.height - 1)),
283
+ x: Math.round(scrollableRegion.width * (contentSize.width / scrollableRegion.width - 1)),
284
+ y: Math.round(scrollableRegion.height * (contentSize.height / scrollableRegion.height - 1)),
307
285
  };
308
- let requiredOffset;
309
- let remainingOffset;
310
- if (offset.x === 0 && offset.y === 0) {
311
- requiredOffset = offset;
312
- // if it has to be scrolled to the very beginning, then scroll maximum amount of pixels and a bit extra to be sure
313
- remainingOffset = { x: -(maxOffset.x + 0), y: -(maxOffset.y + 0) };
314
- }
315
- else {
316
- requiredOffset = { x: Math.min(offset.x, maxOffset.x), y: Math.min(offset.y, maxOffset.y) };
317
- remainingOffset = utils.geometry.offsetNegative(requiredOffset, currentScrollOffset);
318
- // if it has to be scrolled to the very end, then do a bit of extra scrolling to be sure
319
- // if (requiredOffset.x === maxOffset.x) remainingOffset.x += 100
320
- // if (requiredOffset.y === maxOffset.y) remainingOffset.y += 100
321
- }
322
- // if (requiredOffset.x === 0) remainingOffset.x -= 100
323
- // if (requiredOffset.y === 0) remainingOffset.y -= 100
324
- // if (requiredOffset.x === maxOffset.x) remainingOffset.x += 100
325
- // if (requiredOffset.y === maxOffset.y) remainingOffset.y += 100
286
+ const requiredOffset = { x: Math.min(offset.x, maxOffset.x), y: Math.min(offset.y, maxOffset.y) };
287
+ let remainingOffset = offset.x === 0 && offset.y === 0
288
+ ? { x: -maxOffset.x, y: -maxOffset.y } // if it has to be scrolled to the very beginning, then scroll maximum amount of pixels
289
+ : utils.geometry.offsetNegative(requiredOffset, currentScrollOffset);
326
290
  if (this.driver.isAndroid) {
327
291
  remainingOffset = utils.geometry.scale(remainingOffset, this.driver.pixelRatio);
328
292
  }
329
- const actions = [];
330
- const xPadding = Math.floor(scrollableRegion.width * 0.1);
331
- const yTrack = Math.floor(scrollableRegion.y + scrollableRegion.height / 2); // center
332
- const xLeft = scrollableRegion.y + xPadding;
293
+ const touchPadding = await this.getTouchPadding();
294
+ const xPadding = Math.max(Math.floor(effectiveRegion.width * 0.1), touchPadding);
295
+ const yTrack = Math.floor(effectiveRegion.y + effectiveRegion.height / 2); // center
296
+ const xLeft = effectiveRegion.y + xPadding;
333
297
  const xDirection = remainingOffset.y > 0 ? 'right' : 'left';
298
+ const xGap = xDirection === 'right' ? -touchPadding : touchPadding;
334
299
  let xRemaining = Math.abs(remainingOffset.x);
335
300
  while (xRemaining > 0) {
336
- const xRight = scrollableRegion.x + Math.min(xRemaining + xPadding, scrollableRegion.width - xPadding);
301
+ const xRight = effectiveRegion.x + Math.min(xRemaining + xPadding, effectiveRegion.width - xPadding);
337
302
  const [xStart, xEnd] = xDirection === 'right' ? [xRight, xLeft] : [xLeft, xRight];
338
- actions.push({ action: 'press', x: xStart, y: yTrack }, { action: 'wait', ms: 1500 }, { action: 'moveTo', x: xEnd, y: yTrack }, { action: 'release' });
303
+ await this._spec.performAction(this.driver.target, [
304
+ { action: 'press', y: yTrack, x: xStart },
305
+ { action: 'wait', ms: 100 },
306
+ { action: 'moveTo', y: yTrack, x: xStart + xGap },
307
+ { action: 'wait', ms: 100 },
308
+ { action: 'moveTo', y: yTrack, x: xEnd + xGap },
309
+ { action: 'wait', ms: 100 },
310
+ { action: 'moveTo', y: yTrack + 1, x: xEnd + xGap },
311
+ { action: 'release' },
312
+ ]);
339
313
  xRemaining -= xRight - xLeft;
340
314
  }
341
- const yPadding = Math.floor(scrollableRegion.height * 0.1);
342
- const xTrack = Math.floor(scrollableRegion.x + 5); // a little bit off left border
343
- const yTop = scrollableRegion.y + yPadding;
315
+ const yPadding = Math.max(Math.floor(effectiveRegion.height * 0.1), touchPadding);
316
+ const xTrack = Math.floor(effectiveRegion.x + 5); // a little bit off left border
317
+ const yBottom = effectiveRegion.y + effectiveRegion.height - yPadding;
344
318
  const yDirection = remainingOffset.y > 0 ? 'down' : 'up';
345
- let yRemaining = Math.abs(remainingOffset.y) + (await this.getTouchPadding()) * 2;
319
+ const yGap = yDirection === 'down' ? -touchPadding : touchPadding;
320
+ let yRemaining = Math.abs(remainingOffset.y);
346
321
  while (yRemaining > 0) {
347
- const yBottom = scrollableRegion.y + Math.min(yRemaining + yPadding, scrollableRegion.height - yPadding);
322
+ const yTop = Math.max(yBottom - yRemaining, effectiveRegion.y + yPadding);
348
323
  const [yStart, yEnd] = yDirection === 'down' ? [yBottom, yTop] : [yTop, yBottom];
349
- actions.push({ action: 'press', x: xTrack, y: yStart }, { action: 'wait', ms: 1500 }, { action: 'moveTo', x: xTrack, y: yEnd }, { action: 'wait', ms: 1500 }, { action: 'release' });
324
+ await this._spec.performAction(this.driver.target, [
325
+ { action: 'press', x: xTrack, y: yStart },
326
+ { action: 'wait', ms: 100 },
327
+ { action: 'moveTo', x: xTrack, y: yStart + yGap },
328
+ { action: 'wait', ms: 100 },
329
+ { action: 'moveTo', x: xTrack, y: yEnd + yGap },
330
+ { action: 'wait', ms: 100 },
331
+ { action: 'moveTo', x: xTrack + 1, y: yEnd + yGap },
332
+ { action: 'release' },
333
+ ]);
350
334
  yRemaining -= yBottom - yTop;
351
335
  }
352
- if (actions.length > 0) {
353
- await this._spec.performAction(this.driver.target, actions);
354
- }
355
- this._state.scrollOffset = requiredOffset;
336
+ const actualScrollableRegion = await this.getClientRegion();
337
+ this._state.scrollOffset = utils.geometry.offsetNegative(requiredOffset, {
338
+ x: scrollableRegion.x - actualScrollableRegion.x,
339
+ y: scrollableRegion.y - actualScrollableRegion.y,
340
+ });
356
341
  return this._state.scrollOffset;
357
342
  }
358
343
  });
@@ -394,6 +379,9 @@ class Element {
394
379
  async click() {
395
380
  await this._spec.click(this.context.target, this.target);
396
381
  }
382
+ async type(value) {
383
+ await this._spec.type(this.context.target, this.target, value);
384
+ }
397
385
  async preserveState() {
398
386
  if (this.driver.isNative)
399
387
  return;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HelperAndroid = void 0;
4
+ class HelperAndroid {
5
+ constructor(options) {
6
+ this._spec = options.spec;
7
+ this._element = options.element;
8
+ this._legacy = options.legacy;
9
+ this._logger = options.logger;
10
+ }
11
+ static async make(options) {
12
+ const { spec, driver, logger } = options;
13
+ let legacy = false;
14
+ let element = await driver.element({
15
+ type: '-android uiautomator',
16
+ selector: 'new UiSelector().description("EyesAppiumHelperEDT")',
17
+ });
18
+ if (!element) {
19
+ legacy = true;
20
+ element = await driver.element({
21
+ type: '-android uiautomator',
22
+ selector: 'new UiSelector().description("EyesAppiumHelper")',
23
+ });
24
+ }
25
+ return element ? new HelperAndroid({ spec, element, legacy, logger }) : null;
26
+ }
27
+ async _getElementId(element) {
28
+ const resourceId = await element.getAttribute('resource-id');
29
+ if (!resourceId)
30
+ return null;
31
+ return resourceId.split('/')[1];
32
+ }
33
+ async getContentSize(element) {
34
+ let contentHeight;
35
+ if (this._legacy) {
36
+ await this._element.click();
37
+ contentHeight = await this._element.getText();
38
+ }
39
+ else {
40
+ const elementId = this._getElementId(element);
41
+ if (!elementId)
42
+ return null;
43
+ await this._element.type(`offset;${elementId};0;0;0`);
44
+ await this._element.click();
45
+ contentHeight = await this._element.getText();
46
+ await this._element.type('');
47
+ }
48
+ const region = await this._spec.getElementRegion(this._element.driver.target, element.target);
49
+ return { width: region.width, height: Number(contentHeight) };
50
+ }
51
+ async getRegion(element) {
52
+ if (this._legacy)
53
+ return null;
54
+ const elementId = await this._getElementId(element);
55
+ if (!elementId)
56
+ return null;
57
+ await this._element.type(`getRect;${elementId};0;0`);
58
+ await this._element.click();
59
+ const regionString = await this._element.getText();
60
+ await this._element.type('');
61
+ const [, x, y, height, width] = regionString.match(/\[(-?\d+(?:\.\d+)?);(-?\d+(?:\.\d+)?);(-?\d+(?:\.\d+)?);(-?\d+(?:\.\d+)?)\]/);
62
+ const region = { x: Number(x), y: Number(y), width: Number(width), height: Number(height) };
63
+ if (Number.isNaN(region.x + region.y + region.width + region.height))
64
+ return null;
65
+ return region;
66
+ }
67
+ }
68
+ exports.HelperAndroid = HelperAndroid;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HelperIOS = void 0;
4
+ class HelperIOS {
5
+ constructor(options) {
6
+ this._driver = options.driver;
7
+ this._element = options.element;
8
+ this._spec = options.spec;
9
+ this._logger = options.logger;
10
+ }
11
+ static async make(options) {
12
+ const { spec, driver, logger } = options;
13
+ const element = await driver.element({ type: 'name', selector: 'applitools_grab_scrollable_data_button' });
14
+ return element ? new HelperIOS({ driver, element, spec, logger }) : null;
15
+ }
16
+ async getContentSize(_element) {
17
+ await this._element.click();
18
+ const sizeLabel = await this._driver.element({ type: 'name', selector: 'applitools_content_size_label' });
19
+ const sizeString = await (sizeLabel === null || sizeLabel === void 0 ? void 0 : sizeLabel.getText());
20
+ if (!sizeString)
21
+ return null;
22
+ const [, width, height] = sizeString.match(/\{(-?\d+(?:\.\d+)?),\s?(-?\d+(?:\.\d+)?)\}/);
23
+ const contentSize = { width: Number(width), height: Number(height) };
24
+ if (Number.isNaN(contentSize.width + contentSize.height))
25
+ return null;
26
+ const paddingLabel = await this._driver.element({ type: 'name', selector: 'applitools_content_offset_label' });
27
+ const paddingString = await (paddingLabel === null || paddingLabel === void 0 ? void 0 : paddingLabel.getText());
28
+ if (!paddingString)
29
+ return contentSize;
30
+ const [, x, y] = paddingString.match(/\{(-?\d+(?:\.\d+)?),\s?(-?\d+(?:\.\d+)?)\}/);
31
+ const contentOffset = { x: Number(x), y: Number(y) };
32
+ if (!Number.isNaN(contentOffset.x))
33
+ contentSize.width -= contentOffset.x;
34
+ if (!Number.isNaN(contentOffset.y))
35
+ contentSize.height -= contentOffset.y;
36
+ return contentSize;
37
+ }
38
+ }
39
+ exports.HelperIOS = HelperIOS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/driver",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Applitools universal framework wrapper",
5
5
  "keywords": [
6
6
  "applitools",
@@ -34,6 +34,20 @@
34
34
  "./fake": {
35
35
  "default": "./dist/fake/index.js",
36
36
  "types": "./types/fake/index.d.ts"
37
+ },
38
+ "./debug": {
39
+ "default": "./dist/debug/index.js",
40
+ "types": "./types/debug/index.d.ts"
41
+ }
42
+ },
43
+ "typesVersions": {
44
+ "*": {
45
+ "fake": [
46
+ "./types/fake/index.d.ts"
47
+ ],
48
+ "debug": [
49
+ "./types/debug/index.d.ts"
50
+ ]
37
51
  }
38
52
  },
39
53
  "files": [
@@ -56,7 +70,7 @@
56
70
  },
57
71
  "dependencies": {
58
72
  "@applitools/snippets": "2.1.7",
59
- "@applitools/types": "1.0.20",
73
+ "@applitools/types": "1.0.22",
60
74
  "@applitools/utils": "1.2.4"
61
75
  },
62
76
  "devDependencies": {
@@ -0,0 +1,20 @@
1
+ import type { SpecDriver } from '@applitools/types';
2
+ export declare function checkSpecDriver<TDriver, TContext, TElement, TSelector>(options: {
3
+ spec: SpecDriver<TDriver, TContext, TElement, TSelector>;
4
+ driver: TDriver;
5
+ }): Promise<({
6
+ skipped: boolean;
7
+ test: string;
8
+ error?: undefined;
9
+ } | {
10
+ success: boolean;
11
+ test: string;
12
+ error?: undefined;
13
+ } | {
14
+ test: string;
15
+ error: {
16
+ message: any;
17
+ expected: any;
18
+ actual: any;
19
+ };
20
+ })[]>;
@@ -0,0 +1 @@
1
+ export * from './check-spec-driver';
package/types/driver.d.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  import type * as types from '@applitools/types';
3
3
  import { Context, ContextReference } from './context';
4
4
  import { Element } from './element';
5
+ import { HelperIOS } from './helper-ios';
6
+ import { HelperAndroid } from './helper-android';
5
7
  export declare class Driver<TDriver, TContext, TElement, TSelector> {
6
8
  private _target;
7
9
  private _mainContext;
@@ -9,6 +11,7 @@ export declare class Driver<TDriver, TContext, TElement, TSelector> {
9
11
  private _driverInfo;
10
12
  private _logger;
11
13
  private _utils;
14
+ private _helper?;
12
15
  protected readonly _spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
13
16
  constructor(options: {
14
17
  spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
@@ -18,6 +21,7 @@ export declare class Driver<TDriver, TContext, TElement, TSelector> {
18
21
  get target(): TDriver;
19
22
  get currentContext(): Context<TDriver, TContext, TElement, TSelector>;
20
23
  get mainContext(): Context<TDriver, TContext, TElement, TSelector>;
24
+ get helper(): HelperAndroid<TDriver, TContext, TElement, TSelector> | HelperIOS<TDriver, TContext, TElement, TSelector>;
21
25
  get features(): {
22
26
  shadowSelector?: boolean;
23
27
  allCookies?: boolean;
@@ -37,6 +37,7 @@ export declare class Element<TDriver, TContext, TElement, TSelector> {
37
37
  isScrollable(): Promise<boolean>;
38
38
  isRoot(): Promise<boolean>;
39
39
  getTouchPadding(): Promise<number>;
40
+ getText(): Promise<string>;
40
41
  getAttribute(name: string): Promise<string>;
41
42
  setAttribute(name: string, value: string): Promise<void>;
42
43
  scrollTo(offset: types.Location): Promise<types.Location>;
@@ -45,6 +46,7 @@ export declare class Element<TDriver, TContext, TElement, TSelector> {
45
46
  getTranslateOffset(): Promise<types.Location>;
46
47
  getInnerOffset(): Promise<types.Location>;
47
48
  click(): Promise<void>;
49
+ type(value: string): Promise<void>;
48
50
  preserveState(): Promise<ElementState>;
49
51
  restoreState(state?: ElementState): Promise<void>;
50
52
  hideScrollbars(): Promise<void>;
@@ -20,7 +20,7 @@ export declare class MockDriver {
20
20
  browserVersion: any;
21
21
  };
22
22
  executeScript(script: any, args?: any[]): Promise<any>;
23
- findElement(selector: any, rootElement: any): Promise<any>;
23
+ findElement(selector: any, rootElement?: any): Promise<any>;
24
24
  findElements(selector: any, rootElement: any): Promise<any>;
25
25
  switchToFrame(reference: any): Promise<this>;
26
26
  switchToParentFrame(): Promise<this>;
@@ -0,0 +1,23 @@
1
+ import type * as types from '@applitools/types';
2
+ import type { Driver } from './driver';
3
+ import type { Element } from './element';
4
+ export declare class HelperAndroid<TDriver, TContext, TElement, TSelector> {
5
+ static make<TDriver, TContext, TElement, TSelector>(options: {
6
+ spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
7
+ driver: Driver<TDriver, TContext, TElement, TSelector>;
8
+ logger: any;
9
+ }): Promise<HelperAndroid<TDriver, TContext, TElement, TSelector> | null>;
10
+ private readonly _spec;
11
+ private readonly _element;
12
+ private readonly _legacy;
13
+ private _logger;
14
+ constructor(options: {
15
+ spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
16
+ element: Element<TDriver, TContext, TElement, TSelector>;
17
+ legacy: boolean;
18
+ logger?: any;
19
+ });
20
+ _getElementId(element: Element<TDriver, TContext, TElement, TSelector>): Promise<string>;
21
+ getContentSize(element: Element<TDriver, TContext, TElement, TSelector>): Promise<types.Size>;
22
+ getRegion(element: Element<TDriver, TContext, TElement, TSelector>): Promise<types.Region>;
23
+ }
@@ -0,0 +1,21 @@
1
+ import type * as types from '@applitools/types';
2
+ import type { Driver } from './driver';
3
+ import type { Element } from './element';
4
+ export declare class HelperIOS<TDriver, TContext, TElement, TSelector> {
5
+ static make<TDriver, TContext, TElement, TSelector>(options: {
6
+ spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
7
+ driver: Driver<TDriver, TContext, TElement, TSelector>;
8
+ logger: any;
9
+ }): Promise<HelperIOS<TDriver, TContext, TElement, TSelector> | null>;
10
+ private readonly _driver;
11
+ private readonly _element;
12
+ private readonly _spec;
13
+ private _logger;
14
+ constructor(options: {
15
+ driver: Driver<TDriver, TContext, TElement, TSelector>;
16
+ element: Element<TDriver, TContext, TElement, TSelector>;
17
+ spec: types.SpecDriver<TDriver, TContext, TElement, TSelector>;
18
+ logger?: any;
19
+ });
20
+ getContentSize(_element: Element<TDriver, TContext, TElement, TSelector>): Promise<types.Size>;
21
+ }