Skip to content

Commit b4c6709

Browse files
vchimevvchimev
vchimev
authored and
vchimev
committed
feat(image-comparison): first time capture of element/rectangle
1 parent 3e8d840 commit b4c6709

File tree

4 files changed

+117
-105
lines changed

4 files changed

+117
-105
lines changed

lib/appium-driver.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export declare class AppiumDriver {
123123
source(): Promise<any>;
124124
sessionId(): Promise<any>;
125125
compareElement(element: UIElement, imageName: string): Promise<boolean>;
126-
compareRectangle(rect: IRectangle, imageName: string, timeOutSeconds?: number, tollerance?: number): Promise<boolean>;
126+
compareRectangles(rect: IRectangle, imageName: string, timeOutSeconds?: number, tollerance?: number): Promise<boolean>;
127127
compareScreen(imageName: string, timeOutSeconds?: number, tollerance?: number): Promise<boolean>;
128128
takeScreenshot(fileName: string): Promise<string>;
129129
logScreenshot(fileName: string): Promise<string>;
@@ -134,4 +134,5 @@ export declare class AppiumDriver {
134134
quit(): Promise<void>;
135135
private convertArrayToUIElements(array, searchM, args);
136136
private static configureLogging(driver, verbose);
137+
private getExpectedImagePath(imageName);
137138
}

lib/appium-driver.ts

Lines changed: 73 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -270,137 +270,96 @@ export class AppiumDriver {
270270
}
271271

272272
public async compareElement(element: UIElement, imageName: string, ) {
273-
return this.compareRectangle(await element.getRectangle(), imageName);
273+
return this.compareRectangles(await element.getRectangle(), imageName);
274274
}
275275

276-
public async compareRectangle(rect: IRectangle, imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
276+
public async compareRectangles(rect: IRectangle, imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
277+
277278
if (!imageName.endsWith(AppiumDriver.pngFileExt)) {
278279
imageName = imageName.concat(AppiumDriver.pngFileExt);
279280
}
280281

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-
}
282+
const pathExpectedImage = this.getExpectedImagePath(imageName);
296283

297-
if (!this._logPath) {
298-
this._logPath = getReportPath(this._args);
284+
// First time capture
285+
if (!fileExists(pathExpectedImage)) {
286+
const pathActualImage = resolve(this._storageByDeviceName, imageName.replace(".", "_actual."));
287+
await this.takeScreenshot(pathActualImage);
288+
await this._imageHelper.clipRectangleImage(rect, pathActualImage);
289+
console.log("Remove the 'actual' suffix to continue using the image as expected one ", pathExpectedImage);
290+
return false;
299291
}
300292

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-
}
293+
// Compare
294+
let pathActualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual.")));
295+
const pathDiffImage = pathActualImage.replace("actual", "diff");
315296

316-
// return false;
317-
}
297+
await this._imageHelper.clipRectangleImage(rect, pathActualImage);
298+
const cropPoint = new Point(0, 0);
299+
await this._imageHelper.setCropImagePoint(cropPoint);
300+
let result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
318301

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);
302+
// Iterate
322303
if (!result) {
323-
let eventStartTime = Date.now().valueOf();
304+
const eventStartTime = Date.now().valueOf();
324305
let counter = 1;
325306
timeOutSeconds *= 1000;
326307
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);
308+
const pathActualImageConter = resolve(this._logPath, imageName.replace(".", "_actual_" + counter + "."));
309+
pathActualImage = await this.takeScreenshot(pathActualImageConter);
310+
await this._imageHelper.clipRectangleImage(rect, pathActualImage);
311+
await this._imageHelper.setCropImagePoint(cropPoint);
312+
result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
329313
counter++;
330314
}
331315
} else {
332-
if (fileExists(diffImage)) {
333-
unlinkSync(diffImage);
316+
if (fileExists(pathDiffImage)) {
317+
unlinkSync(pathDiffImage);
334318
}
335-
if (fileExists(actualImage)) {
336-
unlinkSync(actualImage);
319+
if (fileExists(pathActualImage)) {
320+
unlinkSync(pathActualImage);
337321
}
338322
}
339323

340324
return result;
341325
}
342326

343327
public async compareScreen(imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
328+
344329
if (!imageName.endsWith(AppiumDriver.pngFileExt)) {
345330
imageName = imageName.concat(AppiumDriver.pngFileExt);
346331
}
347332

348-
if (!this._storageByDeviceName) {
349-
this._storageByDeviceName = getStorageByDeviceName(this._args);
350-
}
351-
352-
let expectedImage = resolve(this._storageByDeviceName, imageName);
353-
if (!fileExists(expectedImage)) {
354-
if (!this._storageByPlatform) {
355-
this._storageByPlatform = getStorageByPlatform(this._args);
356-
}
357-
expectedImage = resolve(this._storageByPlatform, imageName);
358-
}
359-
360-
if (!fileExists(expectedImage)) {
361-
expectedImage = resolve(this._storageByDeviceName, imageName);
362-
}
363-
364-
if (!this._logPath) {
365-
this._logPath = getReportPath(this._args);
366-
}
367-
368-
expectedImage = resolve(this._storageByDeviceName, imageName);
333+
const pathExpectedImage = this.getExpectedImagePath(imageName);
369334

370-
// Firts capture of screen when the expected image is not available
371-
if (!fileExists(expectedImage)) {
335+
// First time capture
336+
if (!fileExists(pathExpectedImage)) {
372337
await this.takeScreenshot(resolve(this._storageByDeviceName, imageName.replace(".", "_actual.")));
373-
console.log("Remove the 'actual' suffix to continue using the image as expected one ", expectedImage);
374-
let eventStartTime = Date.now().valueOf();
375-
let counter = 1;
376-
timeOutSeconds *= 1000;
377-
378-
while ((Date.now().valueOf() - eventStartTime) <= timeOutSeconds) {
379-
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual" + "_" + counter + ".")));
380-
counter++;
381-
}
382-
338+
console.log("Remove the 'actual' suffix to continue using the image as expected one ", pathExpectedImage);
383339
return false;
384340
}
385341

386-
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual.")));
387-
let diffImage = actualImage.replace("actual", "diff");
388-
let result = await this._imageHelper.compareImages(actualImage, expectedImage, diffImage, tollerance);
342+
// Compare
343+
let pathActualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual.")));
344+
const pathDiffImage = pathActualImage.replace("actual", "diff");
345+
let result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
346+
347+
// Iterate
389348
if (!result) {
390-
let eventStartTime = Date.now().valueOf();
349+
const eventStartTime = Date.now().valueOf();
391350
let counter = 1;
392351
timeOutSeconds *= 1000;
393352
while ((Date.now().valueOf() - eventStartTime) <= timeOutSeconds && !result) {
394-
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual" + "_" + counter + ".")));
395-
result = await this._imageHelper.compareImages(actualImage, expectedImage, diffImage, tollerance);
353+
pathActualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual_" + counter + ".")));
354+
result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
396355
counter++;
397356
}
398357
} else {
399-
if (fileExists(diffImage)) {
400-
unlinkSync(diffImage);
358+
if (fileExists(pathDiffImage)) {
359+
unlinkSync(pathDiffImage);
401360
}
402-
if (fileExists(actualImage)) {
403-
unlinkSync(actualImage);
361+
if (fileExists(pathActualImage)) {
362+
unlinkSync(pathActualImage);
404363
}
405364
}
406365

@@ -529,4 +488,30 @@ export class AppiumDriver {
529488
log(" > " + meth.magenta + path + " " + (data || "").grey, verbose);
530489
});
531490
};
491+
492+
private getExpectedImagePath(imageName: string) {
493+
494+
if (!this._storageByDeviceName) {
495+
this._storageByDeviceName = getStorageByDeviceName(this._args);
496+
}
497+
498+
let pathExpectedImage = resolve(this._storageByDeviceName, imageName);
499+
500+
if (!fileExists(pathExpectedImage)) {
501+
if (!this._storageByPlatform) {
502+
this._storageByPlatform = getStorageByPlatform(this._args);
503+
}
504+
pathExpectedImage = resolve(this._storageByPlatform, imageName);
505+
}
506+
507+
if (!fileExists(pathExpectedImage)) {
508+
pathExpectedImage = resolve(this._storageByDeviceName, imageName);
509+
}
510+
511+
if (!this._logPath) {
512+
this._logPath = getReportPath(this._args);
513+
}
514+
515+
return pathExpectedImage;
516+
}
532517
}

lib/image-helper.d.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
import { ImageOptions } from "./image-options";
22
import { INsCapabilities } from "./interfaces/ns-capabilities";
33
import { IRectangle } from "./interfaces/rectangle";
4+
import { Point } from "./point";
45
export declare class ImageHelper {
56
private _args;
6-
private _cropImageRect;
7+
private _cropImagePoint;
78
private _blockOutAreas;
89
constructor(_args: INsCapabilities);
9-
readonly cropImageRect: IRectangle;
10-
cropImageRec: IRectangle;
10+
cropImagePoint: Point;
11+
setCropImagePoint(point: Point): void;
1112
blockOutAreas: IRectangle[];
1213
imageOutputLimit(): ImageOptions;
1314
thresholdType(): ImageOptions;
1415
threshold(): number;
1516
delta(): number;
16-
private static getOffsetPixels(args);
1717
static cropImageDefaultParams(_args: INsCapabilities): {
1818
x: number;
1919
y: any;
2020
};
21+
private static getOffsetPixels(args);
2122
private runDiff(diffOptions, diffImage);
22-
compareRectangle(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
23+
compareRectangles(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
2324
compareImages(actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
25+
clipRectangleImage(rect: IRectangle, path: string): Promise<void>;
26+
readImage(path: string): Promise<{}>;
2427
}

lib/image-helper.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
import * as blinkDiff from "blink-diff";
2+
import * as pngJsImage from "pngjs-image";
23
import { ImageOptions } from "./image-options";
34
import { INsCapabilities } from "./interfaces/ns-capabilities";
45
import { IRectangle } from "./interfaces/rectangle";
6+
import { Point } from "./point";
57

68
export class ImageHelper {
79

8-
private _cropImageRect: IRectangle;
10+
private _cropImagePoint: Point;
911
private _blockOutAreas: IRectangle[];
1012

1113
constructor(private _args: INsCapabilities) {
1214
}
1315

14-
get cropImageRect() {
15-
return this._cropImageRect;
16+
get cropImagePoint(): Point {
17+
return this._cropImagePoint;
1618
}
1719

18-
set cropImageRec(rect: IRectangle) {
19-
this._cropImageRect = rect;
20+
set cropImagePoint(point: Point) {
21+
this._cropImagePoint = point;
22+
}
23+
24+
// ?
25+
public setCropImagePoint(point: Point) {
26+
this._cropImagePoint = point;
2027
}
2128

2229
get blockOutAreas() {
@@ -43,14 +50,14 @@ export class ImageHelper {
4350
return 20;
4451
}
4552

46-
private static getOffsetPixels(args: INsCapabilities) {
47-
return args.device.config ? args.device.config.offsetPixels : 0
48-
}
49-
5053
public static cropImageDefaultParams(_args: INsCapabilities) {
5154
return { x: 0, y: ImageHelper.getOffsetPixels(_args) };
5255
}
5356

57+
private static getOffsetPixels(args: INsCapabilities) {
58+
return args.device.config ? args.device.config.offsetPixels : 0
59+
}
60+
5461
private runDiff(diffOptions: blinkDiff, diffImage: string) {
5562
return new Promise<boolean>((resolve, reject) => {
5663
diffOptions.run(function (error, result) {
@@ -76,7 +83,7 @@ export class ImageHelper {
7683
});
7784
}
7885

79-
public compareRectangle(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
86+
public compareRectangles(rect: IRectangle, actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
8087
let diff = new blinkDiff({
8188

8289
imageAPath: actual,
@@ -96,7 +103,7 @@ export class ImageHelper {
96103
}
97104

98105
public compareImages(actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
99-
const rectToCrop = this._cropImageRect || ImageHelper.cropImageDefaultParams(this._args);
106+
const rectToCrop = this._cropImagePoint || ImageHelper.cropImageDefaultParams(this._args);
100107
let diff = new blinkDiff({
101108

102109
imageAPath: actual,
@@ -117,4 +124,20 @@ export class ImageHelper {
117124
this._blockOutAreas = undefined;
118125
return result;
119126
}
127+
128+
public async clipRectangleImage(rect: IRectangle, path: string) {
129+
let imageToClip: pngJsImage;
130+
imageToClip = await this.readImage(path);
131+
imageToClip.clip(rect.x, rect.y, rect.width, rect.height);
132+
imageToClip.writeImage(path);
133+
}
134+
135+
public async readImage(path: string) {
136+
return new Promise((resolve, reject) => {
137+
pngJsImage.readImage(path, function (err, image) {
138+
if (err) throw err;
139+
return resolve(image);
140+
});
141+
})
142+
}
120143
}

0 commit comments

Comments
 (0)