Compare commits

...
Author SHA1 Message Date
Kazuaki Matsuo 3d09b0f1e9 7.8.0 2021-06-29 10:05:00 -07:00
Kazuaki Matsuo 2cc3baf735 feat: add releaseActions as w3c actions (from 2.0 branch) (#501) 2021-06-28 08:12:57 +02:00
Christopher Hiller b35d22a7d8 Merge pull request #500 from boneskull/boneskull/redirect
chore(docs): point users to appium/appium for 2.x support
2021-06-25 09:41:10 -07:00
Christopher Hiller 9b874c7c06 chore(docs): point users to appium/appium for 2.x support 2021-06-24 16:15:36 -07:00
dependabot[bot] dc2e932559 chore: Bump mocha from 8.4.0 to 9.0.0 (#497)
Bumps [mocha](https://github.com/mochajs/mocha) from 8.4.0 to 9.0.0.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v8.4.0...v9.0.0)

---
updated-dependencies:
- dependency-name: mocha
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-08 13:47:36 +02:00
Jonathan Lipps 1a58a88001 7.7.0 2021-06-01 15:49:23 -07:00
Ayan Khan 3ade20ec31 feat: expose validateCaps in basedriver index 2021-06-01 15:46:19 -07:00
dependabot[bot] de7e41bbf7 chore: Bump sinon from 10.0.1 to 11.0.0 (#491)
Bumps [sinon](https://github.com/sinonjs/sinon) from 10.0.1 to 11.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v10.0.1...v11.0.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-25 14:42:26 +02:00
Kazuaki Matsuo b65c3a8286 fix: convert element.visualization to string (#489)
* fix to convert element.visualization to string

* add isstring

* Revert "add isstring"

This reverts commit 71b6f43aa5690cec9fe68e4d02c03478f9a9601c.
2021-05-22 03:18:07 +09:00
Mykola Mokhnach c750e6e1fe feat: Add a setting to customize image matching method (#480) 2021-05-03 19:52:11 +02:00
Kazuaki Matsuo cfb657b29e ci: use node 12 and 14 instead of 10 and 12 (#470) 2021-03-24 13:44:36 -07:00
dependabot[bot] 0f570e3b5d chore: Bump sinon from 9.2.4 to 10.0.0 (#469)
Bumps [sinon](https://github.com/sinonjs/sinon) from 9.2.4 to 10.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v9.2.4...v10.0.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-24 10:06:41 +09:00
Mykola Mokhnach 5071c652e5 7.6.0 2021-03-19 09:02:12 +01:00
Mykola Mokhnach a3221c08ad refactor: Remove swipe-related helpers from the base driver (#466) 2021-03-19 09:01:23 +01:00
Kazuaki Matsuo ffbc8b4229 7.5.1 2021-02-24 00:13:37 -08:00
Kazuaki Matsuo 537c29e5d6 fix: timeout of JWProxy for httpAgent and httpsAgent (#460)
* fix: timeout of JWProxy

* remove timeout
2021-02-24 17:10:29 +09:00
Mykola Mokhnach 63041b99bb 7.5.0 2021-02-19 12:21:15 +01:00
Alex Ong c5e6eb194e feat: Support multiple image match in findByImage (#449) 2021-02-19 12:19:00 +01:00
Mykola Mokhnach a04ceca921 7.4.2 2021-02-12 09:32:16 +01:00
Mykola Mokhnach 71555a0227 chore: Update minor axios versions 2021-02-11 19:33:07 +01:00
13 changed files with 98 additions and 91 deletions
+2 -2
View File
@@ -8,10 +8,10 @@ pool:
strategy:
matrix:
node_10_x:
node_version: 10.x
node_12_x:
node_version: 12.x
node_14_x:
node_version: 14.x
steps:
- task: NodeTool@0
+4 -2
View File
@@ -1,5 +1,7 @@
appium-base-driver
===================
# appium-base-driver
> **NOTE: `appium-base-driver` for Appium 2.x has moved to the [`2.0` branch of `appium/appium`](https://github.com/appium/appium/tree/2.0/packages/base-driver)**
[![NPM version](http://img.shields.io/npm/v/appium-base-driver.svg)](https://npmjs.org/package/appium-base-driver)
[![Downloads](http://img.shields.io/npm/dm/appium-base-driver.svg)](https://npmjs.org/package/appium-base-driver)
[![Dependency Status](https://david-dm.org/appium/appium-base-driver.svg)](https://david-dm.org/appium/appium-base-driver)
+2 -2
View File
@@ -55,8 +55,8 @@ export { statusCodes, getSummaryByCode };
// W3C capabilities parser
import * as caps from './lib/basedriver/capabilities';
const { processCapabilities, isStandardCap } = caps;
export { processCapabilities, isStandardCap };
const { processCapabilities, isStandardCap, validateCaps } = caps;
export { processCapabilities, isStandardCap, validateCaps };
// Web socket helpers
import * as ws from './lib/express/websocket';
+30 -17
View File
@@ -208,6 +208,7 @@ helpers.findByImage = async function findByImage (b64Template, {
}) {
const {
imageMatchThreshold: threshold,
imageMatchMethod,
fixImageTemplateSize,
fixImageTemplateScale,
defaultImageTemplateScale,
@@ -229,9 +230,7 @@ helpers.findByImage = async function findByImage (b64Template, {
screenHeight);
}
let rect = null;
let b64Matched = null;
let score = 0;
const results = [];
const condition = async () => {
try {
const {b64Screenshot, scale} = await this.getScreenshotForImageFind(screenWidth, screenHeight);
@@ -241,12 +240,27 @@ helpers.findByImage = async function findByImage (b64Template, {
fixImageTemplateScale, ...scale
});
const comparedImage = await this.compareImages(MATCH_TEMPLATE_MODE, b64Screenshot, b64Template, {threshold, visualize});
rect = comparedImage.rect;
b64Matched = comparedImage.visualization;
score = comparedImage.score;
const comparisonOpts = {
threshold,
visualize,
multiple,
};
if (imageMatchMethod) {
comparisonOpts.method = imageMatchMethod;
}
if (multiple) {
results.push(...(await this.compareImages(MATCH_TEMPLATE_MODE,
b64Screenshot,
b64Template,
comparisonOpts)));
} else {
results.push(await this.compareImages(MATCH_TEMPLATE_MODE,
b64Screenshot,
b64Template,
comparisonOpts));
}
return true;
} catch (err) {
// if compareImages fails, we'll get a specific error, but we should
// retry, so trap that and just return false to trigger the next round of
@@ -273,29 +287,28 @@ helpers.findByImage = async function findByImage (b64Template, {
}
}
if (!rect) {
if (_.isEmpty(results)) {
if (multiple) {
return [];
}
throw new errors.NoSuchElementError();
}
log.info(`Image template matched: ${JSON.stringify(rect)}`);
if (b64Matched) {
log.info(`Matched base64 data: ${b64Matched.substring(0, 200)}...`);
}
const imgEl = new ImageElement(b64Template, rect, score, b64Matched);
const elements = results.map(({rect, score, visualization}) => {
log.info(`Image template matched: ${JSON.stringify(rect)}`);
return new ImageElement(b64Template, rect, score, visualization);
});
// if we're just checking staleness, return straightaway so we don't add
// a new element to the cache. shouldCheckStaleness does not support multiple
// elements, since it is a purely internal mechanism
if (shouldCheckStaleness) {
return imgEl;
return elements[0];
}
const protocolEl = this.registerImageElement(imgEl);
const registeredElements = elements.map((imgEl) => this.registerImageElement(imgEl));
return multiple ? [protocolEl] : protocolEl;
return multiple ? registeredElements : registeredElements[0];
};
/**
+38 -6
View File
@@ -22,7 +22,21 @@ const DEFAULT_MATCH_THRESHOLD = 0.4;
* @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
* on the actual `mode` value. See the documentation on `appium-support`
* on the actual `mode` value.
* For MATCH_TEMPLATE_MODE:
* - visualize: include the visualization of the match in the result
* (default: false)
* - threshold: the image match threshold, higher values
* require a closer image similarity to match
* (default: 0.5)
* - multiple: return multiple matches in the image
* (default: false)
* - matchNeighbourThreshold: if multiple is specified,
* determine the number of pixels within
* which to consider a pixel as part of the
* same match result
* (default: 10)
* See the documentation in the `appium-support`
* module for more details.
* @returns {Object} The content of the resulting dictionary depends
* on the actual `mode` and `options` values. See the documentation on
@@ -34,7 +48,8 @@ const DEFAULT_MATCH_THRESHOLD = 0.4;
commands.compareImages = async function compareImages (mode, firstImage, secondImage, options = {}) {
const img1 = Buffer.from(firstImage, 'base64');
const img2 = Buffer.from(secondImage, 'base64');
let result = {};
let result = null;
switch (_.toLower(mode)) {
case MATCH_FEATURES_MODE.toLowerCase():
result = await imageUtil.getImagesMatches(img1, img2, options);
@@ -45,17 +60,34 @@ commands.compareImages = async function compareImages (mode, firstImage, secondI
case MATCH_TEMPLATE_MODE.toLowerCase():
// firstImage/img1 is the full image and secondImage/img2 is the partial one
result = await imageUtil.getImageOccurrence(img1, img2, options);
if (options.multiple) {
return result.multiple.map(convertVisualizationToBase64);
}
break;
default:
throw new errors.InvalidArgumentError(`'${mode}' images comparison mode is unknown. ` +
`Only ${JSON.stringify([MATCH_FEATURES_MODE, GET_SIMILARITY_MODE, MATCH_TEMPLATE_MODE])} modes are supported.`);
}
if (!_.isEmpty(result.visualization)) {
result.visualization = result.visualization.toString('base64');
}
return result;
return convertVisualizationToBase64(result);
};
/**
* base64 encodes the visualization part of the result
* (if necessary)
*
* @param {OccurenceResult} element - occurrence result
*
**/
function convertVisualizationToBase64 (element) {
if (!_.isEmpty(element.visualization)) {
element.visualization = element.visualization.toString('base64');
}
return element;
}
Object.assign(extensions, commands, helpers);
export { commands, helpers, DEFAULT_MATCH_THRESHOLD, MATCH_TEMPLATE_MODE };
export default extensions;
+7
View File
@@ -8,6 +8,12 @@ const GLOBAL_DEFAULT_SETTINGS = {
// element will not be found
imageMatchThreshold: DEFAULT_MATCH_THRESHOLD,
// One of possible image matching methods.
// Read https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_template_matching/py_template_matching.html
// for more details.
// TM_CCOEFF_NORMED by default
imageMatchMethod: '',
// if the image returned by getScreenshot differs in size or aspect ratio
// from the screen, attempt to fix it automatically
fixImageFindScreenshotDims: true,
@@ -54,6 +60,7 @@ const GLOBAL_DEFAULT_SETTINGS = {
// drivers can choose to ignore them for their own settings validation, etc...
const BASEDRIVER_HANDLED_SETTINGS = [
'imageMatchThreshold',
'imageMatchMethod',
'fixImageFindScreenshotDims',
'fixImageTemplateSize',
'fixImageTemplateScale',
+3 -30
View File
@@ -13,8 +13,9 @@ import { desiredCapabilityConstraints } from './desired-caps';
import { validateCaps } from './capabilities';
import B from 'bluebird';
import _ from 'lodash';
import { util } from 'appium-support';
import { ImageElement, makeImageElementCache, getImgElFromArgs } from './image-element';
import {
ImageElement, makeImageElementCache, getImgElFromArgs
} from './image-element';
import AsyncLock from 'async-lock';
import { EventEmitter } from 'events';
@@ -428,34 +429,6 @@ class BaseDriver extends Protocol {
this.clearNewCommandTimeout();
}
async getSwipeOptions (gestures, touchCount = 1) {
let startX = this.helpers.getCoordDefault(gestures[0].options.x),
startY = this.helpers.getCoordDefault(gestures[0].options.y),
endX = this.helpers.getCoordDefault(gestures[2].options.x),
endY = this.helpers.getCoordDefault(gestures[2].options.y),
duration = this.helpers.getSwipeTouchDuration(gestures[1]),
element = gestures[0].options.element,
destElement = gestures[2].options.element || gestures[0].options.element;
// there's no destination element handling in bootstrap and since it applies to all platforms, we handle it here
if (util.hasValue(destElement)) {
let locResult = await this.getLocationInView(destElement);
let sizeResult = await this.getSize(destElement);
let offsetX = (Math.abs(endX) < 1 && Math.abs(endX) > 0) ? sizeResult.width * endX : endX;
let offsetY = (Math.abs(endY) < 1 && Math.abs(endY) > 0) ? sizeResult.height * endY : endY;
endX = locResult.x + offsetX;
endY = locResult.y + offsetY;
// if the target element was provided, the coordinates for the destination need to be relative to it.
if (util.hasValue(element)) {
let firstElLocation = await this.getLocationInView(element);
endX -= firstElLocation.x;
endY -= firstElLocation.y;
}
}
// clients are responsible to use these options correctly
return {startX, startY, endX, endY, duration, touchCount, element};
}
proxyActive (/* sessionId */) {
return false;
}
+1 -23
View File
@@ -359,28 +359,6 @@ function isPackageOrBundle (app) {
return (/^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/).test(app);
}
function getCoordDefault (val) {
// going the long way and checking for undefined and null since
// we can't be assured `elId` is a string and not an int. Same
// thing with destElement below.
return util.hasValue(val) ? val : 0.5;
}
function getSwipeTouchDuration (waitGesture) {
// the touch action api uses ms, we want seconds
// 0.8 is the default time for the operation
let duration = 0.8;
if (typeof waitGesture.options.ms !== 'undefined' && waitGesture.options.ms) {
duration = waitGesture.options.ms / 1000;
if (duration === 0) {
// set to a very low number, since they wanted it fast
// but below 0.1 becomes 0 steps, which causes errors
duration = 0.1;
}
}
return duration;
}
/**
* Finds all instances 'firstKey' and create a duplicate with the key 'secondKey',
* Do the same thing in reverse. If we find 'secondKey', create a duplicate with the key 'firstKey'.
@@ -443,5 +421,5 @@ function parseCapsArray (cap) {
}
export {
configureApp, isPackageOrBundle, getCoordDefault, getSwipeTouchDuration, duplicateKeys, parseCapsArray
configureApp, isPackageOrBundle, duplicateKeys, parseCapsArray
};
-1
View File
@@ -41,7 +41,6 @@ class JWProxy {
keepAlive: opts.keepAlive ?? true,
maxSockets: 10,
maxFreeSockets: 5,
timeout: 60000,
};
this.httpAgent = new http.Agent(agentOpts);
this.httpsAgent = new https.Agent(agentOpts);
+1
View File
@@ -280,6 +280,7 @@ const METHOD_MAP = {
},
'/session/:sessionId/actions': {
POST: {command: 'performActions', payloadParams: {required: ['actions']}},
DELETE: {command: 'releaseActions'},
},
'/session/:sessionId/touch/longclick': {
POST: {command: 'touchLongClick', payloadParams: {required: ['elements']}}
+6 -6
View File
@@ -4,7 +4,7 @@
"keywords": [
"appium"
],
"version": "7.4.1",
"version": "7.8.0",
"author": "appium",
"license": "Apache-2.0",
"repository": {
@@ -35,10 +35,10 @@
],
"dependencies": {
"@babel/runtime": "^7.0.0",
"appium-support": "^2.48.0",
"appium-support": "^2.52.0",
"async-lock": "^1.0.0",
"asyncbox": "^2.3.1",
"axios": "^0.21.0",
"axios": "^0.x",
"bluebird": "^3.5.3",
"body-parser": "^1.18.2",
"colors": "^1.1.2",
@@ -50,7 +50,7 @@
"method-override": "^3.0.0",
"morgan": "^1.9.0",
"serve-favicon": "^2.4.5",
"source-map-support": "^0.5.5",
"source-map-support": "^0.x",
"validate.js": "^0.13.0",
"webdriverio": "^6.0.2",
"ws": "^7.0.0"
@@ -81,10 +81,10 @@
"eslint-config-appium": "^4.0.1",
"finalhandler": "^1.1.1",
"gulp": "^4.0.0",
"mocha": "^8.0.1",
"mocha": "^9.0.0",
"querystring": "^0.2.0",
"pre-commit": "^1.1.3",
"serve-static": "^1.13.1",
"sinon": "^9.0.0"
"sinon": "^11.0.0"
}
}
+3 -1
View File
@@ -74,7 +74,9 @@ describe('finding elements by image', function () {
});
it('should find image elements happypath', async function () {
const d = new TestDriver();
basicStub(d);
const {compareStub} = basicStub(d);
compareStub.returns([{rect, score}]);
const els = await d.findByImage(template, {multiple: true});
els.should.have.length(1);
basicImgElVerify(els[0], d);
+1 -1
View File
@@ -40,7 +40,7 @@ describe('Protocol', function () {
}
let hash = shasum.digest('hex').substring(0, 8);
// Modify the hash whenever the protocol has intentionally been modified.
hash.should.equal('52aacae0');
hash.should.equal('b1dff09e');
});
});