@@ -22,7 +22,11 @@ import {
22
22
VM ,
23
23
ScreenPosition ,
24
24
SimplePosition ,
25
- findRectangle
25
+ calibrationQRCodesGenerate ,
26
+ calibrationQRCodesScan ,
27
+ pngToDataURI ,
28
+ CalibrationQRCodesConfig ,
29
+ MouseButton
26
30
} from "vm-providers" ;
27
31
import { ElementHandle , Frame , Page } from "playwright-core" ;
28
32
import { createWriteStream } from "fs" ;
@@ -44,45 +48,14 @@ export class CalibrationError {
44
48
* to give details allowing to understand why the calibration failed.
45
49
*
46
50
* @param screenshot - Cf {@link CalibrationError.screenshot | screenshot}
47
- * @param color - Cf {@link CalibrationError.color | color}
48
- * @param expectedWidth - Cf {@link CalibrationError.expectedWidth | expectedWidth}
49
- * @param expectedHeight - Cf {@link CalibrationError.expectedHeight | expectedHeight}
50
- * @param colorTolerance - Cf {@link CalibrationError.colorTolerance | colorTolerance}
51
51
*/
52
52
constructor (
53
53
/**
54
54
* Screenshot of the virtual machine, in which the browser viewport could not be found.
55
55
*/
56
- public screenshot : PNG ,
57
- /**
58
- * Color of the rectangle displayed in the viewport.
59
- */
60
- public color : Color ,
61
- /**
62
- * Expected width (in pixels) of the rectangle that was looked for in the screenshot.
63
- */
64
- public expectedWidth : number ,
65
- /**
66
- * Expected height (in pixels) of the rectangle that was looked for in the screenshot.
67
- */
68
- public expectedHeight : number ,
69
- /**
70
- * Tolerance on the color that was used (as configured in {@link CalibrationOptions.colorTolerance}).
71
- */
72
- public colorTolerance : number
56
+ public screenshot : PNG
73
57
) { }
74
58
75
- /**
76
- * Error message.
77
- */
78
- get message ( ) : string {
79
- return `Could not find the ${ this . expectedWidth } x${
80
- this . expectedHeight
81
- } rectangle filled with color ${ JSON . stringify ( this . color ) } (tolerance ${
82
- this . colorTolerance
83
- } )`;
84
- }
85
-
86
59
/**
87
60
* Saves the {@link CalibrationError.screenshot | screenshot} as a file.
88
61
* @param fileName - Full path (or path relative to the current directory) where to store the screenshot.
@@ -181,56 +154,15 @@ export class CalibrationResult implements ScreenPosition {
181
154
}
182
155
}
183
156
184
- /**
185
- * Color, expressed as an array of three numbers between 0 and 255,
186
- * representing the red, green and blue parts
187
- * @public
188
- */
189
- export type Color = [ number , number , number ] ;
190
- const rgb = ( color : Color ) => `rgb(${ color [ 0 ] } ,${ color [ 1 ] } ,${ color [ 2 ] } )` ;
191
-
192
157
/**
193
158
* Options for the calibration, passed to {@link playwrightCalibrate}.
194
159
* @public
195
160
*/
196
- export interface CalibrationOptions {
197
- /**
198
- * Color of the rectangle to display in the viewport.
199
- * Defaults to `[255, 0, 0]` (red)
200
- * @defaultValue [255, 0, 0]
201
- */
202
- calibrationColor ?: Color ;
161
+ export interface CalibrationOptions extends CalibrationQRCodesConfig {
203
162
/**
204
- * Color of the border of the rectangle to be displayed in the viewport.
205
- * Defaults to `[100, 100, 100]`
206
- * @defaultValue [100, 100, 100]
163
+ * Whether to skip the click done during calibration.
207
164
*/
208
- borderColor ?: Color ;
209
- /**
210
- * Width (in pixels) of the border of the rectangle to be displayed in the viewport.
211
- * Defaults to `30`.
212
- * @defaultValue 30
213
- */
214
- borderWidth ?: number ;
215
- /**
216
- * Allowed difference between the color in the screenshot and {@link CalibrationOptions.calibrationColor | calibrationColor}, 0 meaning no difference.
217
- * The difference is computed as the sum of the absolute value of the difference for each red, green and blue parts.
218
- * Defaults to `[255, 0, 0]`.
219
- * @defaultValue [255, 0, 0]
220
- */
221
- colorTolerance ?: number ;
222
- /**
223
- * Extra horizontal space to add at the right of the colored rectangle.
224
- * Defaults to `0`.
225
- * @defaultValue 0
226
- */
227
- estimatedXMargin ?: number ;
228
- /**
229
- * Extra vertical space to add at the bottom of the colored rectangle.
230
- * Defaults to `0`.
231
- * @defaultValue 0
232
- */
233
- estimatedYMargin ?: number ;
165
+ skipClick ?: boolean ;
234
166
}
235
167
236
168
/**
@@ -248,16 +180,7 @@ export async function playwrightCalibrate(
248
180
frame : Page | Frame ,
249
181
options : CalibrationOptions = { }
250
182
) : Promise < CalibrationResult > {
251
- const {
252
- calibrationColor = [ 255 , 0 , 0 ] ,
253
- borderColor = [ 100 , 100 , 100 ] ,
254
- borderWidth = 30 ,
255
- colorTolerance = 50 ,
256
- estimatedXMargin = 0 ,
257
- estimatedYMargin = 0
258
- } = options ;
259
- const rgbCalibrationColor = rgb ( calibrationColor ) ;
260
- const rgbBorderColor = rgb ( borderColor ) ;
183
+ const { skipClick, ...qrCodeOptions } = options ;
261
184
const elementId = JSON . stringify ( createUUIDv4 ( ) ) ;
262
185
const displayRectangleResult : {
263
186
width : number ;
@@ -269,9 +192,7 @@ export async function playwrightCalibrate(
269
192
} = await frame . evaluate ( `(() => {
270
193
const div = document.createElement("div");
271
194
div.setAttribute("id", ${ elementId } );
272
- div.style.cssText = "display:block;position:fixed;background-color:${ rgbCalibrationColor } ;border:${ borderWidth } px solid ${ rgbBorderColor } ;left:0px;top:0px;right:0px;bottom:0px;cursor:none;z-index:999999;";
273
- div.style.maxWidth = (screen.availWidth - window.screenX - ${ estimatedXMargin } ) + "px";
274
- div.style.maxHeight = (screen.availHeight - window.screenY - ${ estimatedYMargin } ) + "px";
195
+ div.style.cssText = "display:block;position:fixed;left:0px;top:0px;right:0px;bottom:0px;cursor:none;z-index:999999;";
275
196
document.body.appendChild(div);
276
197
return {
277
198
width: div.clientWidth,
@@ -282,7 +203,17 @@ return {
282
203
screenHeight: window.screen.height
283
204
};})()` ) ;
284
205
try {
285
- // moves the mouse out of the colored zone
206
+ const viewportImage = calibrationQRCodesGenerate (
207
+ displayRectangleResult . width ,
208
+ displayRectangleResult . height ,
209
+ qrCodeOptions
210
+ ) ;
211
+ await frame . evaluate (
212
+ `(() => {const calibrationDIV = document.getElementById(${ elementId } ); calibrationDIV.style.backgroundImage = ${ JSON . stringify (
213
+ `url("${ await pngToDataURI ( viewportImage ) } ")`
214
+ ) } ;})()`
215
+ ) ;
216
+ // moves the mouse out of the way
286
217
await vm . sendMouseMoveEvent ( {
287
218
x : displayRectangleResult . screenX ,
288
219
y : displayRectangleResult . screenY ,
@@ -291,30 +222,41 @@ return {
291
222
} ) ;
292
223
await wait ( 1000 ) ;
293
224
const image = await vm . takePNGScreenshot ( ) ;
294
- const calibrationResult = findRectangle (
295
- image ,
296
- [ ...calibrationColor , 255 ] ,
297
- displayRectangleResult . width ,
298
- displayRectangleResult . height ,
299
- colorTolerance
300
- ) ;
301
- if ( ! calibrationResult ) {
302
- throw new CalibrationError (
303
- image ,
304
- calibrationColor ,
305
- displayRectangleResult . width ,
306
- displayRectangleResult . height ,
307
- colorTolerance
225
+ try {
226
+ const result = calibrationQRCodesScan ( image , qrCodeOptions ) ;
227
+ if ( ! skipClick ) {
228
+ // click on the detected QR code:
229
+ await vm . sendMouseMoveEvent ( {
230
+ x : Math . floor ( result . qrCode . x + result . qrCode . width / 2 ) ,
231
+ y : Math . floor ( result . qrCode . y + result . qrCode . height / 2 ) ,
232
+ screenWidth : image . width ,
233
+ screenHeight : image . height
234
+ } ) ;
235
+ await wait ( 100 ) ;
236
+ await vm . sendMouseDownEvent ( MouseButton . LEFT ) ;
237
+ await wait ( 50 ) ;
238
+ await vm . sendMouseUpEvent ( MouseButton . LEFT ) ;
239
+ await wait ( 100 ) ;
240
+ // moves again the mouse out of the way
241
+ await vm . sendMouseMoveEvent ( {
242
+ x : displayRectangleResult . screenX ,
243
+ y : displayRectangleResult . screenY ,
244
+ screenWidth : image . width ,
245
+ screenHeight : image . height
246
+ } ) ;
247
+ await wait ( 100 ) ;
248
+ }
249
+ return new CalibrationResult (
250
+ result . viewport . x - displayRectangleResult . screenX ,
251
+ result . viewport . y - displayRectangleResult . screenY ,
252
+ image . width ,
253
+ image . height ,
254
+ vm ,
255
+ frame
308
256
) ;
257
+ } catch ( error ) {
258
+ throw new CalibrationError ( image ) ;
309
259
}
310
- return new CalibrationResult (
311
- calibrationResult . x - borderWidth - displayRectangleResult . screenX ,
312
- calibrationResult . y - borderWidth - displayRectangleResult . screenY ,
313
- image . width ,
314
- image . height ,
315
- vm ,
316
- frame
317
- ) ;
318
260
} finally {
319
261
await frame . evaluate (
320
262
`(() => {const calibrationDIV = document.getElementById(${ elementId } ); calibrationDIV.parentNode.removeChild(calibrationDIV);})()`
0 commit comments