1
- import { expect , Locator , Page } from '@playwright/test' ;
1
+ import { expect , Locator , Page , test } from '@playwright/test' ;
2
2
import type { ElementHandle } from 'playwright' ;
3
3
4
4
interface VisibilityOptions {
@@ -25,6 +25,29 @@ interface ClickWithTextOptions {
25
25
parentSelector ?: string ;
26
26
isTargetChanged ?: boolean ;
27
27
index ?: number ;
28
+ wait ?: number ;
29
+ }
30
+
31
+ interface BrowserAlertOptions {
32
+ selector : string ;
33
+ alertMessage ?: string ;
34
+ isEqual ?: boolean ;
35
+ index ?: number ;
36
+ parentSelector ?: string ;
37
+ wait ?: number ;
38
+ }
39
+
40
+ interface BrowserAlertForMultipleHostsOptions extends BrowserAlertOptions {
41
+ host : number ;
42
+ }
43
+
44
+ interface CompareHostsOptions {
45
+ selector : string ;
46
+ extraHost : number ;
47
+ isEqual ?: boolean ;
48
+ index ?: number ;
49
+ clickSelector ?: string ;
50
+ wait ?: number ;
28
51
}
29
52
30
53
interface ElementContainTextOptions {
@@ -68,8 +91,16 @@ export class BaseMethods {
68
91
constructor ( protected readonly page : Page ) { }
69
92
70
93
private resolveLocator ( selector : string , options : { parentSelector ?: string ; text ?: string ; index ?: number } = { } ) : Locator {
94
+ return this . resolveLocatorForPage ( this . page , selector , options ) ;
95
+ }
96
+
97
+ private resolveLocatorForPage (
98
+ page : Page ,
99
+ selector : string ,
100
+ options : { parentSelector ?: string ; text ?: string ; index ?: number } = { } ,
101
+ ) : Locator {
71
102
const { parentSelector, text, index } = options ;
72
- let locator = parentSelector ? this . page . locator ( parentSelector ) . locator ( selector ) : this . page . locator ( selector ) ;
103
+ let locator = parentSelector ? page . locator ( parentSelector ) . locator ( selector ) : page . locator ( selector ) ;
73
104
74
105
if ( text ) {
75
106
locator = locator . filter ( { hasText : text } ) ;
@@ -200,7 +231,7 @@ export class BaseMethods {
200
231
await expect ( locator ) . not . toHaveCount ( 0 ) ;
201
232
}
202
233
203
- async clickElementWithText ( { selector, text, parentSelector, isTargetChanged = false , index } : ClickWithTextOptions ) : Promise < void > {
234
+ async clickElementWithText ( { selector, text, parentSelector, isTargetChanged = false , index, wait } : ClickWithTextOptions ) : Promise < void > {
204
235
const locator = this . resolveLocator ( selector , { parentSelector, text, index } ) ;
205
236
const element = locator . first ( ) ;
206
237
@@ -209,6 +240,10 @@ export class BaseMethods {
209
240
}
210
241
211
242
await element . click ( ) ;
243
+
244
+ if ( wait && wait > 0 ) {
245
+ await this . page . waitForTimeout ( wait ) ;
246
+ }
212
247
}
213
248
214
249
async checkElementContainText ( { selector, text, isContain = true , index, parentSelector } : ElementContainTextOptions ) : Promise < void > {
@@ -364,30 +399,190 @@ export class BaseMethods {
364
399
await poller . not . toContain ( urlPart ) ;
365
400
}
366
401
402
+ skipTestByCondition ( condition : unknown , reason : string = 'Skipped by condition' ) : void {
403
+ if ( condition ) {
404
+ test . info ( ) . skip ( reason ) ;
405
+ }
406
+ }
407
+
408
+ async checkBrowserAlertByText ( {
409
+ selector,
410
+ alertMessage,
411
+ isEqual = true ,
412
+ index = 0 ,
413
+ parentSelector,
414
+ wait = 0 ,
415
+ } : BrowserAlertOptions ) : Promise < void > {
416
+ const locator = this . resolveLocator ( selector , { parentSelector, index } ) ;
417
+
418
+ if ( wait > 0 ) {
419
+ await this . page . waitForTimeout ( wait ) ;
420
+ }
421
+
422
+ const message = await this . captureDialogMessage ( this . page , locator . first ( ) ) ;
423
+
424
+ if ( alertMessage !== undefined ) {
425
+ if ( isEqual ) {
426
+ expect ( message ) . toBe ( alertMessage ) ;
427
+ } else {
428
+ expect ( message ) . not . toBe ( alertMessage ) ;
429
+ }
430
+ }
431
+ }
432
+
433
+ async checkBrowserAlertForMultipleHosts ( {
434
+ selector,
435
+ alertMessage,
436
+ isEqual = true ,
437
+ index = 0 ,
438
+ parentSelector,
439
+ host,
440
+ wait = 0 ,
441
+ } : BrowserAlertForMultipleHostsOptions ) : Promise < void > {
442
+ const baseGroup = this . resolveLocator ( selector , { parentSelector } ) ;
443
+ const baseCount = await baseGroup . count ( ) ;
444
+
445
+ if ( baseCount === 0 ) {
446
+ throw new Error ( `No elements found for selector "${ selector } " on the base page.` ) ;
447
+ }
448
+
449
+ const targetIndex = Math . min ( index , baseCount - 1 ) ;
450
+
451
+ if ( wait > 0 ) {
452
+ await this . page . waitForTimeout ( wait ) ;
453
+ }
454
+
455
+ const baseMessage = await this . captureDialogMessage ( this . page , baseGroup . nth ( targetIndex ) ) ;
456
+
457
+ if ( alertMessage !== undefined ) {
458
+ if ( isEqual ) {
459
+ expect ( baseMessage ) . toBe ( alertMessage ) ;
460
+ } else {
461
+ expect ( baseMessage ) . not . toBe ( alertMessage ) ;
462
+ }
463
+ }
464
+
465
+ const remotePage = await this . page . context ( ) . newPage ( ) ;
466
+
467
+ try {
468
+ await remotePage . goto ( `http://localhost:${ host } /` , { waitUntil : 'networkidle' } ) ;
469
+
470
+ const remoteGroup = this . resolveLocatorForPage ( remotePage , selector , { parentSelector } ) ;
471
+ const remoteCount = await remoteGroup . count ( ) ;
472
+
473
+ if ( remoteCount === 0 ) {
474
+ throw new Error ( `No elements found for selector "${ selector } " on host ${ host } .` ) ;
475
+ }
476
+
477
+ const remoteIndex = Math . min ( targetIndex , remoteCount - 1 ) ;
478
+ const remoteMessage = await this . captureDialogMessage ( remotePage , remoteGroup . nth ( remoteIndex ) ) ;
479
+
480
+ if ( wait > 0 ) {
481
+ await remotePage . waitForTimeout ( wait ) ;
482
+ }
483
+
484
+ if ( isEqual ) {
485
+ if ( alertMessage !== undefined ) {
486
+ expect ( remoteMessage ) . toBe ( alertMessage ) ;
487
+ }
488
+
489
+ expect ( remoteMessage ) . toBe ( baseMessage ) ;
490
+ } else {
491
+ if ( alertMessage !== undefined ) {
492
+ expect ( remoteMessage ) . not . toBe ( alertMessage ) ;
493
+ }
494
+
495
+ expect ( remoteMessage ) . not . toBe ( baseMessage ) ;
496
+ }
497
+ } finally {
498
+ await remotePage . close ( ) ;
499
+ }
500
+ }
501
+
367
502
async compareInfoBetweenHosts (
368
- selector : string ,
369
- extraHost : number ,
370
- isEqual : boolean = true ,
371
- index : number = 0 ,
372
- clickSelector ?: string ,
373
- wait : number = 0 ,
503
+ selectorOrOptions : string | CompareHostsOptions ,
504
+ extraHostArg ? : number ,
505
+ isEqualArg : boolean = true ,
506
+ indexArg : number = 0 ,
507
+ clickSelectorArg ?: string ,
508
+ waitArg : number = 0 ,
374
509
) : Promise < void > {
375
- const baseLocator = this . page . locator ( selector ) . nth ( index ) ;
376
- const baseText = ( await baseLocator . innerText ( ) ) . trim ( ) ;
510
+ let selector : string ;
511
+ let extraHost : number ;
512
+ let isEqual : boolean ;
513
+ let index : number ;
514
+ let clickSelector : string | undefined ;
515
+ let wait : number ;
516
+
517
+ if ( typeof selectorOrOptions === 'string' ) {
518
+ selector = selectorOrOptions ;
519
+ if ( typeof extraHostArg !== 'number' ) {
520
+ throw new Error ( 'The "extraHost" parameter must be provided when using the positional signature.' ) ;
521
+ }
522
+ extraHost = extraHostArg ;
523
+ isEqual = isEqualArg ;
524
+ index = indexArg ;
525
+ clickSelector = clickSelectorArg ;
526
+ wait = waitArg ;
527
+ } else {
528
+ ( { selector, extraHost, isEqual = true , index = 0 , clickSelector, wait = 0 } = selectorOrOptions ) ;
529
+ }
530
+
531
+ const baseGroup = this . page . locator ( selector ) ;
532
+ const baseCount = await baseGroup . count ( ) ;
533
+
534
+ if ( baseCount === 0 ) {
535
+ throw new Error ( `No elements found for selector "${ selector } " on the base page.` ) ;
536
+ }
537
+
538
+ const targetIndex = Math . min ( index , baseCount - 1 ) ;
539
+
540
+ if ( wait > 0 ) {
541
+ await this . page . waitForTimeout ( wait ) ;
542
+ }
543
+
544
+ const baseText = ( await baseGroup . nth ( targetIndex ) . innerText ( ) ) . trim ( ) ;
377
545
378
546
const remotePage = await this . page . context ( ) . newPage ( ) ;
379
547
380
548
try {
381
549
await remotePage . goto ( `http://localhost:${ extraHost } /` , { waitUntil : 'networkidle' } ) ;
382
550
383
551
if ( clickSelector ) {
384
- await remotePage . locator ( clickSelector ) . click ( ) ;
552
+ const remoteClickGroup = remotePage . locator ( clickSelector ) ;
553
+ const remoteClickCount = await remoteClickGroup . count ( ) ;
554
+
555
+ if ( remoteClickCount === 0 ) {
556
+ throw new Error ( `No elements found for selector "${ clickSelector } " on host ${ extraHost } .` ) ;
557
+ }
558
+
559
+ const clickIndex = Math . min ( targetIndex , remoteClickCount - 1 ) ;
560
+
561
+ try {
562
+ await this . captureDialogMessage ( remotePage , remoteClickGroup . nth ( clickIndex ) ) ;
563
+ } catch ( error ) {
564
+ const isTimeoutError = error instanceof Error && / T i m e o u t / . test ( error . message ) ;
565
+ if ( isTimeoutError ) {
566
+ await remoteClickGroup . nth ( clickIndex ) . click ( ) ;
567
+ } else {
568
+ throw error ;
569
+ }
570
+ }
571
+
385
572
if ( wait > 0 ) {
386
573
await remotePage . waitForTimeout ( wait ) ;
387
574
}
388
575
}
389
576
390
- const remoteText = ( await remotePage . locator ( selector ) . nth ( index ) . innerText ( ) ) . trim ( ) ;
577
+ const remoteGroup = remotePage . locator ( selector ) ;
578
+ const remoteCount = await remoteGroup . count ( ) ;
579
+
580
+ if ( remoteCount === 0 ) {
581
+ throw new Error ( `No elements found for selector "${ selector } " on host ${ extraHost } .` ) ;
582
+ }
583
+
584
+ const remoteIndex = Math . min ( targetIndex , remoteCount - 1 ) ;
585
+ const remoteText = ( await remoteGroup . nth ( remoteIndex ) . innerText ( ) ) . trim ( ) ;
391
586
392
587
if ( isEqual ) {
393
588
expect ( remoteText ) . toBe ( baseText ) ;
@@ -414,4 +609,16 @@ export class BaseMethods {
414
609
return style . getPropertyValue ( property as string ) ;
415
610
} , prop ) ;
416
611
}
612
+
613
+ private async captureDialogMessage ( page : Page , locator : Locator ) : Promise < string > {
614
+ const [ dialog ] = await Promise . all ( [
615
+ page . waitForEvent ( 'dialog' , { timeout : 5_000 } ) ,
616
+ locator . click ( ) ,
617
+ ] ) ;
618
+
619
+ const message = dialog . message ( ) ;
620
+ await dialog . accept ( ) ;
621
+
622
+ return message ;
623
+ }
417
624
}
0 commit comments