@@ -37,6 +37,7 @@ const popupStore = new Popup();
3737const consoleLogStore = new Console ( ) ;
3838const availableBrowsers = [ 'chromium' , 'webkit' , 'firefox' ] ;
3939
40+ const { createValueEngine } = require ( './extras/PlaywrightPropEngine' ) ;
4041/**
4142 * Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
4243 *
@@ -46,10 +47,10 @@ const availableBrowsers = ['chromium', 'webkit', 'firefox'];
4647 *
4748 * This helper works with a browser out of the box with no additional tools required to install.
4849 *
49- * Requires `playwright` package version ^0.11.0 to be installed:
50+ * Requires `playwright` package version ^0.12.1 to be installed:
5051 *
5152 * ```
52- * npm i playwright@^0.11.0 --save
53+ * npm i playwright@^0.12.1 --save
5354 * ```
5455 *
5556 * ## Configuration
@@ -250,11 +251,17 @@ class Playwright extends Helper {
250251 try {
251252 requireg ( 'playwright' ) ;
252253 } catch ( e ) {
253- return [ 'playwright@^0.10 ' ] ;
254+ return [ 'playwright@^0.12.1 ' ] ;
254255 }
255256 }
256257
257- _init ( ) {
258+ async _init ( ) {
259+ // register an internal selector engine for reading value property of elements in a selector
260+ try {
261+ await playwright . selectors . register ( '__value' , createValueEngine ) ;
262+ } catch ( e ) {
263+ console . warn ( e ) ;
264+ }
258265 }
259266
260267 _beforeSuite ( ) {
@@ -566,7 +573,7 @@ class Playwright extends Helper {
566573
567574 if ( this . config . basicAuth && ( this . isAuthenticated !== true ) ) {
568575 if ( url . includes ( this . options . url ) ) {
569- await this . page . authenticate ( this . config . basicAuth ) ;
576+ await this . browserContext . setHTTPCredentials ( this . config . basicAuth ) ;
570577 this . isAuthenticated = true ;
571578 }
572579 }
@@ -1529,7 +1536,7 @@ class Playwright extends Helper {
15291536 const array = [ ] ;
15301537
15311538 for ( let index = 0 ; index < els . length ; index ++ ) {
1532- const a = await this . _evaluateHandeInContext ( ( el , attr ) => el [ attr ] || el . getAttribute ( attr ) , els [ index ] , attr ) ;
1539+ const a = await this . _evaluateHandeInContext ( ( [ el , attr ] ) => el [ attr ] || el . getAttribute ( attr ) , [ els [ index ] , attr ] ) ;
15331540 array . push ( await a . jsonValue ( ) ) ;
15341541 }
15351542
@@ -1573,21 +1580,15 @@ class Playwright extends Helper {
15731580 const matcher = await this . context ;
15741581 let waiter ;
15751582 const context = await this . _getContext ( ) ;
1576- if ( locator . isCSS ( ) ) {
1577- const enabledFn = function ( locator ) {
1578- const els = document . querySelectorAll ( locator ) ;
1579- if ( ! els || els . length === 0 ) {
1580- return false ;
1581- }
1582- return Array . prototype . filter . call ( els , el => ! el . disabled ) . length > 0 ;
1583- } ;
1584- waiter = context . waitForFunction ( enabledFn , { timeout : waitTimeout } , locator . value ) ;
1583+ if ( ! locator . isXPath ( ) ) {
1584+ // playwright combined selectors
1585+ waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? `${ locator . type } =${ locator . value } ` : locator . simplify ( ) } >> :not([disabled])` , { timeout : waitTimeout } ) ;
15851586 } else {
1586- const enabledFn = function ( locator , $XPath ) {
1587+ const enabledFn = function ( [ locator , $XPath ] ) {
15871588 eval ( $XPath ) ; // eslint-disable-line no-eval
15881589 return $XPath ( null , locator ) . filter ( el => ! el . disabled ) . length > 0 ;
15891590 } ;
1590- waiter = context . waitForFunction ( enabledFn , { timeout : waitTimeout } , locator . value , $XPath . toString ( ) ) ;
1591+ waiter = context . waitForFunction ( enabledFn , [ locator . value , $XPath . toString ( ) ] , { timeout : waitTimeout } ) ;
15911592 }
15921593 return waiter . catch ( ( err ) => {
15931594 throw new Error ( `element (${ locator . toString ( ) } ) still not enabled after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
@@ -1603,21 +1604,15 @@ class Playwright extends Helper {
16031604 const matcher = await this . context ;
16041605 let waiter ;
16051606 const context = await this . _getContext ( ) ;
1606- if ( locator . isCSS ( ) ) {
1607- const valueFn = function ( locator , value ) {
1608- const els = document . querySelectorAll ( locator ) ;
1609- if ( ! els || els . length === 0 ) {
1610- return false ;
1611- }
1612- return Array . prototype . filter . call ( els , el => ( el . value || '' ) . indexOf ( value ) !== - 1 ) . length > 0 ;
1613- } ;
1614- waiter = context . waitForFunction ( valueFn , { timeout : waitTimeout } , locator . value , value ) ;
1607+ if ( ! locator . isXPath ( ) ) {
1608+ // uses a custom selector engine for finding value properties on elements
1609+ waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? `${ locator . type } =${ locator . value } ` : locator . simplify ( ) } >> __value=${ value } ` , { timeout : waitTimeout , waitFor : 'visible' } ) ;
16151610 } else {
1616- const valueFn = function ( locator , $XPath , value ) {
1611+ const valueFn = function ( [ locator , $XPath , value ] ) {
16171612 eval ( $XPath ) ; // eslint-disable-line no-eval
16181613 return $XPath ( null , locator ) . filter ( el => ( el . value || '' ) . indexOf ( value ) !== - 1 ) . length > 0 ;
16191614 } ;
1620- waiter = context . waitForFunction ( valueFn , { timeout : waitTimeout } , locator . value , $XPath . toString ( ) , value ) ;
1615+ waiter = context . waitForFunction ( valueFn , [ locator . value , $XPath . toString ( ) , value ] , { timeout : waitTimeout } ) ;
16211616 }
16221617 return waiter . catch ( ( err ) => {
16231618 const loc = locator . toString ( ) ;
@@ -1636,20 +1631,20 @@ class Playwright extends Helper {
16361631 let waiter ;
16371632 const context = await this . _getContext ( ) ;
16381633 if ( locator . isCSS ( ) ) {
1639- const visibleFn = function ( locator , num ) {
1634+ const visibleFn = function ( [ locator , num ] ) {
16401635 const els = document . querySelectorAll ( locator ) ;
16411636 if ( ! els || els . length === 0 ) {
16421637 return false ;
16431638 }
16441639 return Array . prototype . filter . call ( els , el => el . offsetParent !== null ) . length === num ;
16451640 } ;
1646- waiter = context . waitForFunction ( visibleFn , { timeout : waitTimeout } , locator . value , num ) ;
1641+ waiter = context . waitForFunction ( visibleFn , [ locator . value , num ] , { timeout : waitTimeout } ) ;
16471642 } else {
1648- const visibleFn = function ( locator , $XPath , num ) {
1643+ const visibleFn = function ( [ locator , $XPath , num ] ) {
16491644 eval ( $XPath ) ; // eslint-disable-line no-eval
16501645 return $XPath ( null , locator ) . filter ( el => el . offsetParent !== null ) . length === num ;
16511646 } ;
1652- waiter = context . waitForFunction ( visibleFn , { timeout : waitTimeout } , locator . value , $XPath . toString ( ) , num ) ;
1647+ waiter = context . waitForFunction ( visibleFn , [ locator . value , $XPath . toString ( ) , num ] , { timeout : waitTimeout } ) ;
16531648 }
16541649 return waiter . catch ( ( err ) => {
16551650 throw new Error ( `The number of elements (${ locator . toString ( ) } ) is not ${ num } after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
@@ -1681,7 +1676,7 @@ class Playwright extends Helper {
16811676 locator = new Locator ( locator , 'css' ) ;
16821677
16831678 const context = await this . _getContext ( ) ;
1684- const waiter = context . waitForSelector ( `${ locator . type } =${ locator . value } ` , { timeout : waitTimeout } ) ;
1679+ const waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? ` ${ locator . type } =${ locator . value } ` : locator . simplify ( ) } ` , { timeout : waitTimeout } ) ;
16851680 return waiter . catch ( ( err ) => {
16861681 throw new Error ( `element (${ locator . toString ( ) } ) still not present on page after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
16871682 } ) ;
@@ -1696,7 +1691,7 @@ class Playwright extends Helper {
16961691 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout ;
16971692 locator = new Locator ( locator , 'css' ) ;
16981693 const context = await this . _getContext ( ) ;
1699- const waiter = context . waitForSelector ( `${ locator . type } =${ locator . value } ` , { timeout : waitTimeout , visibility : 'visible' } ) ;
1694+ const waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? ` ${ locator . type } =${ locator . value } ` : locator . simplify ( ) } ` , { timeout : waitTimeout , waitFor : 'visible' } ) ;
17001695 return waiter . catch ( ( err ) => {
17011696 throw new Error ( `element (${ locator . toString ( ) } ) still not visible after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
17021697 } ) ;
@@ -1709,7 +1704,7 @@ class Playwright extends Helper {
17091704 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout ;
17101705 locator = new Locator ( locator , 'css' ) ;
17111706 const context = await this . _getContext ( ) ;
1712- const waiter = context . waitForSelector ( `${ locator . type } =${ locator . value } ` , { timeout : waitTimeout , visibility : 'hidden' } ) ;
1707+ const waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? ` ${ locator . type } =${ locator . value } ` : locator . simplify ( ) } ` , { timeout : waitTimeout , waitFor : 'hidden' } ) ;
17131708 return waiter . catch ( ( err ) => {
17141709 throw new Error ( `element (${ locator . toString ( ) } ) still visible after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
17151710 } ) ;
@@ -1722,7 +1717,7 @@ class Playwright extends Helper {
17221717 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout ;
17231718 locator = new Locator ( locator , 'css' ) ;
17241719 const context = await this . _getContext ( ) ;
1725- return context . waitForSelector ( `${ locator . type } =${ locator . value } ` , { timeout : waitTimeout , visibility : 'hidden' } ) . catch ( ( err ) => {
1720+ return context . waitForSelector ( `${ locator . isCustom ( ) ? ` ${ locator . type } =${ locator . value } ` : locator . simplify ( ) } ` , { timeout : waitTimeout , waitFor : 'hidden' } ) . catch ( ( err ) => {
17261721 throw new Error ( `element (${ locator . toString ( ) } ) still not hidden after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
17271722 } ) ;
17281723 }
@@ -1743,7 +1738,7 @@ class Playwright extends Helper {
17431738 return this . page . waitForFunction ( ( urlPart ) => {
17441739 const currUrl = decodeURIComponent ( decodeURIComponent ( decodeURIComponent ( window . location . href ) ) ) ;
17451740 return currUrl . indexOf ( urlPart ) > - 1 ;
1746- } , { timeout : waitTimeout } , urlPart ) . catch ( async ( e ) => {
1741+ } , urlPart , { timeout : waitTimeout } ) . catch ( async ( e ) => {
17471742 const currUrl = await this . _getPageUrl ( ) ; // Required because the waitForFunction can't return data.
17481743 if ( / f a i l e d : t i m e o u t / i. test ( e . message ) ) {
17491744 throw new Error ( `expected url to include ${ urlPart } , but found ${ currUrl } ` ) ;
@@ -1767,7 +1762,7 @@ class Playwright extends Helper {
17671762 return this . page . waitForFunction ( ( urlPart ) => {
17681763 const currUrl = decodeURIComponent ( decodeURIComponent ( decodeURIComponent ( window . location . href ) ) ) ;
17691764 return currUrl . indexOf ( urlPart ) > - 1 ;
1770- } , { timeout : waitTimeout } , urlPart ) . catch ( async ( e ) => {
1765+ } , urlPart , { timeout : waitTimeout } ) . catch ( async ( e ) => {
17711766 const currUrl = await this . _getPageUrl ( ) ; // Required because the waitForFunction can't return data.
17721767 if ( / f a i l e d : t i m e o u t / i. test ( e . message ) ) {
17731768 throw new Error ( `expected url to be ${ urlPart } , but found ${ currUrl } ` ) ;
@@ -1788,26 +1783,21 @@ class Playwright extends Helper {
17881783
17891784 if ( context ) {
17901785 const locator = new Locator ( context , 'css' ) ;
1791- if ( locator . isCSS ( ) ) {
1792- waiter = contextObject . waitForFunction ( ( locator , text ) => {
1793- const el = document . querySelector ( locator ) ;
1794- if ( ! el ) return false ;
1795- return el . innerText . indexOf ( text ) > - 1 ;
1796- } , { timeout : waitTimeout } , locator . value , text ) ;
1786+ if ( ! locator . isXPath ( ) ) {
1787+ waiter = contextObject . waitFor ( `${ locator . isCustom ( ) ? `${ locator . type } =${ locator . value } ` : locator . simplify ( ) } >> text=${ text } ` , { timeout : waitTimeout , waitFor : 'visible' } ) ;
17971788 }
17981789
17991790 if ( locator . isXPath ( ) ) {
1800- waiter = contextObject . waitForFunction ( ( locator , text , $XPath ) => {
1791+ waiter = contextObject . waitForFunction ( ( [ locator , text , $XPath ] ) => {
18011792 eval ( $XPath ) ; // eslint-disable-line no-eval
18021793 const el = $XPath ( null , locator ) ;
18031794 if ( ! el . length ) return false ;
18041795 return el [ 0 ] . innerText . indexOf ( text ) > - 1 ;
1805- } , { timeout : waitTimeout } , locator . value , text , $XPath . toString ( ) ) ;
1796+ } , [ locator . value , text , $XPath . toString ( ) ] , { timeout : waitTimeout } ) ;
18061797 }
18071798 } else {
1808- waiter = contextObject . waitForFunction ( text => document . body && document . body . innerText . indexOf ( text ) > - 1 , { timeout : waitTimeout } , text ) ;
1799+ waiter = contextObject . waitForFunction ( text => document . body && document . body . innerText . indexOf ( text ) > - 1 , text , { timeout : waitTimeout } ) ;
18091800 }
1810-
18111801 return waiter . catch ( ( err ) => {
18121802 throw new Error ( `Text "${ text } " was not found on page after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
18131803 } ) ;
@@ -1897,7 +1887,7 @@ class Playwright extends Helper {
18971887 }
18981888 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout ;
18991889 const context = await this . _getContext ( ) ;
1900- return context . waitForFunction ( fn , { timeout : waitTimeout } , ... args ) ;
1890+ return context . waitForFunction ( fn , args , { timeout : waitTimeout } ) ;
19011891 }
19021892
19031893 /**
@@ -1942,17 +1932,14 @@ class Playwright extends Helper {
19421932
19431933 let waiter ;
19441934 const context = await this . _getContext ( ) ;
1945- if ( locator . isCSS ( ) ) {
1946- const visibleFn = function ( locator ) {
1947- return document . querySelector ( locator ) === null ;
1948- } ;
1949- waiter = context . waitForFunction ( visibleFn , { timeout : waitTimeout } , locator . value ) ;
1935+ if ( ! locator . isXPath ( ) ) {
1936+ waiter = context . waitForSelector ( `${ locator . isCustom ( ) ? `${ locator . type } =${ locator . value } ` : locator . simplify ( ) } ` , { timeout : waitTimeout , waitFor : 'detached' } ) ;
19501937 } else {
1951- const visibleFn = function ( locator , $XPath ) {
1938+ const visibleFn = function ( [ locator , $XPath ] ) {
19521939 eval ( $XPath ) ; // eslint-disable-line no-eval
19531940 return $XPath ( null , locator ) . length === 0 ;
19541941 } ;
1955- waiter = context . waitForFunction ( visibleFn , { timeout : waitTimeout } , locator . value , $XPath . toString ( ) ) ;
1942+ waiter = context . waitForFunction ( visibleFn , [ locator . value , $XPath . toString ( ) ] , { timeout : waitTimeout } ) ;
19561943 }
19571944 return waiter . catch ( ( err ) => {
19581945 throw new Error ( `element (${ locator . toString ( ) } ) still on page after ${ waitTimeout / 1000 } sec\n${ err . message } ` ) ;
@@ -1986,7 +1973,11 @@ module.exports = Playwright;
19861973
19871974async function findElements ( matcher , locator ) {
19881975 locator = new Locator ( locator , 'css' ) ;
1989- if ( ! locator . isXPath ( ) ) return matcher . $$ ( locator . simplify ( ) ) ;
1976+ if ( locator . isCustom ( ) ) {
1977+ return matcher . $$ ( `${ locator . type } =${ locator . value } ` ) ;
1978+ } if ( ! locator . isXPath ( ) ) {
1979+ return matcher . $$ ( locator . simplify ( ) ) ;
1980+ }
19901981 return matcher . $$ ( `xpath=${ locator . value } ` ) ;
19911982}
19921983
0 commit comments