44from types import TracebackType
55from typing import Any
66
7- from playwright .async_api import (
8- Browser ,
9- BrowserContext ,
10- Page ,
11- async_playwright ,
12- )
7+ from playwright .async_api import Browser , Page , async_playwright
138
149from reactpy .config import REACTPY_TESTS_DEFAULT_TIMEOUT
1510from reactpy .testing .backend import BackendFixture
11+ from reactpy .testing .common import GITHUB_ACTIONS
1612from reactpy .types import RootComponentConstructor
1713
1814
1915class DisplayFixture :
2016 """A fixture for running web-based tests using ``playwright``"""
2117
22- _exit_stack : AsyncExitStack
18+ page : Page
19+ browser_is_external : bool = False
20+ backend_is_external : bool = False
2321
2422 def __init__ (
2523 self ,
2624 backend : BackendFixture | None = None ,
27- driver : Browser | BrowserContext | Page | None = None ,
25+ browser : Browser | None = None ,
2826 ) -> None :
29- if backend is not None :
27+ if backend :
28+ self .backend_is_external = True
3029 self .backend = backend
31- if driver is not None :
32- if isinstance (driver , Page ):
33- self .page = driver
34- else :
35- self ._browser = driver
30+
31+ if browser :
32+ self .browser_is_external = True
33+ self .browser = browser
3634
3735 async def show (
3836 self ,
@@ -42,34 +40,42 @@ async def show(
4240 await self .goto ("/" )
4341
4442 async def goto (self , path : str , query : Any | None = None ) -> None :
43+ await self .configure_page ()
4544 await self .page .goto (self .backend .url (path , query ))
4645
4746 async def __aenter__ (self ) -> DisplayFixture :
48- es = self ._exit_stack = AsyncExitStack ()
49-
50- browser : Browser | BrowserContext
51- if not hasattr (self , "page" ):
52- if not hasattr (self , "_browser" ):
53- pw = await es .enter_async_context (async_playwright ())
54- browser = await pw .chromium .launch ()
55- else :
56- browser = self ._browser
57- self .page = await browser .new_page ()
58-
59- self .page .set_default_timeout (REACTPY_TESTS_DEFAULT_TIMEOUT .current * 1000 )
60- self .page .on ("console" , lambda msg : print (f"BROWSER CONSOLE: { msg .text } " )) # noqa: T201
61- self .page .on ("pageerror" , lambda exc : print (f"BROWSER ERROR: { exc } " )) # noqa: T201
47+ self .browser_exit_stack = AsyncExitStack ()
48+ self .backend_exit_stack = AsyncExitStack ()
49+
50+ if not hasattr (self , "browser" ):
51+ pw = await self .browser_exit_stack .enter_async_context (async_playwright ())
52+ self .browser = await pw .chromium .launch (headless = GITHUB_ACTIONS )
53+ await self .configure_page ()
54+
6255 if not hasattr (self , "backend" ): # nocov
6356 self .backend = BackendFixture ()
64- await es .enter_async_context (self .backend )
57+ await self . backend_exit_stack .enter_async_context (self .backend )
6558
6659 return self
6760
61+ async def configure_page (self ) -> None :
62+ """Hook for configuring the page before use."""
63+ if getattr (self , "page" , None ) is None :
64+ self .page = await self .browser .new_page ()
65+ self .page .set_default_timeout (REACTPY_TESTS_DEFAULT_TIMEOUT .current * 1000 )
66+ self .page .on ("console" , lambda msg : print (f"BROWSER CONSOLE: { msg .text } " )) # noqa: T201
67+ self .page .on ("pageerror" , lambda exc : print (f"BROWSER ERROR: { exc } " )) # noqa: T201
68+
6869 async def __aexit__ (
6970 self ,
7071 exc_type : type [BaseException ] | None ,
7172 exc_value : BaseException | None ,
7273 traceback : TracebackType | None ,
7374 ) -> None :
7475 self .backend .mount (None )
75- await self ._exit_stack .aclose ()
76+ if getattr (self , "page" , None ) is not None :
77+ await self .page .close ()
78+ if not self .browser_is_external :
79+ await self .browser_exit_stack .aclose ()
80+ if not self .backend_is_external :
81+ await self .backend_exit_stack .aclose ()
0 commit comments