2022-03-11 08:00:46 -08:00
/ * *
* Copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2023-09-08 14:23:35 -07:00
import type { Fixtures , Locator , Page , BrowserContextOptions , PlaywrightTestArgs , PlaywrightTestOptions , PlaywrightWorkerArgs , PlaywrightWorkerOptions , BrowserContext } from 'playwright/test' ;
2024-01-14 08:41:40 -08:00
import type { Component , JsxComponent , MountOptions , ObjectComponentOptions } from '../types/component' ;
2023-09-08 14:23:35 -07:00
import type { ContextReuseMode , FullConfigInternal } from '../../playwright/src/common/config' ;
2024-01-14 08:41:40 -08:00
import type { ImportRef } from './injected/importRegistry' ;
import { wrapObject } from './injected/serializers' ;
2022-05-09 06:44:20 -08:00
let boundCallbacksForMount : Function [ ] = [ ] ;
2022-03-11 08:00:46 -08:00
2022-07-30 05:07:23 +02:00
interface MountResult extends Locator {
2022-08-04 03:14:00 +02:00
unmount ( locator : Locator ) : Promise < void > ;
2022-10-11 04:56:33 +02:00
update ( options : Omit < MountOptions , 'hooksConfig' > | string | JsxComponent ) : Promise < void > ;
2022-07-30 05:07:23 +02:00
}
2024-01-12 20:02:27 -08:00
type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
mount : ( component : any , options : any ) = > Promise < MountResult > ;
} ;
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker : { context : BrowserContext | undefined , hash : string } } ;
type BaseTestFixtures = {
_contextFactory : ( options? : BrowserContextOptions ) = > Promise < BrowserContext > ,
2024-02-20 09:56:33 -08:00
_optionContextReuseMode : ContextReuseMode
2024-01-12 20:02:27 -08:00
} ;
export const fixtures : Fixtures < TestFixtures , WorkerFixtures , BaseTestFixtures > = {
2024-02-20 09:56:33 -08:00
_optionContextReuseMode : 'when-possible' ,
2022-07-12 13:30:24 -08:00
2024-01-12 20:02:27 -08:00
serviceWorkers : 'block' ,
2022-07-12 13:30:24 -08:00
2024-01-12 20:02:27 -08:00
_ctWorker : [ { context : undefined , hash : '' } , { scope : 'worker' } ] ,
2022-05-31 15:59:36 -07:00
2024-01-12 20:02:27 -08:00
page : async ( { page } , use , info ) = > {
if ( ! ( ( info as any ) . _configInternal as FullConfigInternal ) . defineConfigWasUsed )
throw new Error ( 'Component testing requires the use of the defineConfig() in your playwright-ct.config.{ts,js}: https://aka.ms/playwright/ct-define-config' ) ;
await ( page as any ) . _wrapApiCall ( async ( ) = > {
2024-01-14 08:41:40 -08:00
await page . exposeFunction ( '__ctDispatchFunction' , ( ordinal : number , args : any [ ] ) = > {
2024-01-12 20:02:27 -08:00
boundCallbacksForMount [ ordinal ] ( . . . args ) ;
} ) ;
await page . goto ( process . env . PLAYWRIGHT_TEST_BASE_URL ! ) ;
} , true ) ;
await use ( page ) ;
} ,
2022-05-31 15:59:36 -07:00
2024-01-12 20:02:27 -08:00
mount : async ( { page } , use ) = > {
await use ( async ( componentRef : JsxComponent | ImportRef , options? : ObjectComponentOptions & MountOptions ) = > {
const selector = await ( page as any ) . _wrapApiCall ( async ( ) = > {
return await innerMount ( page , componentRef , options ) ;
2022-06-02 12:16:07 -07:00
} , true ) ;
2024-01-12 20:02:27 -08:00
const locator = page . locator ( selector ) ;
return Object . assign ( locator , {
unmount : async ( ) = > {
await locator . evaluate ( async ( ) = > {
const rootElement = document . getElementById ( 'root' ) ! ;
await window . playwrightUnmount ( rootElement ) ;
} ) ;
} ,
update : async ( options : JsxComponent | ObjectComponentOptions ) = > {
if ( isJsxComponent ( options ) )
return await innerUpdate ( page , options ) ;
await innerUpdate ( page , componentRef , options ) ;
}
2022-07-27 15:12:36 -07:00
} ) ;
2024-01-12 20:02:27 -08:00
} ) ;
boundCallbacksForMount = [ ] ;
} ,
} ;
2022-05-06 13:53:38 -08:00
2024-01-12 20:02:27 -08:00
function isJsxComponent ( component : any ) : component is JsxComponent {
return typeof component === 'object' && component && component . __pw_type === 'jsx' ;
2022-10-07 00:07:32 +02:00
}
2024-01-12 20:02:27 -08:00
async function innerUpdate ( page : Page , componentRef : JsxComponent | ImportRef , options : ObjectComponentOptions = { } ) : Promise < void > {
2024-01-14 08:41:40 -08:00
const component = wrapObject ( createComponent ( componentRef , options ) , boundCallbacksForMount ) ;
2022-08-16 19:47:33 +02:00
await page . evaluate ( async ( { component } ) = > {
2024-01-14 08:41:40 -08:00
component = await window . __pwUnwrapObject ( component ) ;
2022-08-16 19:47:33 +02:00
const rootElement = document . getElementById ( 'root' ) ! ;
2022-10-11 04:56:33 +02:00
return await window . playwrightUpdate ( rootElement , component ) ;
2022-08-16 19:47:33 +02:00
} , { component } ) ;
}
2024-01-12 20:02:27 -08:00
async function innerMount ( page : Page , componentRef : JsxComponent | ImportRef , options : ObjectComponentOptions & MountOptions = { } ) : Promise < string > {
2024-01-14 08:41:40 -08:00
const component = wrapObject ( createComponent ( componentRef , options ) , boundCallbacksForMount ) ;
2022-03-11 08:00:46 -08:00
2022-03-11 15:46:11 -08:00
// WebKit does not wait for deferred scripts.
2022-06-04 14:07:06 -07:00
await page . waitForFunction ( ( ) = > ! ! window . playwrightMount ) ;
2022-03-11 15:46:11 -08:00
2022-07-12 08:37:33 -08:00
const selector = await page . evaluate ( async ( { component , hooksConfig } ) = > {
2024-01-14 08:41:40 -08:00
component = await window . __pwUnwrapObject ( component ) ;
2022-06-04 14:07:06 -07:00
let rootElement = document . getElementById ( 'root' ) ;
if ( ! rootElement ) {
rootElement = document . createElement ( 'div' ) ;
rootElement . id = 'root' ;
document . body . appendChild ( rootElement ) ;
}
2022-07-12 08:37:33 -08:00
await window . playwrightMount ( component , rootElement , hooksConfig ) ;
2022-06-04 14:07:06 -07:00
2022-10-05 08:45:10 -07:00
return '#root >> internal:control=component' ;
2022-07-12 08:37:33 -08:00
} , { component , hooksConfig : options.hooksConfig } ) ;
2022-03-11 08:00:46 -08:00
return selector ;
}
2024-01-12 20:02:27 -08:00
function createComponent ( component : JsxComponent | ImportRef , options : ObjectComponentOptions = { } ) : Component {
if ( component . __pw_type === 'jsx' )
return component ;
return {
__pw_type : 'object-component' ,
type : component ,
. . . options ,
} ;
2022-08-16 19:47:33 +02:00
}