Skip to content

Commit 25a99d5

Browse files
authored
test: mmigrate to expect_ where possible (microsoft#428)
1 parent 39374d0 commit 25a99d5

32 files changed

+2455
-1171
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
88
| :--- | :---: | :---: | :---: |
99
| Chromium <!-- GEN:chromium-version -->89.0.4344.0<!-- GEN:stop --> ||||
1010
| WebKit <!-- GEN:webkit-version -->14.1<!-- GEN:stop --> ||||
11-
| Firefox <!-- GEN:firefox-version -->85.0b1<!-- GEN:stop --> ||||
11+
| Firefox <!-- GEN:firefox-version -->85.0b5<!-- GEN:stop --> ||||
1212

1313
Headless execution is supported for all browsers on all platforms.
1414

playwright/_impl/_async_base.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
import asyncio
16-
from typing import Any, Callable, Coroutine, Generic, Optional, TypeVar, cast
16+
from typing import Any, Callable, Generic, TypeVar
1717

1818
from playwright._impl._impl_to_api_mapping import ImplToApiMapping, ImplWrapper
1919

@@ -24,22 +24,17 @@
2424

2525

2626
class AsyncEventInfo(Generic[T]):
27-
def __init__(self, coroutine: Coroutine) -> None:
28-
self._value: Optional[T] = None
29-
self._future = asyncio.get_event_loop().create_task(coroutine)
30-
self._done = False
27+
def __init__(self, future: asyncio.Future) -> None:
28+
self._future = future
3129

3230
@property
3331
async def value(self) -> T:
34-
if not self._done:
35-
self._value = mapping.from_maybe_impl(await self._future)
36-
self._done = True
37-
return cast(T, self._value)
32+
return mapping.from_maybe_impl(await self._future)
3833

3934

4035
class AsyncEventContextManager(Generic[T]):
41-
def __init__(self, coroutine: Coroutine) -> None:
42-
self._event: AsyncEventInfo = AsyncEventInfo(coroutine)
36+
def __init__(self, future: asyncio.Future) -> None:
37+
self._event: AsyncEventInfo = AsyncEventInfo(future)
4338

4439
async def __aenter__(self) -> AsyncEventInfo[T]:
4540
return self._event

playwright/_impl/_browser_context.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from playwright._impl._connection import ChannelOwner, from_channel
2525
from playwright._impl._event_context_manager import EventContextManagerImpl
2626
from playwright._impl._helper import (
27-
PendingWaitEvent,
2827
RouteHandler,
2928
RouteHandlerEntry,
3029
TimeoutSettings,
@@ -55,7 +54,6 @@ def __init__(
5554
self._pages: List[Page] = []
5655
self._routes: List[RouteHandlerEntry] = []
5756
self._bindings: Dict[str, Any] = {}
58-
self._pending_wait_for_events: List[PendingWaitEvent] = []
5957
self._timeout_settings = TimeoutSettings(None)
6058
self._browser: Optional["Browser"] = None
6159
self._owner_page: Optional[Page] = None
@@ -201,9 +199,12 @@ async def unroute(
201199
"setNetworkInterceptionEnabled", dict(enabled=False)
202200
)
203201

204-
async def wait_for_event(
205-
self, event: str, predicate: Callable = None, timeout: float = None
206-
) -> Any:
202+
def expect_event(
203+
self,
204+
event: str,
205+
predicate: Callable = None,
206+
timeout: float = None,
207+
) -> EventContextManagerImpl:
207208
if timeout is None:
208209
timeout = self._timeout_settings.timeout()
209210
wait_helper = WaitHelper(self._loop)
@@ -214,18 +215,14 @@ async def wait_for_event(
214215
wait_helper.reject_on_event(
215216
self, BrowserContext.Events.Close, Error("Context closed")
216217
)
217-
return await wait_helper.wait_for_event(self, event, predicate)
218+
wait_helper.wait_for_event(self, event, predicate)
219+
return EventContextManagerImpl(wait_helper.result())
218220

219221
def _on_close(self) -> None:
220222
self._is_closed_or_closing = True
221223
if self._browser:
222224
self._browser._contexts.remove(self)
223225

224-
for pending_event in self._pending_wait_for_events:
225-
if pending_event.event == BrowserContext.Events.Close:
226-
continue
227-
pending_event.reject(False, "Context")
228-
229226
self.emit(BrowserContext.Events.Close)
230227

231228
async def close(self) -> None:
@@ -245,17 +242,16 @@ async def storage_state(self, path: Union[str, Path] = None) -> StorageState:
245242
json.dump(result, f)
246243
return result
247244

248-
def expect_event(
249-
self,
250-
event: str,
251-
predicate: Callable = None,
252-
timeout: float = None,
253-
) -> EventContextManagerImpl:
254-
return EventContextManagerImpl(self.wait_for_event(event, predicate, timeout))
245+
async def wait_for_event(
246+
self, event: str, predicate: Callable = None, timeout: float = None
247+
) -> Any:
248+
async with self.expect_event(event, predicate, timeout) as event_info:
249+
pass
250+
return await event_info.value
255251

256252
def expect_page(
257253
self,
258254
predicate: Callable[[Page], bool] = None,
259255
timeout: float = None,
260256
) -> EventContextManagerImpl[Page]:
261-
return EventContextManagerImpl(self.wait_for_event("page", predicate, timeout))
257+
return self.expect_event(BrowserContext.Events.Page, predicate, timeout)

playwright/_impl/_event_context_manager.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,27 @@
1313
# limitations under the License.
1414

1515
import asyncio
16-
from typing import Any, Coroutine, Generic, Optional, TypeVar, cast
16+
from typing import Any, Generic, TypeVar
1717

1818
T = TypeVar("T")
1919

2020

2121
class EventInfoImpl(Generic[T]):
22-
def __init__(self, coroutine: Coroutine) -> None:
23-
self._value: Optional[T] = None
24-
self._task = asyncio.get_event_loop().create_task(coroutine)
25-
self._done = False
22+
def __init__(self, future: asyncio.Future) -> None:
23+
self._future = future
2624

2725
@property
2826
async def value(self) -> T:
29-
if not self._done:
30-
self._value = await self._task
31-
self._done = True
32-
return cast(T, self._value)
27+
return await self._future
3328

3429

3530
class EventContextManagerImpl(Generic[T]):
36-
def __init__(self, coroutine: Coroutine) -> None:
37-
self._event: EventInfoImpl = EventInfoImpl(coroutine)
31+
def __init__(self, future: asyncio.Future) -> None:
32+
self._event: EventInfoImpl = EventInfoImpl(future)
33+
34+
@property
35+
def future(self) -> asyncio.Future:
36+
return self._event._future
3837

3938
async def __aenter__(self) -> EventInfoImpl[T]:
4039
return self._event

playwright/_impl/_frame.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,14 @@ def _setup_navigation_wait_helper(self, timeout: float = None) -> WaitHelper:
135135
wait_helper.reject_on_timeout(timeout, f"Timeout {timeout}ms exceeded.")
136136
return wait_helper
137137

138-
async def wait_for_navigation(
138+
def expect_navigation(
139139
self,
140140
url: URLMatch = None,
141-
waitUntil: DocumentLoadState = None,
141+
wait_until: DocumentLoadState = None,
142142
timeout: float = None,
143-
) -> Optional[Response]:
144-
if not waitUntil:
145-
waitUntil = "load"
143+
) -> EventContextManagerImpl[Response]:
144+
if not wait_until:
145+
wait_until = "load"
146146

147147
if timeout is None:
148148
timeout = self._page._timeout_settings.navigation_timeout()
@@ -156,23 +156,26 @@ def predicate(event: Any) -> bool:
156156
return True
157157
return not matcher or matcher.matches(event["url"])
158158

159-
event = await wait_helper.wait_for_event(
159+
wait_helper.wait_for_event(
160160
self._event_emitter,
161161
"navigated",
162162
predicate=predicate,
163163
)
164-
if "error" in event:
165-
raise Error(event["error"])
166-
167-
if waitUntil not in self._load_states:
168-
t = deadline - monotonic_time()
169-
if t > 0:
170-
await self.wait_for_load_state(state=waitUntil, timeout=t)
171164

172-
if "newDocument" in event and "request" in event["newDocument"]:
173-
request = from_channel(event["newDocument"]["request"])
174-
return await request.response()
175-
return None
165+
async def continuation() -> Optional[Response]:
166+
event = await wait_helper.result()
167+
if "error" in event:
168+
raise Error(event["error"])
169+
if wait_until not in self._load_states:
170+
t = deadline - monotonic_time()
171+
if t > 0:
172+
await self.wait_for_load_state(state=wait_until, timeout=t)
173+
if "newDocument" in event and "request" in event["newDocument"]:
174+
request = from_channel(event["newDocument"]["request"])
175+
return await request.response()
176+
return None
177+
178+
return EventContextManagerImpl(asyncio.create_task(continuation()))
176179

177180
async def wait_for_load_state(
178181
self, state: DocumentLoadState = None, timeout: float = None
@@ -184,9 +187,10 @@ async def wait_for_load_state(
184187
if state in self._load_states:
185188
return
186189
wait_helper = self._setup_navigation_wait_helper(timeout)
187-
await wait_helper.wait_for_event(
190+
wait_helper.wait_for_event(
188191
self._event_emitter, "loadstate", lambda s: s == state
189192
)
193+
await wait_helper.result()
190194

191195
async def frame_element(self) -> ElementHandle:
192196
return from_channel(await self._channel.send("frameElement"))
@@ -526,12 +530,14 @@ async def wait_for_function(
526530
async def title(self) -> str:
527531
return await self._channel.send("title")
528532

529-
def expect_navigation(
533+
async def wait_for_navigation(
530534
self,
531535
url: URLMatch = None,
532536
waitUntil: DocumentLoadState = None,
533537
timeout: float = None,
534-
) -> EventContextManagerImpl:
535-
return EventContextManagerImpl(
536-
self.wait_for_navigation(url, waitUntil, timeout)
537-
)
538+
) -> Optional[Response]:
539+
async with self.expect_navigation(
540+
url, waitUntil, timeout=timeout
541+
) as response_info:
542+
pass
543+
return await response_info.value

playwright/_impl/_helper.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import asyncio
1615
import fnmatch
1716
import math
1817
import re
@@ -199,25 +198,6 @@ def monotonic_time() -> int:
199198
return math.floor(time.monotonic() * 1000)
200199

201200

202-
class PendingWaitEvent:
203-
def __init__(
204-
self, event: str, future: asyncio.Future, timeout_future: asyncio.Future
205-
):
206-
self.event = event
207-
self.future = future
208-
self.timeout_future = timeout_future
209-
210-
def reject(self, is_crash: bool, target: str) -> None:
211-
self.timeout_future.cancel()
212-
if self.event == "close" and not is_crash:
213-
return
214-
if self.event == "crash" and is_crash:
215-
return
216-
self.future.set_exception(
217-
Error(f"{target} crashed" if is_crash else f"{target} closed")
218-
)
219-
220-
221201
class RouteHandlerEntry:
222202
def __init__(self, matcher: URLMatcher, handler: RouteHandler):
223203
self.matcher = matcher

playwright/_impl/_network.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,12 @@ def __init__(
293293
def url(self) -> str:
294294
return self._initializer["url"]
295295

296-
async def wait_for_event(
297-
self, event: str, predicate: Callable = None, timeout: float = None
298-
) -> Any:
296+
def expect_event(
297+
self,
298+
event: str,
299+
predicate: Callable = None,
300+
timeout: float = None,
301+
) -> EventContextManagerImpl:
299302
if timeout is None:
300303
timeout = cast(Any, self._parent)._timeout_settings.timeout()
301304
wait_helper = WaitHelper(self._loop)
@@ -311,15 +314,15 @@ async def wait_for_event(
311314
self, WebSocket.Events.Error, Error("Socket error")
312315
)
313316
wait_helper.reject_on_event(self._parent, "close", Error("Page closed"))
314-
return await wait_helper.wait_for_event(self, event, predicate)
317+
wait_helper.wait_for_event(self, event, predicate)
318+
return EventContextManagerImpl(wait_helper.result())
315319

316-
def expect_event(
317-
self,
318-
event: str,
319-
predicate: Callable = None,
320-
timeout: float = None,
321-
) -> EventContextManagerImpl:
322-
return EventContextManagerImpl(self.wait_for_event(event, predicate, timeout))
320+
async def wait_for_event(
321+
self, event: str, predicate: Callable = None, timeout: float = None
322+
) -> Any:
323+
async with self.expect_event(event, predicate, timeout) as event_info:
324+
pass
325+
return await event_info.value
323326

324327
def _on_frame_sent(self, opcode: int, data: str) -> None:
325328
if opcode == 2:

0 commit comments

Comments
 (0)