1- import { expect , Locator , Page } from '@playwright/test' ;
1+ import {
2+ expect , JSHandle , Locator , Page ,
3+ } from '@playwright/test' ;
24
35export type SpecialSelector = { modifier : 'first' } | { modifier : 'last' } | {
46 modifier : 'contains' ,
@@ -93,6 +95,9 @@ export type Action = AssertActions | {
9395 | 'port'
9496 | 'protocol'
9597 | 'search'
98+ } | {
99+ type : 'handle' ,
100+ global : 'window' | 'document' ,
96101} ;
97102
98103let queue : Array < Action > = [ ] ;
@@ -115,7 +120,10 @@ function resolveSelectorItem(parent: Locator | Page, selector: Selector[number])
115120 }
116121}
117122
118- export type Subject = { type : 'locator' , value : Locator } | { type : 'value' , value : unknown } ;
123+ export type Subject =
124+ { type : 'locator' , value : Locator }
125+ | { type : 'value' , value : unknown }
126+ | { type : 'handle' , value : JSHandle } ;
119127
120128export async function evaluateAction (
121129 page : Page ,
@@ -124,6 +132,16 @@ export async function evaluateAction(
124132 aliasMap : Record < string , Subject > ,
125133) : Promise < Subject > {
126134 switch ( action . type ) {
135+ case 'handle' : {
136+ switch ( action . global ) {
137+ case 'window' :
138+ return { type : 'handle' , value : await page . evaluateHandle ( ( ) => window ) } ;
139+ case 'document' :
140+ return { type : 'handle' , value : await page . evaluateHandle ( ( ) => window . document ) } ;
141+ default :
142+ throw new Error ( 'Unknown handle value' ) ;
143+ }
144+ }
127145 case 'alias' : {
128146 // eslint-disable-next-line no-param-reassign
129147 aliasMap [ action . name ] = subject ;
@@ -195,13 +213,30 @@ export async function evaluateAction(
195213 } else {
196214 expect ( subject . value ) . toContain ( action . value ) ;
197215 }
198- } else if ( action . negation ) {
199- await expect ( subject . value ) . not . toContainText ( action . value ) ;
200- } else {
201- await expect ( subject . value ) . toContainText ( action . value ) ;
202216 }
203- break ;
217+ if ( subject . type === 'locator' ) {
218+ if ( action . negation ) {
219+ await expect ( subject . value ) . not . toContainText ( action . value ) ;
220+ } else {
221+ await expect ( subject . value ) . toContainText ( action . value ) ;
222+ }
223+ break ;
224+ }
225+ throw new Error ( 'Handle subject is not implemented' ) ;
204226 case 'property' :
227+ if ( subject . type === 'handle' ) {
228+ const result = await page . evaluate (
229+ ( ctx ) => Object . prototype . hasOwnProperty . call ( ctx . subject , ctx . property ) ,
230+ { subject : subject . value , property : action . value } ,
231+ ) ;
232+ if ( action . negation ) {
233+ expect ( result ) . not . toBe ( true ) ;
234+ } else {
235+ expect ( result ) . toBe ( true ) ;
236+ }
237+ break ;
238+ }
239+
205240 if ( action . negation ) {
206241 expect ( subject . value ) . not . toHaveProperty ( action . value ) ;
207242 } else {
@@ -229,13 +264,17 @@ export async function evaluateAction(
229264 expect ( Object . keys ( subject . value as any ) ) . not . toHaveLength ( 0 ) ;
230265 break ;
231266 }
232- } else if ( action . negation ) {
233- await expect ( subject . value ) . not . toBeEmpty ( ) ;
267+ }
268+ if ( subject . type === 'locator' ) {
269+ if ( action . negation ) {
270+ await expect ( subject . value ) . not . toBeEmpty ( ) ;
271+ break ;
272+ } else {
273+ await expect ( subject . value ) . toBeEmpty ( ) ;
274+ }
234275 break ;
235- } else {
236- await expect ( subject . value ) . toBeEmpty ( ) ;
237276 }
238- break ;
277+ throw new Error ( 'Handle subject is not implemented' ) ;
239278 case 'equal' :
240279 if ( action . negation ) {
241280 expect ( subject . value ) . not . toBe ( action . value ) ;
0 commit comments