Skip to content

Commit 623efa8

Browse files
authored
feat(roll): roll Playwright 1.2.0-next.1594327663260 (microsoft#30)
1 parent 4c705f3 commit 623efa8

File tree

11 files changed

+140
-85
lines changed

11 files changed

+140
-85
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,35 @@ async def run():
5959
asyncio.get_event_loop().run_until_complete(run())
6060
```
6161

62+
#### Mobile and geolocation
63+
64+
This snippet emulates Mobile Safari on a device at a given geolocation, navigates to maps.google.com, performs action and takes a screenshot.
65+
66+
```py
67+
import asyncio
68+
from playwright import webkit, devices
69+
70+
iphone_11 = devices['iPhone 11 Pro']
71+
print(iphone_11)
72+
73+
async def run():
74+
browser = await webkit.launch(headless=False)
75+
context = await browser.newContext(
76+
**iphone_11,
77+
locale='en-US',
78+
geolocation={ 'longitude': 12.492507, 'latitude': 41.889938 },
79+
permissions=['geolocation']
80+
)
81+
page = await context.newPage()
82+
await page.goto('https://maps.google.com')
83+
await page.click('text="Your location"')
84+
await page.waitForRequest('*preview/pwa')
85+
await page.screenshot(path='colosseum-iphone.png')
86+
await browser.close()
87+
88+
asyncio.get_event_loop().run_until_complete(run())
89+
```
90+
6291
#### Evaluate in browser context
6392

6493
This code snippet navigates to example.com in Firefox, and executes a script in the page context.

driver/package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

driver/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
},
1414
"license": "Apache-2.0",
1515
"dependencies": {
16-
"playwright": "1.2.0-next.1594076413467"
16+
"playwright": "1.2.0-next.1594327663260"
1717
},
1818
"devDependencies": {
1919
"pkg": "^4.4.9"

playwright/__init__.py

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

15-
from playwright.playwright import playwright
15+
from playwright.main import playwright_object
1616

17-
chromium = playwright.chromium
18-
firefox = playwright.firefox
19-
webkit = playwright.webkit
20-
browser_types = playwright.browser_types
17+
chromium = playwright_object.chromium
18+
firefox = playwright_object.firefox
19+
webkit = playwright_object.webkit
20+
devices = playwright_object.devices
21+
browser_types = playwright_object.browser_types
2122

2223
__all__ = [
24+
'browser_types',
2325
'chromium',
2426
'firefox',
2527
'webkit',
26-
'browser_types'
28+
'devices'
2729
]

playwright/main.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import asyncio
16+
import gzip
17+
import os
18+
import shutil
19+
import stat
20+
import sys
21+
import subprocess
22+
23+
from playwright.connection import Connection
24+
from playwright.object_factory import create_remote_object
25+
from playwright.playwright import Playwright
26+
from typing import Dict
27+
28+
playwright_object: Playwright = None
29+
30+
async def async_init():
31+
global playwright_object
32+
package_path = os.path.dirname(os.path.abspath(__file__))
33+
platform = sys.platform
34+
if platform == 'darwin':
35+
driver_name = 'driver-macos'
36+
elif platform == 'linux':
37+
driver_name = 'driver-linux'
38+
elif platform == 'win32':
39+
driver_name = 'driver-win.exe'
40+
driver_executable = os.path.join(package_path, driver_name)
41+
archive_name = os.path.join(package_path, 'drivers', driver_name + '.gz')
42+
43+
if not os.path.exists(driver_executable) or os.path.getmtime(driver_executable) < os.path.getmtime(archive_name):
44+
with gzip.open(archive_name, 'rb') as f_in, open(driver_executable, 'wb') as f_out:
45+
shutil.copyfileobj(f_in, f_out)
46+
47+
st = os.stat(driver_executable)
48+
if st.st_mode & stat.S_IEXEC == 0:
49+
os.chmod(driver_executable, st.st_mode | stat.S_IEXEC)
50+
51+
subprocess.run(f"{driver_executable} install", shell=True)
52+
53+
proc = await asyncio.create_subprocess_exec(driver_executable,
54+
stdin=asyncio.subprocess.PIPE,
55+
stdout=asyncio.subprocess.PIPE,
56+
stderr=asyncio.subprocess.PIPE,
57+
limit=32768)
58+
connection = Connection(proc.stdout, proc.stdin, create_remote_object, asyncio.get_event_loop())
59+
playwright_object = await connection.wait_for_object_with_known_name('playwright')
60+
61+
if sys.platform == 'win32':
62+
# Use ProactorEventLoop in 3.7, which is default in 3.8
63+
loop = asyncio.ProactorEventLoop()
64+
asyncio.set_event_loop(loop)
65+
asyncio.get_event_loop().run_until_complete(async_init())

playwright/network.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ async def fulfill(self, status: int = 200, headers: Dict[str,str] = dict(), body
9696
elif isinstance(body, bytes):
9797
response['body'] = base64.b64encode(body)
9898
response['isBase64'] = True
99-
await self._channel.send('fulfill', dict(response=response))
99+
await self._channel.send('fulfill', response)
100100

101101
async def continue_(self, method: str = None, headers: Dict[str,str] = None, postData: Union[str, bytes] = None) -> None:
102102
overrides = dict()
@@ -108,7 +108,7 @@ async def continue_(self, method: str = None, headers: Dict[str,str] = None, pos
108108
overrides['postData'] = base64.b64encode(bytes(postData, 'utf-8'))
109109
elif isinstance(postData, bytes):
110110
overrides['postData'] = base64.b64encode(postData)
111-
await self._channel.send('continue', dict(overrides=overrides))
111+
await self._channel.send('continue', overrides)
112112

113113

114114
class Response(ChannelOwner):

playwright/object_factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from playwright.js_handle import JSHandle
2626
from playwright.network import Request, Response, Route
2727
from playwright.page import BindingCall, Page
28+
from playwright.playwright import Playwright
2829
from playwright.worker import Worker
2930
from typing import Any, Awaitable, Dict, List
3031

@@ -53,6 +54,8 @@ def create_remote_object(scope: ConnectionScope, type: str, guid: str, initializ
5354
return JSHandle(scope, guid, initializer)
5455
if type == 'page':
5556
return Page(scope, guid, initializer)
57+
if type == 'playwright':
58+
return Playwright(scope, guid, initializer)
5659
if type == 'request':
5760
return Request(scope, guid, initializer)
5861
if type == 'response':

playwright/page.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -285,32 +285,34 @@ async def waitForNavigation(self,
285285

286286
async def waitForRequest(self, urlOrPredicate: Union[str, Callable[[Request], bool]]) -> Optional[Request]:
287287
matcher = URLMatcher(urlOrPredicate) if isinstance(urlOrPredicate, str) else None
288-
def predicate(request: Request):
288+
def predicate(request: Request) -> bool:
289289
if matcher:
290290
return matcher.matches(request.url())
291291
return urlOrPredicate(request)
292-
params = locals_to_params(locals())
293-
params['predicate'] = predicate
294-
return self.waitForEvent(Page.Events.Request, **params)
292+
return self.waitForEvent(Page.Events.Request, predicate=predicate)
295293

296-
async def waitForResponse(self, urlOrPredicate: Union[str, Callable[[Request], bool]]) -> Optional[Response]:
294+
async def waitForResponse(self, urlOrPredicate: Union[str, Callable[[Response], bool]]) -> Optional[Response]:
297295
matcher = URLMatcher(urlOrPredicate) if isinstance(urlOrPredicate, str) else None
298-
def predicate(request: Request):
296+
def predicate(response: Response) -> bool:
299297
if matcher:
300-
return matcher.matches(request.url())
301-
return urlOrPredicate(request)
302-
params = locals_to_params(locals())
303-
params['predicate'] = predicate
304-
return await self.waitForEvent(Page.Events.Response, **params)
298+
return matcher.matches(response.url())
299+
return urlOrPredicate(response)
300+
return self.waitForEvent(Page.Events.Response, predicate=predicate)
305301

306-
async def waitForEvent(self, event: str) -> Any:
302+
async def waitForEvent(self, event: str, predicate: Callable[[Any], bool] = None) -> Any:
307303
# TODO: support timeout
304+
308305
future = self._scope._loop.create_future()
309-
self.once(event, lambda e: future.set_result(e))
306+
def listener(e: Any):
307+
if not predicate or predicate(e):
308+
future.set_result(e)
309+
310+
self.on(event, listener)
310311
pending_event = PendingWaitEvent(event, future)
311312
self._pending_wait_for_events.append(pending_event)
312313
result = await future
313314
self._pending_wait_for_events.remove(pending_event)
315+
self.remove_listener(event, listener)
314316
return result
315317

316318
async def goBack(self,

playwright/playwright.py

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

15-
import asyncio
16-
import gzip
17-
import os
18-
import shutil
19-
import stat
20-
import sys
21-
import subprocess
22-
23-
from playwright.connection import Connection
24-
from playwright.object_factory import create_remote_object
15+
from playwright.connection import Channel, ChannelOwner, ConnectionScope, from_channel
2516
from playwright.browser_type import BrowserType
2617
from typing import Dict
2718

28-
class Playwright:
29-
def __init__(self) -> None:
30-
# Use ProactorEventLoop in 3.7, which is default in 3.8
31-
if sys.platform == 'win32':
32-
loop = asyncio.ProactorEventLoop()
33-
asyncio.set_event_loop(loop)
34-
self.loop = asyncio.get_event_loop()
35-
self.loop.run_until_complete(self._sync_init())
36-
37-
async def _sync_init(self):
38-
package_path = os.path.dirname(os.path.abspath(__file__))
39-
platform = sys.platform
40-
if platform == 'darwin':
41-
driver_name = 'driver-macos'
42-
elif platform == 'linux':
43-
driver_name = 'driver-linux'
44-
elif platform == 'win32':
45-
driver_name = 'driver-win.exe'
46-
driver_executable = os.path.join(package_path, driver_name)
47-
archive_name = os.path.join(package_path, 'drivers', driver_name + '.gz')
48-
49-
if not os.path.exists(driver_executable) or os.path.getmtime(driver_executable) < os.path.getmtime(archive_name):
50-
with gzip.open(archive_name, 'rb') as f_in, open(driver_executable, 'wb') as f_out:
51-
shutil.copyfileobj(f_in, f_out)
52-
53-
st = os.stat(driver_executable)
54-
if st.st_mode & stat.S_IEXEC == 0:
55-
os.chmod(driver_executable, st.st_mode | stat.S_IEXEC)
56-
57-
subprocess.run(f"{driver_executable} install", shell=True)
19+
class Playwright(ChannelOwner):
5820

59-
self._proc = await asyncio.create_subprocess_exec(driver_executable,
60-
stdin=asyncio.subprocess.PIPE,
61-
stdout=asyncio.subprocess.PIPE,
62-
stderr=asyncio.subprocess.PIPE,
63-
limit=32768)
64-
self._connection = Connection(self._proc.stdout, self._proc.stdin, create_remote_object, self.loop)
65-
chromium, firefox, webkit = await asyncio.gather(
66-
self._connection.wait_for_object_with_known_name('chromium'),
67-
self._connection.wait_for_object_with_known_name('firefox'),
68-
self._connection.wait_for_object_with_known_name('webkit'))
69-
self.chromium: BrowserType = chromium
70-
self.firefox: BrowserType = firefox
71-
self.webkit: BrowserType = webkit
72-
self.browser_types: Dict[str, BrowserType] = dict(chromium=self.chromium, firefox=self.firefox, webkit=self.webkit)
21+
def __init__(self, scope: ConnectionScope, guid: str, initializer: Dict) -> None:
22+
super().__init__(scope, guid, initializer)
23+
self.chromium: BrowserType = from_channel(initializer['chromium'])
24+
self.firefox: BrowserType = from_channel(initializer['firefox'])
25+
self.webkit: BrowserType = from_channel(initializer['webkit'])
26+
self.devices = initializer['deviceDescriptors']
27+
self.browser_types: Dict[str, BrowserType] = dict(chromium=self.chromium, webkit=self.webkit, firefox=self.firefox)
7328

74-
playwright = Playwright()

tests/conftest.py

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

15+
import asyncio
1516
import os
1617
import pytest
1718
import playwright
@@ -37,7 +38,7 @@ def pytest_generate_tests(metafunc):
3738

3839
@pytest.fixture(scope='session')
3940
def event_loop():
40-
loop = playwright.playwright.loop
41+
loop = asyncio.get_event_loop()
4142
yield loop
4243
loop.close()
4344

0 commit comments

Comments
 (0)