Skip to content

Commit 3e8d840

Browse files
vchimevvchimev
vchimev
authored and
vchimev
committed
feat(image-comparison): implement for elements and rectangles
1 parent 0058296 commit 3e8d840

File tree

6 files changed

+138
-10
lines changed

6 files changed

+138
-10
lines changed

lib/appium-driver.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { UIElement } from "./ui-element";
55
import { Direction } from "./direction";
66
import { Locator } from "./locators";
77
import { INsCapabilities } from "./interfaces/ns-capabilities";
8+
import { IRectangle } from "./interfaces/rectangle";
89
import { Point } from "./point";
910
import { ImageHelper } from "./image-helper";
1011
export declare class AppiumDriver {
@@ -121,6 +122,8 @@ export declare class AppiumDriver {
121122
swipe(y: number, x: number, yOffset: number, inertia?: number, xOffset?: number): Promise<void>;
122123
source(): Promise<any>;
123124
sessionId(): Promise<any>;
125+
compareElement(element: UIElement, imageName: string): Promise<boolean>;
126+
compareRectangle(rect: IRectangle, imageName: string, timeOutSeconds?: number, tollerance?: number): Promise<boolean>;
124127
compareScreen(imageName: string, timeOutSeconds?: number, tollerance?: number): Promise<boolean>;
125128
takeScreenshot(fileName: string): Promise<string>;
126129
logScreenshot(fileName: string): Promise<string>;

lib/appium-driver.ts

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
scroll
2323
} from "./utils";
2424
import { INsCapabilities } from "./interfaces/ns-capabilities";
25+
import { IRectangle } from "./interfaces/rectangle";
2526
import { Point } from "./point";
2627
import { ImageHelper } from "./image-helper";
2728
import { ImageOptions } from "./image-options"
@@ -268,6 +269,77 @@ export class AppiumDriver {
268269
return await this.driver.getSessionId();
269270
}
270271

272+
public async compareElement(element: UIElement, imageName: string, ) {
273+
return this.compareRectangle(await element.getRectangle(), imageName);
274+
}
275+
276+
public async compareRectangle(rect: IRectangle, imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
277+
if (!imageName.endsWith(AppiumDriver.pngFileExt)) {
278+
imageName = imageName.concat(AppiumDriver.pngFileExt);
279+
}
280+
281+
if (!this._storageByDeviceName) {
282+
this._storageByDeviceName = getStorageByDeviceName(this._args);
283+
}
284+
285+
let expectedImage = resolve(this._storageByDeviceName, imageName);
286+
if (!fileExists(expectedImage)) {
287+
if (!this._storageByPlatform) {
288+
this._storageByPlatform = getStorageByPlatform(this._args);
289+
}
290+
expectedImage = resolve(this._storageByPlatform, imageName);
291+
}
292+
293+
if (!fileExists(expectedImage)) {
294+
expectedImage = resolve(this._storageByDeviceName, imageName);
295+
}
296+
297+
if (!this._logPath) {
298+
this._logPath = getReportPath(this._args);
299+
}
300+
301+
expectedImage = resolve(this._storageByDeviceName, imageName);
302+
303+
// Firts capture of screen when the expected image is not available
304+
if (!fileExists(expectedImage)) {
305+
await this.takeScreenshot(resolve(this._storageByDeviceName, imageName.replace(".", "_actual.")));
306+
console.log("Remove the 'actual' suffix to continue using the image as expected one ", expectedImage);
307+
let eventStartTime = Date.now().valueOf();
308+
let counter = 1;
309+
timeOutSeconds *= 1000;
310+
311+
while ((Date.now().valueOf() - eventStartTime) <= timeOutSeconds) {
312+
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual" + "_" + counter + ".")));
313+
counter++;
314+
}
315+
316+
// return false;
317+
}
318+
319+
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual.")));
320+
let diffImage = actualImage.replace("actual", "diff");
321+
let result = await this._imageHelper.compareRectangle(rect, actualImage, expectedImage, diffImage, tollerance);
322+
if (!result) {
323+
let eventStartTime = Date.now().valueOf();
324+
let counter = 1;
325+
timeOutSeconds *= 1000;
326+
while ((Date.now().valueOf() - eventStartTime) <= timeOutSeconds && !result) {
327+
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual" + "_" + counter + ".")));
328+
result = await this._imageHelper.compareRectangle(rect, actualImage, expectedImage, diffImage, tollerance);
329+
counter++;
330+
}
331+
} else {
332+
if (fileExists(diffImage)) {
333+
unlinkSync(diffImage);
334+
}
335+
if (fileExists(actualImage)) {
336+
unlinkSync(actualImage);
337+
}
338+
}
339+
340+
return result;
341+
}
342+
271343
public async compareScreen(imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
272344
if (!imageName.endsWith(AppiumDriver.pngFileExt)) {
273345
imageName = imageName.concat(AppiumDriver.pngFileExt);
@@ -281,14 +353,14 @@ export class AppiumDriver {
281353
if (!fileExists(expectedImage)) {
282354
if (!this._storageByPlatform) {
283355
this._storageByPlatform = getStorageByPlatform(this._args);
284-
}
285-
expectedImage = resolve(this._storageByPlatform, imageName);
356+
}
357+
expectedImage = resolve(this._storageByPlatform, imageName);
286358
}
287-
359+
288360
if (!fileExists(expectedImage)) {
289361
expectedImage = resolve(this._storageByDeviceName, imageName);
290362
}
291-
363+
292364
if (!this._logPath) {
293365
this._logPath = getReportPath(this._args);
294366
}

lib/image-helper.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export declare class ImageHelper {
1919
y: any;
2020
};
2121
private runDiff(diffOptions, diffImage);
22+
compareRectangle(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
2223
compareImages(actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
2324
}

lib/image-helper.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,25 @@ export class ImageHelper {
7676
});
7777
}
7878

79+
public compareRectangle(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
80+
let diff = new blinkDiff({
81+
82+
imageAPath: actual,
83+
imageBPath: expected,
84+
imageOutputPath: output,
85+
imageOutputLimit: this.imageOutputLimit(),
86+
thresholdType: typeThreshold,
87+
threshold: valueThreshold,
88+
delta: this.delta(),
89+
90+
cropImageA: rect,
91+
verbose: this._args.verbose,
92+
});
93+
94+
const result = this.runDiff(diff, output);
95+
return result;
96+
}
97+
7998
public compareImages(actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
8099
const rectToCrop = this._cropImageRect || ImageHelper.cropImageDefaultParams(this._args);
81100
let diff = new blinkDiff({

lib/ui-element.d.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export declare class UIElement {
1818
*/
1919
tap(): Promise<any>;
2020
/**
21-
* double tap
21+
* Double tap on element
2222
*/
2323
doubleTap(): Promise<any>;
2424
/**
@@ -29,6 +29,9 @@ export declare class UIElement {
2929
* Get size of element
3030
*/
3131
size(): Promise<Point>;
32+
/**
33+
* Get text of element
34+
*/
3235
text(): Promise<any>;
3336
/**
3437
* Get web driver element
@@ -52,7 +55,20 @@ export declare class UIElement {
5255
* @param wait
5356
*/
5457
waitForExist(wait?: number): Promise<any>;
58+
/**
59+
* Get attribute of element
60+
* @param attr
61+
*/
5562
getAttribute(attr: any): Promise<any>;
63+
/**
64+
* Get rectangle of element
65+
*/
66+
getRectangle(): Promise<{
67+
x: number;
68+
y: number;
69+
width: number;
70+
height: number;
71+
}>;
5672
/**
5773
* Scroll with offset from elemnt with minimum inertia
5874
* @param direction

lib/ui-element.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Point } from "./point";
22
import { Direction } from "./direction";
3+
import { IRectangle } from "./interfaces/rectangle";
34
import { calculateOffset } from "./utils";
45

56
export class UIElement {
6-
constructor(private _element: any, private _driver: any, private _wd: any, private _webio: any, private _searchMethod: string, private _searchParams: string, private _index?: number) {
7-
}
7+
constructor(private _element: any, private _driver: any, private _wd: any, private _webio: any, private _searchMethod: string, private _searchParams: string, private _index?: number) { }
88

99
/**
1010
* Click on element
@@ -21,7 +21,7 @@ export class UIElement {
2121
}
2222

2323
/**
24-
* double tap
24+
* Double tap on element
2525
*/
2626
public async doubleTap() {
2727
return await this._driver.execute('mobile: doubleTap', { element: (await this.element()).value.ELEMENT });
@@ -46,6 +46,9 @@ export class UIElement {
4646
return point;
4747
}
4848

49+
/**
50+
* Get text of element
51+
*/
4952
public async text() {
5053
return await (await this.element()).text();
5154
}
@@ -89,10 +92,24 @@ export class UIElement {
8992
return this._webio.waitForExist(this._searchParams, wait, false);
9093
}
9194

95+
/**
96+
* Get attribute of element
97+
* @param attr
98+
*/
9299
public async getAttribute(attr) {
93100
return await (await this.element()).getAttribute(attr);
94101
}
95102

103+
/**
104+
* Get rectangle of element
105+
*/
106+
public async getRectangle() {
107+
const location = await this.location();
108+
const size = await this.size();
109+
const rect = { x: location.x, y: location.y, width: size.y, height: size.x };
110+
return rect;
111+
}
112+
96113
/**
97114
* Scroll with offset from elemnt with minimum inertia
98115
* @param direction
@@ -112,7 +129,7 @@ export class UIElement {
112129

113130
if (direction === Direction.down) {
114131
y = (location.y + size.y) - 15;
115-
132+
116133
if (!this._webio.isIOS) {
117134
if (yOffset === 0) {
118135
yOffset = location.y + size.y - 15;
@@ -197,7 +214,7 @@ export class UIElement {
197214
public async hold() {
198215
let action = new this._wd.TouchAction(this._driver);
199216
action
200-
.longPress({el: await this.element()})
217+
.longPress({ el: await this.element() })
201218
.release();
202219
await action.perform();
203220
await this._driver.sleep(150);

0 commit comments

Comments
 (0)