@@ -71,7 +71,7 @@ export type Proxy = {
7171 * Lists the address for which the proxy should be bypassed when the proxyType is "manual".
7272 * A List containing any number of Strings.
7373 */
74- noProxy ?: ( number | string ) [ ] ,
74+ noProxy ?: string [ ] ,
7575 /**
7676 * Defines the proxy host for encrypted TLS traffic when the proxyType is "manual".
7777 * A host and optional port for scheme "https".
@@ -308,7 +308,7 @@ export interface Cookie {
308308 secure ?: boolean ;
309309 httpOnly ?: boolean ;
310310 expiry ?: number ;
311- sameSite ?: "Lax" | "Strict" ;
311+ sameSite ?: "Lax" | "Strict" | "None" ;
312312}
313313
314314export type ElementLookup = CSSSelector | LinkText | PartialLinkText | TagName | XPathSelector ;
@@ -416,7 +416,7 @@ export interface ActionSequencePointer {
416416 parameters ?: {
417417 pointerType ?: "mouse" | "pen" | "touch" ;
418418 } ;
419- actions : ( Actions . Pause | Actions . PointerUp | Actions . PointerDown | Actions . PointerMove ) [ ] ;
419+ actions : ( Actions . Pause | Actions . PointerUp | Actions . PointerDown | Actions . PointerMove | Actions . PointerCancel ) [ ] ;
420420}
421421
422422export interface ActionSequenceWheel {
@@ -472,6 +472,19 @@ export interface WebDriverClientOptions {
472472/**
473473 * A [Local end](https://www.w3.org/TR/webdriver2/#nodes) node implementation of the WebDriver specification.
474474 */
475+
476+ // Helpers for safe JSON (de)serialization
477+ function withReplacer ( serializer ?: Serializer ) {
478+ return typeof serializer ?. serialize === "function"
479+ ? ( _k : string , v : any ) => serializer ! . serialize ! ( v )
480+ : undefined ;
481+ }
482+ function withReviver ( serializer ?: Serializer ) {
483+ return typeof serializer ?. deserialize === "function"
484+ ? ( _k : string , v : any ) => serializer ! . deserialize ! ( v )
485+ : undefined ;
486+ }
487+
475488export class WebDriverClient {
476489
477490 public useImplicitSignal = true ;
@@ -515,22 +528,44 @@ export class WebDriverClient {
515528
516529 public async request < Args , Result > ( method : Method , uri : string , args ?: Args , signal ?: AbortSignal , serializer ?: Serializer ) : Promise < Result > {
517530 try {
518- const requestInit : RequestInit = { } ;
519- requestInit . method = method ;
531+ const headers : Record < string , string > = { "Accept" : "application/json" } ;
532+ const requestInit : RequestInit = { method, headers } ;
520533 signal = withImplicitSignal ( signal , this . useImplicitSignal ) ;
521534 if ( signal ) requestInit . signal = signal ;
522- if ( args !== undefined ) requestInit . body = JSON . stringify ( args , serializer ?. serialize && ( ( key , value ) => serializer . serialize ( value ) ) )
535+ if ( args !== undefined ) {
536+ headers [ "Content-Type" ] = "application/json; charset=utf-8" ;
537+ requestInit . body = JSON . stringify ( args , withReplacer ( serializer ) ) ;
538+ }
523539
524- this . log ( `fetch: ${ method } ${ uri } ${ args !== undefined ? " " + requestInit . body . toString ( ) . substring ( 0 , 40 ) : "" } ` ) ;
540+ const bodyStr = typeof requestInit . body === "string" ? requestInit . body . slice ( 0 , 40 ) : "" ;
541+ this . log ( `fetch: ${ method } ${ uri } ${ bodyStr ? " " + bodyStr : "" } ` ) ;
525542 const response = await this . fetchImplementation ( `${ this . options . address } ${ uri } ` , requestInit ) ;
526543
527- this . log ( ` response: ${ method } ${ response . status } ${ response . statusText } ${ uri } ${ args !== undefined ? " " + requestInit . body . toString ( ) . substring ( 0 , 40 ) : "" } ` ) ;
544+ this . log ( ` response: ${ method } ${ response . status } ${ response . statusText } ${ uri } ${ bodyStr ? " " + bodyStr : "" } ` ) ;
545+ let text = "" ;
546+ try { text = await response . text ( ) ; } catch { }
547+
528548 if ( ! response . ok ) {
529- throw new WebDriverRequestError ( await response . json ( ) as ErrorResult ) ;
549+ if ( text ) {
550+ try {
551+ const errJson = JSON . parse ( text ) ;
552+ throw new WebDriverRequestError ( errJson as ErrorResult ) ;
553+ } catch {
554+ throw new WebDriverRequestError ( `${ response . status } ${ response . statusText } ` ) ;
555+ }
556+ }
557+ throw new WebDriverRequestError ( `${ response . status } ${ response . statusText } ` ) ;
558+ }
559+
560+ if ( ! text ) return undefined as unknown as Result ;
561+
562+ let json : any ;
563+ try {
564+ json = JSON . parse ( text , withReviver ( serializer ) ) ;
565+ } catch {
566+ throw new WebDriverRequestError ( "Invalid JSON from WebDriver endpoint." ) ;
530567 }
531568
532- const text = await response . text ( )
533- const json = JSON . parse ( text , serializer ?. deserialize && ( ( key , value ) => serializer . deserialize ( value ) ) ) ;
534569 const result = ( json as { value : Result } ) . value ;
535570 return result ;
536571 } catch ( cause ) {
@@ -611,7 +646,7 @@ export class Session implements Disposable, Serializer {
611646 */
612647 public async setTimeouts ( timeouts : TimeoutsConfiguration , signal ?: AbortSignal ) : Promise < void > {
613648 try {
614- return this . request ( "POST" , `/session/${ this . sessionId } /timeouts` , timeouts , signal ) ;
649+ return await this . request ( "POST" , `/session/${ this . sessionId } /timeouts` , timeouts , signal ) ;
615650 } catch ( cause ) {
616651 throw new WebDriverMethodError ( `Failed to set timeouts.` , { cause } , { timeouts } ) ;
617652 }
@@ -741,7 +776,7 @@ export class Session implements Disposable, Serializer {
741776 const res = await this . request < { type : "tab" | "window" } , { handle : WindowHandle , type : "tab" | "window" } > ( "POST" , `/session/${ this . sessionId } /window/new` , { type } , signal ) ;
742777 return new Window ( this , res . handle , res . type ) ;
743778 } catch ( cause ) {
744- throw new WebDriverMethodError ( `Failed open a new window.` , { cause } , { type } ) ;
779+ throw new WebDriverMethodError ( `Failed to open a new window.` , { cause } , { type } ) ;
745780 }
746781 }
747782
@@ -874,16 +909,16 @@ export class Session implements Disposable, Serializer {
874909 *
875910 * The ***Get Page Source*** command returns a string serialization of the DOM of the current browsing context active document.
876911 */
877- public getPageSource ( signal ?: AbortSignal ) : Promise < string > {
912+ public async getPageSource ( signal ?: AbortSignal ) : Promise < string > {
878913 try {
879- return this . request ( "GET" , `/session/${ this . sessionId } /source` , undefined , signal ) ;
914+ return await this . request ( "GET" , `/session/${ this . sessionId } /source` , undefined , signal ) ;
880915 } catch ( cause ) {
881916 throw new WebDriverMethodError ( `Failed to get page source.` , { cause } ) ;
882917 }
883918 }
884919
885920 /**
886- * [13.2.1 Execute Scrip ](https://www.w3.org/TR/webdriver2/#execute-script)
921+ * [13.2.1 Execute Script ](https://www.w3.org/TR/webdriver2/#execute-script)
887922 */
888923 public async executeScript ( script : string , signal ?: AbortSignal , ...args : any [ ] ) : Promise < any > {
889924 try {
@@ -896,9 +931,9 @@ export class Session implements Disposable, Serializer {
896931 /**
897932 * [13.2.2 Execute Async Script](https://www.w3.org/TR/webdriver2/#execute-async-script)
898933 */
899- public executeScriptAsync ( script : string , signal ?: AbortSignal , ...args : any [ ] ) : Promise < any > {
934+ public async executeScriptAsync ( script : string , signal ?: AbortSignal , ...args : any [ ] ) : Promise < any > {
900935 try {
901- return this . request ( "POST" , `/session/${ this . sessionId } /execute/async` , { script, args } , signal ) ;
936+ return await this . request ( "POST" , `/session/${ this . sessionId } /execute/async` , { script, args } , signal ) ;
902937 } catch ( cause ) {
903938 throw new WebDriverMethodError ( `Failed to execute script async.` , { cause } ) ;
904939 }
@@ -1199,9 +1234,9 @@ export class Element implements WebElementReference {
11991234 /**
12001235 * [12.4.3 Get Element Property](https://www.w3.org/TR/webdriver2/#get-element-property)
12011236 */
1202- public async getProperty ( name : string , signal ?: AbortSignal ) : Promise < null | string > {
1237+ public async getProperty ( name : string , signal ?: AbortSignal ) : Promise < any > {
12031238 try {
1204- return await this . request < { } , null | string > ( "GET" , `/session/${ this . sessionId } /element/${ this . elementId } /property/${ name } ` , undefined , signal ) ;
1239+ return await this . request < { } , any > ( "GET" , `/session/${ this . sessionId } /element/${ this . elementId } /property/${ name } ` , undefined , signal ) ;
12051240 } catch ( cause ) {
12061241 throw new WebDriverMethodError ( `Failed to get property ${ name } from element.` , { cause } , { name } ) ;
12071242 }
@@ -1257,7 +1292,7 @@ export class Element implements WebElementReference {
12571292 try {
12581293 return await this . request < { } , ElementRect > ( "GET" , `/session/${ this . sessionId } /element/${ this . elementId } /rect` , undefined , signal ) ;
12591294 } catch ( cause ) {
1260- throw new WebDriverMethodError ( `Failed to get tag name from element.` , { cause } ) ;
1295+ throw new WebDriverMethodError ( `Failed to get rect from element.` , { cause } ) ;
12611296 }
12621297 }
12631298
@@ -1395,4 +1430,4 @@ export class ShadowRoot implements ShadowRootReference {
13951430 throw error ;
13961431 } ) ;
13971432 }
1398- }
1433+ }
0 commit comments