Skip to content

Commit d117655

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

File tree

6 files changed

+120
-141
lines changed

6 files changed

+120
-141
lines changed

lib/appium-driver.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ 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>;
128+
compare(imageName: string, timeOutSeconds?: number, tollerance?: number, rect?: IRectangle): Promise<boolean>;
129+
prepareImageToCompare(filePath: string, rect: IRectangle): Promise<void>;
128130
takeScreenshot(fileName: string): Promise<string>;
129131
logScreenshot(fileName: string): Promise<string>;
130132
logPageSource(fileName: string): Promise<void>;
@@ -134,4 +136,5 @@ export declare class AppiumDriver {
134136
quit(): Promise<void>;
135137
private convertArrayToUIElements(array, searchM, args);
136138
private static configureLogging(driver, verbose);
139+
private getExpectedImagePath(imageName);
137140
}

lib/appium-driver.ts

Lines changed: 68 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { UIElement } from "./ui-element";
1111
import { Direction } from "./direction";
1212
import { Locator } from "./locators";
1313
import {
14+
addExt,
1415
log,
1516
getStorageByPlatform,
1617
getStorageByDeviceName,
@@ -19,7 +20,7 @@ import {
1920
getAppPath,
2021
getReportPath,
2122
calculateOffset,
22-
scroll
23+
scroll,
2324
} from "./utils";
2425
import { INsCapabilities } from "./interfaces/ns-capabilities";
2526
import { IRectangle } from "./interfaces/rectangle";
@@ -270,141 +271,81 @@ export class AppiumDriver {
270271
}
271272

272273
public async compareElement(element: UIElement, imageName: string, ) {
273-
return this.compareRectangle(await element.getRectangle(), imageName);
274+
return this.compareRectangles(await element.getRectangle(), imageName);
274275
}
275276

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-
}
277+
public async compareRectangles(rect: IRectangle, imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
278+
return this.compare(imageName, timeOutSeconds, tollerance, rect);
279+
}
284280

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-
}
281+
public async compareScreen(imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
282+
return this.compare(imageName, timeOutSeconds, tollerance);
283+
}
292284

293-
if (!fileExists(expectedImage)) {
294-
expectedImage = resolve(this._storageByDeviceName, imageName);
295-
}
285+
public async compare(imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01, rect?: IRectangle) {
296286

297287
if (!this._logPath) {
298288
this._logPath = getReportPath(this._args);
299289
}
300290

301-
expectedImage = resolve(this._storageByDeviceName, imageName);
291+
imageName = addExt(imageName, AppiumDriver.pngFileExt);
302292

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;
293+
const pathExpectedImage = this.getExpectedImagePath(imageName);
310294

311-
while ((Date.now().valueOf() - eventStartTime) <= timeOutSeconds) {
312-
let actualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual" + "_" + counter + ".")));
313-
counter++;
295+
// First time capture
296+
if (!fileExists(pathExpectedImage)) {
297+
const pathActualImage = resolve(this._storageByDeviceName, imageName.replace(".", "_actual."));
298+
await this.takeScreenshot(pathActualImage);
299+
300+
if (rect) {
301+
await this._imageHelper.clipRectangleImage(rect, pathActualImage);
314302
}
315303

316-
// return false;
304+
console.log("Remove the 'actual' suffix to continue using the image as expected one ", pathExpectedImage);
305+
return false;
317306
}
318307

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);
308+
// Compare
309+
let pathActualImage = await this.takeScreenshot(resolve(this._logPath, imageName.replace(".", "_actual.")));
310+
const pathDiffImage = pathActualImage.replace("actual", "diff");
311+
312+
await this.prepareImageToCompare(pathActualImage, rect);
313+
let result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
314+
315+
// Iterate
322316
if (!result) {
323-
let eventStartTime = Date.now().valueOf();
317+
const eventStartTime = Date.now().valueOf();
324318
let counter = 1;
325319
timeOutSeconds *= 1000;
326320
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);
321+
const pathActualImageConter = resolve(this._logPath, imageName.replace(".", "_actual_" + counter + "."));
322+
pathActualImage = await this.takeScreenshot(pathActualImageConter);
323+
324+
await this.prepareImageToCompare(pathActualImage, rect);
325+
result = await this._imageHelper.compareImages(pathActualImage, pathExpectedImage, pathDiffImage, tollerance);
329326
counter++;
330327
}
331328
} else {
332-
if (fileExists(diffImage)) {
333-
unlinkSync(diffImage);
329+
if (fileExists(pathDiffImage)) {
330+
unlinkSync(pathDiffImage);
334331
}
335-
if (fileExists(actualImage)) {
336-
unlinkSync(actualImage);
332+
if (fileExists(pathActualImage)) {
333+
unlinkSync(pathActualImage);
337334
}
338335
}
339336

337+
this._imageHelper.imageCropRect = undefined;
340338
return result;
341339
}
342340

343-
public async compareScreen(imageName: string, timeOutSeconds: number = 3, tollerance: number = 0.01) {
344-
if (!imageName.endsWith(AppiumDriver.pngFileExt)) {
345-
imageName = imageName.concat(AppiumDriver.pngFileExt);
346-
}
347-
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);
369-
370-
// Firts capture of screen when the expected image is not available
371-
if (!fileExists(expectedImage)) {
372-
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-
383-
return false;
384-
}
385-
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);
389-
if (!result) {
390-
let eventStartTime = Date.now().valueOf();
391-
let counter = 1;
392-
timeOutSeconds *= 1000;
393-
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);
396-
counter++;
397-
}
341+
public async prepareImageToCompare(filePath: string, rect: IRectangle) {
342+
if (rect) {
343+
await this._imageHelper.clipRectangleImage(rect, filePath);
344+
const rectToCrop = { x: 0, y: 0, width: undefined, height: undefined };
345+
this._imageHelper.imageCropRect = rectToCrop;
398346
} else {
399-
if (fileExists(diffImage)) {
400-
unlinkSync(diffImage);
401-
}
402-
if (fileExists(actualImage)) {
403-
unlinkSync(actualImage);
404-
}
347+
this._imageHelper.imageCropRect = ImageHelper.cropImageDefault(this._args);
405348
}
406-
407-
return result;
408349
}
409350

410351
public takeScreenshot(fileName: string) {
@@ -529,4 +470,26 @@ export class AppiumDriver {
529470
log(" > " + meth.magenta + path + " " + (data || "").grey, verbose);
530471
});
531472
};
473+
474+
private getExpectedImagePath(imageName: string) {
475+
476+
if (!this._storageByDeviceName) {
477+
this._storageByDeviceName = getStorageByDeviceName(this._args);
478+
}
479+
480+
let pathExpectedImage = resolve(this._storageByDeviceName, imageName);
481+
482+
if (!fileExists(pathExpectedImage)) {
483+
if (!this._storageByPlatform) {
484+
this._storageByPlatform = getStorageByPlatform(this._args);
485+
}
486+
pathExpectedImage = resolve(this._storageByPlatform, imageName);
487+
}
488+
489+
if (!fileExists(pathExpectedImage)) {
490+
pathExpectedImage = resolve(this._storageByDeviceName, imageName);
491+
}
492+
493+
return pathExpectedImage;
494+
}
532495
}

lib/image-helper.d.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@ export declare class ImageHelper {
66
private _cropImageRect;
77
private _blockOutAreas;
88
constructor(_args: INsCapabilities);
9-
readonly cropImageRect: IRectangle;
10-
cropImageRec: IRectangle;
9+
imageCropRect: IRectangle;
1110
blockOutAreas: IRectangle[];
1211
imageOutputLimit(): ImageOptions;
1312
thresholdType(): ImageOptions;
1413
threshold(): number;
1514
delta(): number;
16-
private static getOffsetPixels(args);
17-
static cropImageDefaultParams(_args: INsCapabilities): {
15+
static cropImageDefault(_args: INsCapabilities): {
1816
x: number;
1917
y: any;
18+
width: any;
19+
height: 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>;
2323
compareImages(actual: string, expected: string, output: string, valueThreshold?: number, typeThreshold?: any): Promise<boolean>;
24+
clipRectangleImage(rect: IRectangle, path: string): Promise<{}>;
25+
readImage(path: string): Promise<any>;
2426
}

lib/image-helper.ts

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import * as blinkDiff from "blink-diff";
1+
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

@@ -11,11 +13,11 @@ export class ImageHelper {
1113
constructor(private _args: INsCapabilities) {
1214
}
1315

14-
get cropImageRect() {
16+
get imageCropRect(): IRectangle {
1517
return this._cropImageRect;
1618
}
1719

18-
set cropImageRec(rect: IRectangle) {
20+
set imageCropRect(rect: IRectangle) {
1921
this._cropImageRect = rect;
2022
}
2123

@@ -43,15 +45,15 @@ export class ImageHelper {
4345
return 20;
4446
}
4547

46-
private static getOffsetPixels(args: INsCapabilities) {
47-
return args.device.config ? args.device.config.offsetPixels : 0
48+
public static cropImageDefault(_args: INsCapabilities) {
49+
return { x: 0, y: ImageHelper.getOffsetPixels(_args), width: undefined, height: undefined };
4850
}
4951

50-
public static cropImageDefaultParams(_args: INsCapabilities) {
51-
return { x: 0, y: ImageHelper.getOffsetPixels(_args) };
52+
private static getOffsetPixels(args: INsCapabilities) {
53+
return args.device.config ? args.device.config.offsetPixels : 0
5254
}
5355

54-
private runDiff(diffOptions: blinkDiff, diffImage: string) {
56+
private runDiff(diffOptions: BlinkDiff, diffImage: string) {
5557
return new Promise<boolean>((resolve, reject) => {
5658
diffOptions.run(function (error, result) {
5759
if (error) {
@@ -76,8 +78,8 @@ export class ImageHelper {
7678
});
7779
}
7880

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+
public compareImages(actual: string, expected: string, output: string, valueThreshold: number = this.threshold(), typeThreshold: any = ImageOptions.pixel) {
82+
const diff = new BlinkDiff({
8183

8284
imageAPath: actual,
8385
imageBPath: expected,
@@ -87,34 +89,40 @@ export class ImageHelper {
8789
threshold: valueThreshold,
8890
delta: this.delta(),
8991

90-
cropImageA: rect,
92+
cropImageA: this._cropImageRect,
93+
cropImageB: this._cropImageRect,
94+
blockOut: this._blockOutAreas,
9195
verbose: this._args.verbose,
9296
});
9397

9498
const result = this.runDiff(diff, output);
99+
this._blockOutAreas = undefined;
95100
return result;
96101
}
97102

98-
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);
100-
let diff = new blinkDiff({
101-
102-
imageAPath: actual,
103-
imageBPath: expected,
104-
imageOutputPath: output,
105-
imageOutputLimit: this.imageOutputLimit(),
106-
thresholdType: typeThreshold,
107-
threshold: valueThreshold,
108-
delta: this.delta(),
103+
public async clipRectangleImage(rect: IRectangle, path: string) {
104+
let imageToClip: PngJsImage;
105+
imageToClip = await this.readImage(path);
106+
imageToClip.clip(rect.x, rect.y, rect.width, rect.height);
107+
return new Promise((resolve, reject) => {
108+
imageToClip.writeImage(path, (err) => {
109+
if (err) {
110+
return reject(err);
111+
}
112+
return resolve();
113+
});
109114

110-
cropImageA: rectToCrop,
111-
cropImageB: rectToCrop,
112-
blockOut: this._blockOutAreas,
113-
verbose: this._args.verbose,
114-
});
115+
})
116+
}
115117

116-
const result = this.runDiff(diff, output);
117-
this._blockOutAreas = undefined;
118-
return result;
118+
public readImage(path: string): Promise<any> {
119+
return new Promise((resolve, reject) => {
120+
PngJsImage.readImage(path, (err, image) => {
121+
if (err) {
122+
return reject(err);
123+
}
124+
return resolve(image);
125+
});
126+
})
119127
}
120128
}

lib/utils.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ export declare function calculateOffset(direction: any, y: number, yOffset: numb
3333
* @param xOffset
3434
*/
3535
export declare function scroll(wd: any, driver: any, direction: Direction, isIOS: boolean, y: number, x: number, yOffset: number, xOffset: number, verbose: any): Promise<void>;
36+
export declare const addExt: (fileName: string, ext: string) => string;

0 commit comments

Comments
 (0)