Skip to content

Commit 4ad29a7

Browse files
authored
tests: added launcher tests (#55)
1 parent db61f1a commit 4ad29a7

File tree

3 files changed

+346
-7
lines changed

3 files changed

+346
-7
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env node
2+
3+
process.exit(1);

tests/conftest.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,19 @@ def event_loop():
4040

4141

4242
@pytest.fixture(scope="session")
43-
async def browser_factory(browser_name, pytestconfig):
43+
def browser_type(browser_name: str):
44+
return playwright.browser_types[browser_name]
45+
46+
47+
@pytest.fixture(scope="session")
48+
def launch_arguments(pytestconfig):
49+
return {"headless": not pytestconfig.getoption("--headful")}
50+
51+
52+
@pytest.fixture(scope="session")
53+
async def browser_factory(launch_arguments, browser_type):
4454
async def launch(**kwargs):
45-
return await playwright.browser_types[browser_name].launch(
46-
headless=not pytestconfig.getoption("--headful"), **kwargs
47-
)
55+
return await browser_type.launch(**launch_arguments, **kwargs)
4856

4957
return launch
5058

@@ -115,17 +123,17 @@ def is_chromium(browser_name):
115123

116124

117125
@pytest.fixture(scope="session")
118-
def is_win(browser_name):
126+
def is_win():
119127
return sys.platform == "win32"
120128

121129

122130
@pytest.fixture(scope="session")
123-
def is_linux(browser_name):
131+
def is_linux():
124132
return sys.platform == "linux"
125133

126134

127135
@pytest.fixture(scope="session")
128-
def is_mac(browser_name):
136+
def is_mac():
129137
return sys.platform == "darwin"
130138

131139

tests/test_launcher.py

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
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 os
17+
import pytest
18+
19+
from playwright.helper import Error
20+
from playwright.browser_type import BrowserType
21+
22+
__dirname = os.path.dirname(os.path.abspath(__file__))
23+
24+
25+
async def test_browser_type_launch_should_reject_all_promises_when_browser_is_closed(
26+
browser_type: BrowserType, launch_arguments
27+
):
28+
browser = await browser_type.launch(**launch_arguments)
29+
page = await (await browser.newContext()).newPage()
30+
never_resolves = asyncio.ensure_future(page.evaluate("() => new Promise(r => {})"))
31+
await page.close()
32+
with pytest.raises(Error) as exc:
33+
await never_resolves
34+
assert "Protocol error" in exc.value.message
35+
36+
37+
@pytest.mark.skip_browser("firefox")
38+
async def test_browser_type_launch_should_throw_if_page_argument_is_passed(
39+
browser_type, launch_arguments
40+
):
41+
with pytest.raises(Error) as exc:
42+
await browser_type.launch(**launch_arguments, args=["http://example.com"])
43+
assert "can not specify page" in exc.value.message
44+
45+
46+
@pytest.mark.skip("does not return the expected error") # TODO: should be removed
47+
async def test_browser_type_launch_should_reject_if_launched_browser_fails_immediately(
48+
browser_type, launch_arguments
49+
):
50+
with pytest.raises(Error) as exc:
51+
await browser_type.launch(
52+
**launch_arguments,
53+
executablePath=os.path.join(
54+
__dirname, "assets", "dummy_bad_browser_executable.js"
55+
)
56+
)
57+
assert "browser_type.launch logs" in exc.value.message
58+
59+
60+
async def test_browser_type_launch_should_reject_if_executable_path_is_invalid(
61+
browser_type, launch_arguments
62+
):
63+
with pytest.raises(Error) as exc:
64+
await browser_type.launch(
65+
**launch_arguments, executablePath="random-invalid-path"
66+
)
67+
assert "Failed to launch" in exc.value.message
68+
69+
70+
async def test_browser_type_launch_server_should_return_child_process_instance(
71+
browser_type, launch_arguments
72+
):
73+
browser_server = await browser_type.launchServer(**launch_arguments)
74+
assert browser_server.pid > 0
75+
await browser_server.close()
76+
77+
78+
async def test_browser_type_launch_server_should_fire_close_event(
79+
browser_type, launch_arguments
80+
):
81+
browser_server = await browser_type.launchServer(**launch_arguments)
82+
close_event = asyncio.Future()
83+
browser_server.on("close", lambda: close_event.set_result(None))
84+
await asyncio.gather(close_event, browser_server.close())
85+
86+
87+
async def test_browser_type_executable_path_should_work(browser_type):
88+
executable_path = browser_type.executablePath
89+
assert os.path.exists(executable_path)
90+
assert os.path.realpath(executable_path) == executable_path
91+
92+
93+
async def test_browser_type_name_should_work(
94+
browser_type, is_webkit, is_firefox, is_chromium
95+
):
96+
if is_webkit:
97+
assert browser_type.name == "webkit"
98+
elif is_firefox:
99+
assert browser_type.name == "firefox"
100+
elif is_chromium:
101+
assert browser_type.name == "chromium"
102+
else:
103+
raise ValueError("Unknown browser")
104+
105+
106+
async def test_browser_is_connected_should_set_connected_state(
107+
browser_type, launch_arguments
108+
):
109+
browser_server = await browser_type.launchServer(**launch_arguments)
110+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
111+
assert remote.isConnected()
112+
await remote.close()
113+
assert remote.isConnected() is False
114+
await browser_server.close()
115+
116+
117+
async def test_browser_is_connected_should_throw_when_used_after_isConnected_returns_false(
118+
browser_type, launch_arguments
119+
):
120+
browser_server = await browser_type.launchServer(**launch_arguments)
121+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
122+
page = await remote.newPage()
123+
disconnected_future = asyncio.Future()
124+
remote.once("disconnected", lambda: disconnected_future.set_result(None))
125+
await asyncio.gather(browser_server.close(), disconnected_future)
126+
assert remote.isConnected() is False
127+
with pytest.raises(Error) as exc:
128+
await page.evaluate('"1 + 1"')
129+
assert "has been closed" in exc.value.message
130+
131+
132+
@pytest.mark.skip("currently flaky") # TODO: should be removed
133+
async def test_browser_disconnect_should_reject_navigation_when_browser_closes(
134+
browser_type, launch_arguments, server
135+
):
136+
server.set_route("/one-style.css", lambda r: None)
137+
browser_server = await browser_type.launchServer(**launch_arguments)
138+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
139+
page = await remote.newPage()
140+
goto_future = asyncio.Future()
141+
142+
async def handle_goto():
143+
try:
144+
await page.goto(server.PREFIX + "/one-style.html", timeout=60000)
145+
except Error as exc:
146+
goto_future.set_result(exc)
147+
148+
asyncio.ensure_future(handle_goto())
149+
await server.wait_for_request("/one-style.css")
150+
await remote.close()
151+
error = await goto_future
152+
assert "Navigation failed because page was closed!" in error.message
153+
await browser_server.close()
154+
155+
156+
async def test_browser_disconnect_should_reject_waitForSelector_when_browser_closes(
157+
browser_type, launch_arguments, server
158+
):
159+
server.set_route("/empty.html", lambda r: None)
160+
browser_server = await browser_type.launchServer(**launch_arguments)
161+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
162+
page = await remote.newPage()
163+
wait_for_selector_future = asyncio.Future()
164+
165+
async def handle_wait_for_selector():
166+
try:
167+
await page.waitForSelector("div", state="attached", timeout=60000)
168+
except Error as exc:
169+
wait_for_selector_future.set_result(exc)
170+
171+
# Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
172+
asyncio.ensure_future(handle_wait_for_selector())
173+
await page.waitForSelector("body", state="attached")
174+
175+
await remote.close()
176+
error = await wait_for_selector_future
177+
assert "Protocol error" in error.message
178+
await browser_server.close()
179+
180+
181+
async def test_browser_disconnect_should_throw_if_used_after_disconnect(
182+
browser_type, launch_arguments
183+
):
184+
browser_server = await browser_type.launchServer(**launch_arguments)
185+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
186+
page = await remote.newPage()
187+
await remote.close()
188+
with pytest.raises(Error) as exc:
189+
await page.evaluate('"1 + 1"')
190+
assert "has been closed" in exc.value.message
191+
await browser_server.close()
192+
193+
194+
async def test_browser_disconnect_should_emit_close_events_on_pages_and_contexts(
195+
browser_type, launch_arguments
196+
):
197+
browser_server = await browser_type.launchServer(**launch_arguments)
198+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
199+
context = await remote.newContext()
200+
page = await context.newPage()
201+
pages_closed = []
202+
page.on("close", lambda: pages_closed.append(True))
203+
context_close_fixture = asyncio.Future()
204+
context.on("close", lambda: context_close_fixture.set_result(None))
205+
await asyncio.gather(context_close_fixture, browser_server.close())
206+
assert len(pages_closed) == 1
207+
208+
209+
async def test_browser_close_should_terminate_network_waiters(
210+
browser_type, launch_arguments, server
211+
):
212+
browser_server = await browser_type.launchServer(**launch_arguments)
213+
remote = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
214+
new_page = await remote.newPage()
215+
wait_for_request_future = asyncio.Future()
216+
217+
async def handle_waitForRequest():
218+
try:
219+
await new_page.waitForRequest(server.EMPTY_PAGE)
220+
except Error as exc:
221+
wait_for_request_future.set_result(exc)
222+
223+
wait_for_response_future = asyncio.Future()
224+
225+
async def handle_waitForResponse():
226+
try:
227+
await new_page.waitForResponse(server.EMPTY_PAGE)
228+
except Error as exc:
229+
wait_for_response_future.set_result(exc)
230+
231+
asyncio.ensure_future(handle_waitForRequest())
232+
asyncio.ensure_future(handle_waitForResponse())
233+
results = await asyncio.gather(
234+
wait_for_request_future, wait_for_response_future, browser_server.close(),
235+
)
236+
for i in range(2):
237+
message = results[i].message
238+
assert "Page closed" in message
239+
assert "Timeout" not in message
240+
241+
242+
async def test_browser_close_should_fire_close_event_for_all_contexts(
243+
browser_type, launch_arguments
244+
):
245+
browser = await browser_type.launch(**launch_arguments)
246+
context = await browser.newContext()
247+
closed = []
248+
context.on("close", lambda: closed.append(True))
249+
await browser.close()
250+
assert closed == [True]
251+
252+
253+
async def test_browser_close_should_be_callable_twice(browser_type, launch_arguments):
254+
browser = await browser_type.launch(**launch_arguments)
255+
await asyncio.gather(
256+
browser.close(), browser.close(),
257+
)
258+
await browser.close()
259+
260+
261+
async def test_browser_type_launch_server_should_work(browser_type, launch_arguments):
262+
browser_server = await browser_type.launchServer(**launch_arguments)
263+
browser = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
264+
browser_context = await browser.newContext()
265+
assert len(browser_context.pages) == 0
266+
assert browser_server.wsEndpoint
267+
page = await browser_context.newPage()
268+
assert await page.evaluate("11 * 11") == 121
269+
await page.close()
270+
await browser.close()
271+
await browser_server.close()
272+
273+
274+
async def test_browser_type_launch_server_should_fire_disconnected_when_closing_the_server(
275+
browser_type, launch_arguments
276+
):
277+
browser_server = await browser_type.launchServer(**launch_arguments)
278+
browser = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
279+
280+
disconnected_promise = asyncio.Future()
281+
browser.once("disconnected", lambda: disconnected_promise.set_result(None))
282+
283+
closed_promise = asyncio.Future()
284+
browser_server.on("close", lambda: closed_promise.set_result(None))
285+
286+
await browser_server.kill()
287+
await asyncio.gather(
288+
disconnected_promise, closed_promise,
289+
)
290+
291+
292+
async def test_browser_type_launch_server_should_fire_close_event_during_kill(
293+
browser_type, launch_arguments
294+
):
295+
order = []
296+
browser_server = await browser_type.launchServer(**launch_arguments)
297+
298+
closed_promise = asyncio.Future()
299+
browser_server.on(
300+
"close", lambda: (order.append("closed"), closed_promise.set_result(None))
301+
)
302+
303+
async def kill_with_order():
304+
await browser_server.kill()
305+
order.append("killed")
306+
307+
await asyncio.gather(kill_with_order(), closed_promise)
308+
assert order == ["closed", "killed"]
309+
310+
311+
async def test_browser_type_connect_should_be_able_to_reconnect_to_a_browser(
312+
browser_type, launch_arguments, server
313+
):
314+
browser_server = await browser_type.launchServer(**launch_arguments)
315+
316+
browser = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
317+
browser_context = await browser.newContext()
318+
page = await browser_context.newPage()
319+
await page.goto(server.EMPTY_PAGE)
320+
await browser.close()
321+
322+
browser = await browser_type.connect(wsEndpoint=browser_server.wsEndpoint)
323+
browser_context = await browser.newContext()
324+
page = await browser_context.newPage()
325+
await page.goto(server.EMPTY_PAGE)
326+
await browser.close()
327+
328+
await browser_server.close()

0 commit comments

Comments
 (0)