@appium/images-plugin 2.0.10 → 2.1.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.
@@ -2,14 +2,12 @@ import _ from 'lodash';
2
2
  import {errors} from 'appium/driver';
3
3
  import {util} from 'appium/support';
4
4
  import log from './logger';
5
- import {DEFAULT_SETTINGS} from './finder';
5
+ import {
6
+ IMAGE_STRATEGY, DEFAULT_SETTINGS, IMAGE_TAP_STRATEGIES,
7
+ IMAGE_ELEMENT_PREFIX, IMAGE_EL_TAP_STRATEGY_W3C,
8
+ } from './constants';
6
9
 
7
- const IMAGE_ELEMENT_PREFIX = 'appium-image-element-';
8
10
  const TAP_DURATION_MS = 125;
9
- const IMAGE_EL_TAP_STRATEGY_W3C = 'w3cActions';
10
- const IMAGE_EL_TAP_STRATEGY_MJSONWP = 'touchActions';
11
- const IMAGE_TAP_STRATEGIES = [IMAGE_EL_TAP_STRATEGY_MJSONWP, IMAGE_EL_TAP_STRATEGY_W3C];
12
- const DEFAULT_TEMPLATE_IMAGE_SCALE = 1.0;
13
11
 
14
12
  /**
15
13
  * @typedef Dimension
@@ -23,28 +21,41 @@ const DEFAULT_TEMPLATE_IMAGE_SCALE = 1.0;
23
21
  * @property {number} y - y coordinate
24
22
  */
25
23
 
24
+ /**
25
+ * @typedef ImageElementOpts
26
+ * @property {Buffer} template - the image which was used to find this ImageElement
27
+ * @property {Rect} rect - bounds of matched image element
28
+ * @property {number} score The similarity score as a float number in range [0.0, 1.0].
29
+ * 1.0 is the highest score (means both images are totally equal).
30
+ * @property {Buffer?} match - the image which has matched marks. Defaults to null.
31
+ * @property {import('./finder').default?} finder - the finder we can use to re-check stale elements
32
+ * @property {import('@appium/types').Rect?} containerRect - The bounding
33
+ * rectangle to limit the search in
34
+ */
35
+
26
36
  /**
27
37
  * Representation of an "image element", which is simply a set of coordinates
28
38
  * and methods that can be used on that set of coordinates via the driver
29
39
  */
30
40
  export default class ImageElement {
31
41
  /**
32
- * @param {string} b64Template - the base64-encoded image which was used to
33
- * find this ImageElement
34
- * @param {Rect} rect - bounds of matched image element
35
- * @param {number} score The similarity score as a float number in range [0.0, 1.0].
36
- * 1.0 is the highest score (means both images are totally equal).
37
- * @param {string?} b64Result - the base64-encoded image which has matched marks.
38
- * Defaults to null.
39
- * @param {import('./finder').default?} finder - the finder we can use to re-check stale elements
42
+ * @param {ImageElementOpts}
40
43
  */
41
- constructor(b64Template, rect, score, b64Result = null, finder = null) {
42
- this.template = b64Template;
44
+ constructor({
45
+ template,
46
+ rect,
47
+ score,
48
+ match = null,
49
+ finder = null,
50
+ containerRect = null,
51
+ }) {
52
+ this.template = template;
43
53
  this.rect = rect;
44
54
  this.id = `${IMAGE_ELEMENT_PREFIX}${util.uuidV4()}`;
45
- this.b64MatchedImage = b64Result;
55
+ this.match = match;
46
56
  this.score = score;
47
57
  this.finder = finder;
58
+ this.containerRect = containerRect;
48
59
  }
49
60
 
50
61
  /**
@@ -72,20 +83,25 @@ export default class ImageElement {
72
83
  }
73
84
 
74
85
  /**
75
- * @returns {?string} - the base64-encoded image which has matched marks
86
+ * @returns {string} - the base64-encoded original image used for matching
87
+ */
88
+ get originalImage() {
89
+ return this.template.toString('base64');
90
+ }
91
+
92
+ /**
93
+ * @returns {string|null} - the base64-encoded image which has matched marks
76
94
  */
77
95
  get matchedImage() {
78
- return this.b64MatchedImage;
96
+ return this.match?.toString('base64') ?? null;
79
97
  }
80
98
 
81
99
  /**
82
- * @param {string} protocolKey - the protocol-specific JSON key for
83
- * a WebElement
84
100
  *
85
101
  * @returns {Element} - this image element as a WebElement
86
102
  */
87
- asElement(protocolKey) {
88
- return {[protocolKey]: this.id};
103
+ asElement() {
104
+ return util.wrapElement(this.id);
89
105
  }
90
106
 
91
107
  /**
@@ -132,11 +148,12 @@ export default class ImageElement {
132
148
  if (checkForImageElementStaleness || updatePos) {
133
149
  log.info('Checking image element for staleness before clicking');
134
150
  try {
135
- newImgEl = await this.finder.findByImage(this.template, {
151
+ newImgEl = await this.finder.findByImage(this.template, driver, {
136
152
  shouldCheckStaleness: true,
137
153
  // Set ignoreDefaultImageTemplateScale because this.template is device screenshot based image
138
154
  // managed inside Appium after finidng image by template which managed by a user
139
155
  ignoreDefaultImageTemplateScale: true,
156
+ containerRect: this.containerRect,
140
157
  });
141
158
  } catch (err) {
142
159
  throw new errors.StaleElementReferenceError();
@@ -209,6 +226,22 @@ export default class ImageElement {
209
226
  );
210
227
  }
211
228
 
229
+ /**
230
+ * Perform lookup of image element(s) inside of the current element
231
+ *
232
+ * @param {boolean} multiple - Whether to lookup multiple elements
233
+ * @param {import('appium/driver').BaseDriver} driver - The driver to use for commands
234
+ * @param {string[]} args = Rest of arguments for executeScripts
235
+ * @returns {Promise<Element|Element[]|ImageElement>} - WebDriver element with a special id prefix
236
+ */
237
+ async find(multiple, driver, ...args) {
238
+ const [strategy, selector] = args;
239
+ if (strategy !== IMAGE_STRATEGY) {
240
+ throw new errors.InvalidSelectorError(`Lookup strategies other than '${IMAGE_STRATEGY}' are not supported`);
241
+ }
242
+ return await this.finder.findByImage(selector, driver, {multiple, containerRect: this.rect});
243
+ }
244
+
212
245
  /**
213
246
  * Handle various Appium commands that involve an image element
214
247
  *
@@ -223,6 +256,10 @@ export default class ImageElement {
223
256
  switch (cmd) {
224
257
  case 'click':
225
258
  return await imgEl.click(driver);
259
+ case 'findElementFromElement':
260
+ return await imgEl.find(false, driver, ...args);
261
+ case 'findElementsFromElement':
262
+ return await imgEl.find(true, driver, ...args);
226
263
  case 'elementDisplayed':
227
264
  return true;
228
265
  case 'getSize':
@@ -232,6 +269,8 @@ export default class ImageElement {
232
269
  return imgEl.location;
233
270
  case 'getElementRect':
234
271
  return imgEl.rect;
272
+ case 'getElementScreenshot':
273
+ return imgEl.originalImage;
235
274
  case 'getAttribute':
236
275
  // /session/:sessionId/element/:elementId/attribute/:name
237
276
  // /session/:sessionId/element/:elementId/attribute/visual should retun the visual data
@@ -250,13 +289,7 @@ export default class ImageElement {
250
289
  }
251
290
  }
252
291
 
253
- export {
254
- ImageElement,
255
- IMAGE_EL_TAP_STRATEGY_MJSONWP,
256
- IMAGE_EL_TAP_STRATEGY_W3C,
257
- DEFAULT_TEMPLATE_IMAGE_SCALE,
258
- IMAGE_ELEMENT_PREFIX,
259
- };
292
+ export {ImageElement};
260
293
 
261
294
  /**
262
295
  * @typedef {import('@appium/types').Rect} Rect
package/lib/plugin.js CHANGED
@@ -5,16 +5,11 @@ import {errors} from 'appium/driver';
5
5
  import BasePlugin from 'appium/plugin';
6
6
  import {compareImages} from './compare';
7
7
  import ImageElementFinder from './finder';
8
- import {ImageElement, IMAGE_ELEMENT_PREFIX} from './image-element';
9
-
10
- const IMAGE_STRATEGY = '-image';
8
+ import {ImageElement} from './image-element';
9
+ import {IMAGE_STRATEGY, IMAGE_ELEMENT_PREFIX} from './constants';
11
10
 
12
11
  function getImgElFromArgs(args) {
13
- for (let arg of args) {
14
- if (_.isString(arg) && arg.startsWith(IMAGE_ELEMENT_PREFIX)) {
15
- return arg;
16
- }
17
- }
12
+ return args.find((arg) => _.isString(arg) && arg.startsWith(IMAGE_ELEMENT_PREFIX));
18
13
  }
19
14
 
20
15
  export default class ImageElementPlugin extends BasePlugin {
@@ -57,8 +52,7 @@ export default class ImageElementPlugin extends BasePlugin {
57
52
  return await next();
58
53
  }
59
54
 
60
- this.finder.setDriver(driver);
61
- return await this.finder.findByImage(selector, {multiple});
55
+ return await this.finder.findByImage(selector, driver, {multiple});
62
56
  }
63
57
 
64
58
  async handle(next, driver, cmdName, ...args) {
@@ -66,13 +60,17 @@ export default class ImageElementPlugin extends BasePlugin {
66
60
  // and execute the command on it
67
61
  const imgElId = getImgElFromArgs(args);
68
62
  if (imgElId) {
69
- if (!this.finder.imgElCache.has(imgElId)) {
63
+ const imgEl = this.finder.getImageElement(imgElId);
64
+ if (!imgEl) {
70
65
  throw new errors.NoSuchElementError();
71
66
  }
72
- const imgEl = this.finder.imgElCache.get(imgElId);
73
67
  return await ImageElement.execute(driver, imgEl, cmdName, ...args);
74
68
  }
75
69
 
70
+ if (cmdName === 'deleteSession') {
71
+ this.finder.clearImageElements();
72
+ }
73
+
76
74
  // otherwise just do the normal thing
77
75
  return await next();
78
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appium/images-plugin",
3
- "version": "2.0.10",
3
+ "version": "2.1.0",
4
4
  "description": "Plugin for working with images and image elements in Appium",
5
5
  "keywords": [
6
6
  "automation",
@@ -39,9 +39,10 @@
39
39
  "test:unit": "mocha \"./test/unit/**/*.spec.js\""
40
40
  },
41
41
  "dependencies": {
42
- "@appium/opencv": "^2.0.10",
42
+ "@appium/opencv": "^2.1.0",
43
43
  "lodash": "4.17.21",
44
44
  "lru-cache": "7.18.3",
45
+ "sharp": "0.32.1",
45
46
  "source-map-support": "0.5.21"
46
47
  },
47
48
  "peerDependencies": {
@@ -58,7 +59,7 @@
58
59
  "publishConfig": {
59
60
  "access": "public"
60
61
  },
61
- "gitHead": "591d15bc5e65383f824fc8eaa5f2814266a34df4",
62
+ "gitHead": "6b245534c213f3b8d6405515aee1e89133295098",
62
63
  "typedoc": {
63
64
  "entryPoint": "./lib/plugin.js"
64
65
  }