feat(images-plugin): use peer deps
MODIFY
packages/images-plugin/lib/compare.js
packages/images-plugin/lib/compare.js
import _ from 'lodash';
-import {errors} from '@appium/base-driver';
+import {errors} from 'appium/driver';
import {getImagesMatches, getImagesSimilarity, getImageOccurrence} from '@appium/opencv';
const MATCH_FEATURES_MODE = 'matchFeatures';
@@ -19,10 +19,10 @@ const DEFAULT_MATCH_THRESHOLD = 0.4;
* All image formats, that OpenCV library itself accepts, are supported.
* @param {string} secondImage - Base64-encoded image file.
* All image formats, that OpenCV library itself accepts, are supported.
- * @param {?Object} options [{}] - The content of this dictionary depends
+ * @param {object} [options] - The content of this dictionary depends
* on the actual `mode` value. See the documentation on `@appium/support`
* module for more details.
- * @returns {Object} The content of the resulting dictionary depends
+ * @returns {Promise<object|object[]>} The content of the resulting dictionary depends
* on the actual `mode` and `options` values. See the documentation on
* `@appium/support` module for more details.
* @throws {Error} If required OpenCV modules are not installed or

MODIFY
packages/images-plugin/lib/finder.js
packages/images-plugin/lib/finder.js
import _ from 'lodash';
import LRU from 'lru-cache';
-import {imageUtil, util} from '@appium/support';
-import {errors} from '@appium/base-driver';
+import {errors} from 'appium/driver';
+import {util, imageUtil} from 'appium/support';
import {
ImageElement,
DEFAULT_TEMPLATE_IMAGE_SCALE,
@@ -73,6 +73,17 @@ const DEFAULT_SETTINGS = {
};
export default class ImageElementFinder {
+ /** @type {ExternalDriver} */
+ driver;
+
+ /** @type {LRU<string,ImageElement>} */
+ imgElCache;
+
+ /**
+ *
+ * @param {ExternalDriver} driver
+ * @param {number} [maxSize]
+ */
constructor(driver, maxSize = MAX_CACHE_SIZE_BYTES) {
this.driver = driver;
this.imgElCache = new LRU({
@@ -86,6 +97,10 @@ export default class ImageElementFinder {
this.driver = driver;
}
+ /**
+ * @param {ImageElement} imgEl
+ * @returns {Element}
+ */
registerImageElement(imgEl) {
this.imgElCache.set(imgEl.id, imgEl);
const protoKey = this.driver.isW3CProtocol() ? W3C_ELEMENT_KEY : MJSONWP_ELEMENT_KEY;
@@ -109,9 +124,9 @@ export default class ImageElementFinder {
*
* @param {string} b64Template - base64-encoded image used as a template to be
* matched in the screenshot
- * @param {FindByImageOptions} - additional options
+ * @param {FindByImageOptions} opts - additional options
*
- * @returns {WebElement} - WebDriver element with a special id prefix
+ * @returns {Promise<Element|Element[]|ImageElement>} - WebDriver element with a special id prefix
*/
async findByImage(
b64Template,
@@ -236,10 +251,10 @@ export default class ImageElementFinder {
* Ensure that the image template sent in for a find is of a suitable size
*
* @param {string} b64Template - base64-encoded image
- * @param {int} screenWidth - width of screen
- * @param {int} screenHeight - height of screen
+ * @param {number} screenWidth - width of screen
+ * @param {number} screenHeight - height of screen
*
- * @returns {string} base64-encoded image, potentially resized
+ * @returns {Promise<string>} base64-encoded image, potentially resized
*/
async ensureTemplateSize(b64Template, screenWidth, screenHeight) {
let imgObj = await imageUtil.getJimpImage(b64Template);
@@ -262,23 +277,14 @@ export default class ImageElementFinder {
return (await imgObj.getBuffer(imageUtil.MIME_PNG)).toString('base64');
}
- /**
- * @typedef Screenshot
- * @property {string} b64Screenshot - base64 based screenshot string
- */
- /**
- * @typedef ScreenshotScale
- * @property {float} xScale - Scale ratio for width
- * @property {float} yScale - Scale ratio for height
- */
/**
* Get the screenshot image that will be used for find by element, potentially
* altering it in various ways based on user-requested settings
*
- * @param {int} screenWidth - width of screen
- * @param {int} screenHeight - height of screen
+ * @param {number} screenWidth - width of screen
+ * @param {number} screenHeight - height of screen
*
- * @returns { {b64Screenshot: Screenshot, scale: ScreenshotScale? } } base64-encoded screenshot and ScreenshotScale
+ * @returns {Promise<Screenshot & {scale?: ScreenshotScale}>} base64-encoded screenshot and ScreenshotScale
*/
async getScreenshotForImageFind(screenWidth, screenHeight) {
if (!this.driver.getScreenshot) {
@@ -400,12 +406,12 @@ export default class ImageElementFinder {
/**
* @typedef ImageTemplateSettings
* @property {boolean} fixImageTemplateScale - fixImageTemplateScale in device-settings
- * @property {float} defaultImageTemplateScale - defaultImageTemplateScale in device-settings
+ * @property {number} defaultImageTemplateScale - defaultImageTemplateScale in device-settings
* @property {boolean} ignoreDefaultImageTemplateScale - Ignore defaultImageTemplateScale if it has true.
* If b64Template has been scaled to defaultImageTemplateScale or should ignore the scale,
* this parameter should be true. e.g. click in image-element module
- * @property {float} xScale - Scale ratio for width
- * @property {float} yScale - Scale ratio for height
+ * @property {number} xScale - Scale ratio for width
+ * @property {number} yScale - Scale ratio for height
*/
/**
@@ -417,9 +423,9 @@ export default class ImageElementFinder {
* matched in the screenshot
* @param {ImageTemplateSettings} opts - Image template scale related options
*
- * @returns {string} base64-encoded scaled template screenshot
+ * @returns {Promise<string>} base64-encoded scaled template screenshot
*/
- async fixImageTemplateScale(b64Template, opts = {}) {
+ async fixImageTemplateScale(b64Template, opts) {
if (!opts) {
return b64Template;
}
@@ -450,7 +456,7 @@ export default class ImageElementFinder {
}
// xScale and yScale can be NaN if defaultImageTemplateScale is string, for example
- if (!parseFloat(xScale) || !parseFloat(yScale)) {
+ if (!parseFloat(String(xScale)) || !parseFloat(String(yScale))) {
return b64Template;
}
@@ -459,7 +465,10 @@ export default class ImageElementFinder {
Math.round(xScale * FLOAT_PRECISION) ===
Math.round(DEFAULT_FIX_IMAGE_TEMPLATE_SCALE * FLOAT_PRECISION) &&
Math.round(
- yScale * FLOAT_PRECISION === Math.round(DEFAULT_FIX_IMAGE_TEMPLATE_SCALE * FLOAT_PRECISION)
+ Number(
+ yScale * FLOAT_PRECISION ===
+ Math.round(DEFAULT_FIX_IMAGE_TEMPLATE_SCALE * FLOAT_PRECISION)
+ )
)
) {
return b64Template;
@@ -481,3 +490,19 @@ export default class ImageElementFinder {
}
export {W3C_ELEMENT_KEY, MJSONWP_ELEMENT_KEY, DEFAULT_SETTINGS, DEFAULT_FIX_IMAGE_TEMPLATE_SCALE};
+
+/**
+ * @typedef {import('@appium/types').ExternalDriver} ExternalDriver
+ * @typedef {import('@appium/types').Element} Element
+ */
+
+/**
+ * @typedef Screenshot
+ * @property {string} b64Screenshot - base64 based screenshot string
+ */
+
+/**
+ * @typedef ScreenshotScale
+ * @property {number} xScale - Scale ratio for width
+ * @property {number} yScale - Scale ratio for height
+ */

MODIFY
packages/images-plugin/lib/image-element.js
packages/images-plugin/lib/image-element.js
import _ from 'lodash';
-import {errors} from '@appium/base-driver';
+import {errors} from 'appium/driver';
+import {util} from 'appium/support';
import log from './logger';
-import {util} from '@appium/support';
import {DEFAULT_SETTINGS} from './finder';
const IMAGE_ELEMENT_PREFIX = 'appium-image-element-';
@@ -82,7 +82,7 @@ export default class ImageElement {
* @param {string} protocolKey - the protocol-specific JSON key for
* a WebElement
*
- * @returns {WebElement} - this image element as a WebElement
+ * @returns {Element} - this image element as a WebElement
*/
asElement(protocolKey) {
return {[protocolKey]: this.id};
@@ -212,7 +212,7 @@ export default class ImageElement {
/**
* Handle various Appium commands that involve an image element
*
- * @param {import('@appium/base-driver').BaseDriver} driver - the driver to use for commands
+ * @param {import('appium/driver').BaseDriver} driver - the driver to use for commands
* @param {string} cmd - the name of the driver command
* @param {string} imgElId - the id of the ImageElement to work with
* @param {string[]} args - Rest of arguments for executeScripts
@@ -260,4 +260,5 @@ export {
/**
* @typedef {import('@appium/types').Rect} Rect
+ * @typedef {import('@appium/types').Element} Element
*/

MODIFY
packages/images-plugin/lib/logger.js
packages/images-plugin/lib/logger.js
-import {logger} from '@appium/support';
+import {logger} from 'appium/support';
+
const log = logger.getLogger('ImageElementPlugin');
+
export default log;

MODIFY
packages/images-plugin/lib/plugin.js
packages/images-plugin/lib/plugin.js
/* eslint-disable no-case-declarations */
import _ from 'lodash';
-import {errors} from '@appium/base-driver';
-import BasePlugin from '@appium/base-plugin';
+import {errors} from 'appium/driver';
+import BasePlugin from 'appium/plugin';
import {compareImages} from './compare';
import ImageElementFinder from './finder';
import {ImageElement, IMAGE_ELEMENT_PREFIX} from './image-element';

MODIFY
packages/images-plugin/package.json
packages/images-plugin/package.json
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
- "@appium/base-driver": "file:../base-driver",
- "@appium/base-plugin": "file:../base-plugin",
"@appium/opencv": "file:../opencv",
- "@appium/support": "file:../support",
"lru-cache": "7.10.1"
},
+ "peerDependencies": {
+ "appium": "^2.0.0-beta.35"
+ },
"appium": {
"pluginName": "images",
"mainClass": "ImageElementPlugin"

MODIFY
packages/images-plugin/test/unit/finder.spec.js
packages/images-plugin/test/unit/finder.spec.js
import _ from 'lodash';
-import {imageUtil} from '@appium/support';
-import BaseDriver from '@appium/base-driver';
+import {imageUtil} from 'appium/support';
+import {BaseDriver} from 'appium/driver';
import {ImageElementPlugin, IMAGE_STRATEGY} from '../../lib/plugin';
import ImageElementFinder from '../../lib/finder';
import ImageElement from '../../lib/image-element';
@@ -58,9 +58,12 @@ describe('finding elements by image', function () {
const size = {width: 100, height: 200};
const screenshot = 'iVBORfoo';
const template = 'iVBORbar';
+ /** @type {PluginDriver} */
+ let d;
+ /** @type {ImageElementFinder} */
+ let f;
+ /** @type {import('sinon').SinonStubbedMember<import('../../lib/compare').compareImages>} */
let compareStub;
- let d = new PluginDriver();
- let f = new ImageElementFinder(d);
function basicStub(driver, finder) {
const sizeStub = sandbox.stub(driver, 'getWindowSize').returns(size);
@@ -81,7 +84,9 @@ describe('finding elements by image', function () {
beforeEach(function () {
d = new PluginDriver();
f = new ImageElementFinder(d);
- compareStub = sandbox.stub(compareModule, 'compareImages').resolves({rect, score});
+ compareStub = sandbox;
+ compareStub = sandbox.stub(compareModule, 'compareImages');
+ compareStub.resolves({rect, score});
basicStub(d, f);
});
@@ -90,8 +95,7 @@ describe('finding elements by image', function () {
basicImgElVerify(imgElProto, f);
});
it('should find image elements happypath', async function () {
- compareStub.restore();
- compareStub = sandbox.stub(compareModule, 'compareImages').resolves([{rect, score}]);
+ compareStub.resolves([{rect, score}]);
const els = await f.findByImage(template, {multiple: true});
els.should.have.length(1);
basicImgElVerify(els[0], f);
@@ -129,23 +133,23 @@ describe('finding elements by image', function () {
});
it('should throw an error if template match fails', async function () {
- compareStub.throws(new Error('Cannot find any occurrences'));
+ compareStub.rejects(new Error('Cannot find any occurrences'));
await f
.findByImage(template, {multiple: false})
- .should.eventually.be.rejectedWith(/element could not be located/);
+ .should.be.rejectedWith(/element could not be located/);
});
it('should return empty array for multiple elements if template match fails', async function () {
- compareStub.throws(new Error('Cannot find any occurrences'));
+ compareStub.rejects(new Error('Cannot find any occurrences'));
await f.findByImage(template, {multiple: true}).should.eventually.eql([]);
});
it('should respect implicit wait', async function () {
d.setImplicitWait(10);
compareStub.resetHistory();
- compareStub.onCall(0).throws(new Error('Cannot find any occurrences'));
compareStub.returns({rect, score});
+ compareStub.onFirstCall().throws(new Error('Cannot find any occurrences'));
const imgElProto = await f.findByImage(template, {multiple: false});
basicImgElVerify(imgElProto, f);
- compareStub.callCount.should.eql(2);
+ compareStub.should.have.been.calledTwice;
});
it('should not add element to cache and return it directly when checking staleness', async function () {
const imgEl = await f.findByImage(template, {
@@ -159,10 +163,15 @@ describe('finding elements by image', function () {
});
describe('fixImageTemplateScale', function () {
- const d = new PluginDriver();
- const f = new ImageElementFinder(d);
+ let d;
+ let f;
const basicTemplate = 'iVBORbaz';
+ beforeEach(function () {
+ d = new PluginDriver();
+ f = new ImageElementFinder(d);
+ });
+
it('should not fix template size scale if no scale value', async function () {
await f
.fixImageTemplateScale(basicTemplate, {fixImageTemplateScale: true})

MODIFY
packages/images-plugin/test/unit/image-element.spec.js
packages/images-plugin/test/unit/image-element.spec.js
import _ from 'lodash';
-import BaseDriver from '@appium/base-driver';
+import BaseDriver from 'appium/driver';
import ImageElementFinder from '../../lib/finder';
import {getImgElFromArgs} from '../../lib/plugin';
import ImageElement, {IMAGE_ELEMENT_PREFIX} from '../../lib/image-element';

MODIFY
packages/images-plugin/test/unit/plugin.spec.js
packages/images-plugin/test/unit/plugin.spec.js
import {ImageElementPlugin, IMAGE_STRATEGY} from '../../lib/plugin';
import {MATCH_FEATURES_MODE, GET_SIMILARITY_MODE, MATCH_TEMPLATE_MODE} from '../../lib/compare';
-import BaseDriver from '@appium/base-driver';
+import BaseDriver from 'appium/driver';
import {W3C_ELEMENT_KEY} from '../../lib/finder';
import {TEST_IMG_1_B64, TEST_IMG_2_B64, TEST_IMG_2_PART_B64} from '../fixtures';

MODIFY
packages/images-plugin/tsconfig.json
packages/images-plugin/tsconfig.json
{
"compilerOptions": {
- "outDir": "./build",
+ "outDir": "build",
"paths": {
- "@appium/base-plugin": ["../base-plugin"],
- "@appium/support": ["../support"],
- "@appium/base-driver": ["../base-driver"],
"@appium/opencv": ["../opencv"],
"appium": ["../appium"]
}
"extends": "../../config/tsconfig.base.json",
"include": ["lib"],
"references": [
- {
- "path": "../base-plugin"
- },
- {
- "path": "../support"
- },
- {
- "path": "../base-driver"
- },
{
"path": "../opencv"
},

feat(images-plugin): use peer deps