# 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. import asyncio import os import re from pathlib import Path from typing import Dict, List, Optional import pytest from playwright.async_api import ( BrowserContext, Error, JSHandle, Page, Route, TimeoutError, ) from tests.server import Server, TestServerRequest from tests.utils import TARGET_CLOSED_ERROR_MESSAGE, must async def test_close_should_reject_all_promises(context: BrowserContext) -> None: new_page = await context.new_page() with pytest.raises(Error) as exc_info: await asyncio.gather(new_page.evaluate("() => new Promise(r => {})"), new_page.close()) assert " closed" in exc_info.value.message async def test_closed_should_not_visible_in_context_pages( context: BrowserContext, ) -> None: page = await context.new_page() assert page in context.pages await page.close() assert page not in context.pages async def test_close_should_run_beforeunload_if_asked_for( context: BrowserContext, server: Server, is_chromium: bool, is_webkit: bool ) -> None: page = await context.new_page() await page.goto(server.PREFIX + "/beforeunload.html") # We have to interact with a page so that 'beforeunload' handlers # fire. await page.click("body") async with page.expect_event("dialog") as dialog_info: await page.close(run_before_unload=True) dialog = await dialog_info.value assert dialog.type == "beforeunload" assert dialog.default_value == "" if is_chromium: assert dialog.message == "" elif is_webkit: assert dialog.message == "Leave?" else: assert "This page is asking you to confirm that you want to leave" in dialog.message async with page.expect_event("close"): await dialog.accept() async def test_close_should_not_run_beforeunload_by_default( context: BrowserContext, server: Server ) -> None: page = await context.new_page() await page.goto(server.PREFIX + "/beforeunload.html") # We have to interact with a page so that 'beforeunload' handlers # fire. await page.click("body") await page.close() async def test_should_be_able_to_navigate_away_from_page_with_before_unload( server: Server, page: Page ) -> None: await page.goto(server.PREFIX + "/beforeunload.html") # We have to interact with a page so that 'beforeunload' handlers # fire. await page.click("body") await page.goto(server.EMPTY_PAGE) async def test_close_should_set_the_page_close_state(context: BrowserContext) -> None: page = await context.new_page() assert page.is_closed() is False await page.close() assert page.is_closed() async def test_close_should_terminate_network_waiters( context: BrowserContext, server: Server ) -> None: page = await context.new_page() async def wait_for_request() -> Error: with pytest.raises(Error) as exc_info: async with page.expect_request(server.EMPTY_PAGE): pass return exc_info.value async def wait_for_response() -> Error: with pytest.raises(Error) as exc_info: async with page.expect_response(server.EMPTY_PAGE): pass return exc_info.value results = await asyncio.gather(wait_for_request(), wait_for_response(), page.close()) for i in range(2): error = results[i] assert error assert TARGET_CLOSED_ERROR_MESSAGE in error.message assert "Timeout" not in error.message async def test_close_should_be_callable_twice(context: BrowserContext) -> None: page = await context.new_page() await asyncio.gather( page.close(), page.close(), ) await page.close() async def test_load_should_fire_when_expected(page: Page) -> None: async with page.expect_event("load"): await page.goto("about:blank") @pytest.mark.skip("FIXME") async def test_should_work_with_wait_for_loadstate(page: Page, server: Server) -> None: messages = [] def _handler(request: TestServerRequest) -> None: messages.append("route") request.setHeader("Content-Type", "text/html") request.write(b"") request.finish() server.set_route( "/empty.html", _handler, ) await page.set_content(f'empty.html') async def wait_for_clickload() -> None: await page.click("a") await page.wait_for_load_state("load") messages.append("clickload") async def wait_for_page_load() -> None: await page.wait_for_event("load") messages.append("load") await asyncio.gather( wait_for_clickload(), wait_for_page_load(), ) assert messages == ["route", "load", "clickload"] async def test_async_stacks_should_work(page: Page, server: Server) -> None: await page.route("**/empty.html", lambda route, response: asyncio.create_task(route.abort())) with pytest.raises(Error) as exc_info: await page.goto(server.EMPTY_PAGE) assert exc_info.value.stack assert __file__ in exc_info.value.stack async def test_opener_should_provide_access_to_the_opener_page(page: Page) -> None: async with page.expect_popup() as popup_info: await page.evaluate("window.open('about:blank')") popup = await popup_info.value opener = await popup.opener() assert opener == page async def test_opener_should_return_null_if_parent_page_has_been_closed( page: Page, ) -> None: async with page.expect_popup() as popup_info: await page.evaluate("window.open('about:blank')") popup = await popup_info.value await page.close() opener = await popup.opener() assert opener is None async def test_domcontentloaded_should_fire_when_expected(page: Page, server: Server) -> None: future = asyncio.create_task(page.goto("about:blank")) async with page.expect_event("domcontentloaded"): pass await future async def test_wait_for_request(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request(server.PREFIX + "/digits/2.png") as request_info: await page.evaluate( """() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }""" ) request = await request_info.value assert request.url == server.PREFIX + "/digits/2.png" async def test_wait_for_request_should_work_with_predicate(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request( lambda request: request.url == server.PREFIX + "/digits/2.png" ) as request_info: await page.evaluate( """() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }""" ) request = await request_info.value assert request.url == server.PREFIX + "/digits/2.png" async def test_wait_for_request_should_timeout(page: Page, server: Server) -> None: with pytest.raises(Error) as exc_info: async with page.expect_event("request", timeout=1): pass assert exc_info.type is TimeoutError async def test_wait_for_request_should_respect_default_timeout(page: Page, server: Server) -> None: page.set_default_timeout(1) with pytest.raises(Error) as exc_info: async with page.expect_event("request", lambda _: False): pass assert exc_info.type is TimeoutError async def test_wait_for_request_should_work_with_no_timeout(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request(server.PREFIX + "/digits/2.png", timeout=0) as request_info: await page.evaluate( """() => setTimeout(() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }, 50)""" ) request = await request_info.value assert request.url == server.PREFIX + "/digits/2.png" async def test_wait_for_request_should_work_with_url_match(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request(re.compile(r"digits\/\d\.png")) as request_info: await page.evaluate("fetch('/digits/1.png')") request = await request_info.value assert request.url == server.PREFIX + "/digits/1.png" async def test_wait_for_event_should_fail_with_error_upon_disconnect( page: Page, ) -> None: with pytest.raises(Error) as exc_info: async with page.expect_download(): await page.close() assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message async def test_wait_for_response_should_work(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_response(server.PREFIX + "/digits/2.png") as response_info: await page.evaluate( """() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }""" ) response = await response_info.value assert response.url == server.PREFIX + "/digits/2.png" async def test_wait_for_response_should_respect_timeout(page: Page) -> None: with pytest.raises(Error) as exc_info: async with page.expect_response("**/*", timeout=1): pass assert exc_info.type is TimeoutError async def test_wait_for_response_should_respect_default_timeout(page: Page) -> None: page.set_default_timeout(1) with pytest.raises(Error) as exc_info: async with page.expect_response(lambda _: False): pass assert exc_info.type is TimeoutError async def test_wait_for_response_should_work_with_predicate(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_response( lambda response: response.url == server.PREFIX + "/digits/2.png" ) as response_info: await page.evaluate( """() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }""" ) response = await response_info.value assert response.url == server.PREFIX + "/digits/2.png" async def test_wait_for_response_should_work_with_no_timeout(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_response(server.PREFIX + "/digits/2.png") as response_info: await page.evaluate( """() => { fetch('/digits/1.png') fetch('/digits/2.png') fetch('/digits/3.png') }""" ) response = await response_info.value assert response.url == server.PREFIX + "/digits/2.png" async def test_wait_for_response_should_use_context_timeout( page: Page, context: BrowserContext, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) context.set_default_timeout(1_000) with pytest.raises(Error) as exc_info: async with page.expect_response("https://playwright.dev"): pass assert exc_info.type is TimeoutError assert "Timeout 1000ms exceeded" in exc_info.value.message async def test_expect_response_should_not_hang_when_predicate_throws( page: Page, ) -> None: with pytest.raises(Exception, match="Oops!"): async with page.expect_response("**/*"): raise Exception("Oops!") async def test_expose_binding(page: Page) -> None: binding_source = [] def binding(source: Dict, a: int, b: int) -> int: binding_source.append(source) return a + b await page.expose_binding("add", lambda source, a, b: binding(source, a, b)) result = await page.evaluate("add(5, 6)") assert binding_source[0]["context"] == page.context assert binding_source[0]["page"] == page assert binding_source[0]["frame"] == page.main_frame assert result == 11 async def test_expose_function(page: Page, server: Server) -> None: await page.expose_function("compute", lambda a, b: a * b) result = await page.evaluate("compute(9, 4)") assert result == 36 async def test_expose_function_should_throw_exception_in_page_context( page: Page, server: Server ) -> None: def throw() -> None: raise Exception("WOOF WOOF") await page.expose_function("woof", lambda: throw()) result = await page.evaluate( """async() => { try { await woof() } catch (e) { return {message: e.message, stack: e.stack} } }""" ) assert result["message"] == "WOOF WOOF" assert __file__ in result["stack"] async def test_expose_function_should_be_callable_from_inside_add_init_script( page: Page, ) -> None: called = [] await page.expose_function("woof", lambda: called.append(True)) await page.add_init_script("woof()") await page.reload() assert called == [True] async def test_expose_function_should_survive_navigation(page: Page, server: Server) -> None: await page.expose_function("compute", lambda a, b: a * b) await page.goto(server.EMPTY_PAGE) result = await page.evaluate("compute(9, 4)") assert result == 36 async def test_expose_function_should_await_returned_promise(page: Page) -> None: async def mul(a: int, b: int) -> int: return a * b await page.expose_function("compute", mul) assert await page.evaluate("compute(3, 5)") == 15 async def test_expose_function_should_work_on_frames(page: Page, server: Server) -> None: await page.expose_function("compute", lambda a, b: a * b) await page.goto(server.PREFIX + "/frames/nested-frames.html") frame = page.frames[1] assert await frame.evaluate("compute(3, 5)") == 15 async def test_expose_function_should_work_on_frames_before_navigation( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/frames/nested-frames.html") await page.expose_function("compute", lambda a, b: a * b) frame = page.frames[1] assert await frame.evaluate("compute(3, 5)") == 15 async def test_expose_function_should_work_after_cross_origin_navigation( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) await page.expose_function("compute", lambda a, b: a * b) await page.goto(server.CROSS_PROCESS_PREFIX + "/empty.html") assert await page.evaluate("compute(9, 4)") == 36 async def test_expose_function_should_work_with_complex_objects(page: Page, server: Server) -> None: await page.expose_function("complexObject", lambda a, b: dict(x=a["x"] + b["x"])) result = await page.evaluate("complexObject({x: 5}, {x: 2})") assert result["x"] == 7 async def test_expose_bindinghandle_should_work(page: Page, server: Server) -> None: targets: List[JSHandle] = [] def logme(t: JSHandle) -> int: targets.append(t) return 17 await page.expose_binding("logme", lambda source, t: logme(t), handle=True) result = await page.evaluate("logme({ foo: 42 })") assert (await targets[0].evaluate("x => x.foo")) == 42 assert result == 17 async def test_page_error_should_fire(page: Page, server: Server, browser_name: str) -> None: url = server.PREFIX + "/error.html" async with page.expect_event("pageerror") as error_info: await page.goto(url) error = await error_info.value assert error.name == "Error" assert error.message == "Fancy error!" # Note that WebKit reports the stack of the 'throw' statement instead of the Error constructor call. if browser_name == "chromium": assert ( error.stack == """Error: Fancy error! at c (myscript.js:14:11) at b (myscript.js:10:5) at a (myscript.js:6:5) at myscript.js:3:1""" ) if browser_name == "firefox": assert ( error.stack == """Error: Fancy error! at c (myscript.js:14:11) at b (myscript.js:10:5) at a (myscript.js:6:5) at (myscript.js:3:1)""" ) if browser_name == "webkit": assert ( error.stack == f"""Error: Fancy error! at c ({url}:14:36) at b ({url}:10:6) at a ({url}:6:6) at global code ({url}:3:2)""" ) async def test_page_error_should_handle_odd_values(page: Page) -> None: cases = [["null", "null"], ["undefined", "undefined"], ["0", "0"], ['""', ""]] for [value, message] in cases: async with page.expect_event("pageerror") as error_info: await page.evaluate(f"() => setTimeout(() => {{ throw {value}; }}, 0)") error = await error_info.value assert error.message == message async def test_page_error_should_handle_object(page: Page, is_chromium: bool) -> None: async with page.expect_event("pageerror") as error_info: await page.evaluate("() => setTimeout(() => { throw {}; }, 0)") error = await error_info.value assert error.message == "Object" if is_chromium else "[object Object]" async def test_page_error_should_handle_window(page: Page, is_chromium: bool) -> None: async with page.expect_event("pageerror") as error_info: await page.evaluate("() => setTimeout(() => { throw window; }, 0)") error = await error_info.value assert error.message == "Window" if is_chromium else "[object Window]" async def test_page_error_should_pass_error_name_property(page: Page) -> None: async with page.expect_event("pageerror") as error_info: await page.evaluate( """() => setTimeout(() => { const error = new Error("my-message"); error.name = "my-name"; throw error; }, 0) """ ) error = await error_info.value assert error.message == "my-message" assert error.name == "my-name" expected_output = "
hello
" async def test_set_content_should_work(page: Page, server: Server) -> None: await page.set_content("
hello
") result = await page.content() assert result == expected_output async def test_set_content_should_work_with_domcontentloaded(page: Page, server: Server) -> None: await page.set_content("
hello
", wait_until="domcontentloaded") result = await page.content() assert result == expected_output async def test_set_content_should_work_with_doctype(page: Page, server: Server) -> None: doctype = "" await page.set_content(f"{doctype}
hello
") result = await page.content() assert result == f"{doctype}{expected_output}" async def test_set_content_should_work_with_HTML_4_doctype(page: Page, server: Server) -> None: doctype = ( '' ) await page.set_content(f"{doctype}
hello
") result = await page.content() assert result == f"{doctype}{expected_output}" async def test_set_content_should_respect_timeout(page: Page, server: Server) -> None: img_path = "/img.png" # stall for image server.set_route(img_path, lambda request: None) with pytest.raises(Error) as exc_info: await page.set_content(f'', timeout=1) assert exc_info.type is TimeoutError async def test_set_content_should_respect_default_navigation_timeout( page: Page, server: Server ) -> None: page.set_default_navigation_timeout(1) img_path = "/img.png" # stall for image await page.route(img_path, lambda route, request: None) with pytest.raises(Error) as exc_info: await page.set_content(f'') assert "Timeout 1ms exceeded" in exc_info.value.message assert exc_info.type is TimeoutError async def test_set_content_should_await_resources_to_load(page: Page, server: Server) -> None: img_route: "asyncio.Future[Route]" = asyncio.Future() await page.route("**/img.png", lambda route, request: img_route.set_result(route)) loaded = [] async def load() -> None: await page.set_content(f'') loaded.append(True) content_promise = asyncio.create_task(load()) await asyncio.sleep(0) # execute scheduled tasks, but don't await them route = await img_route assert loaded == [] asyncio.create_task(route.continue_()) await content_promise async def test_set_content_should_work_with_tricky_content(page: Page) -> None: await page.set_content("
hello world
" + "\x7F") assert await page.eval_on_selector("div", "div => div.textContent") == "hello world" async def test_set_content_should_work_with_accents(page: Page) -> None: await page.set_content("
aberración
") assert await page.eval_on_selector("div", "div => div.textContent") == "aberración" async def test_set_content_should_work_with_emojis(page: Page) -> None: await page.set_content("
🐥
") assert await page.eval_on_selector("div", "div => div.textContent") == "🐥" async def test_set_content_should_work_with_newline(page: Page) -> None: await page.set_content("
\n
") assert await page.eval_on_selector("div", "div => div.textContent") == "\n" async def test_add_script_tag_should_work_with_a_url(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) script_handle = await page.add_script_tag(url="/injectedfile.js") assert script_handle.as_element() assert await page.evaluate("__injected") == 42 async def test_add_script_tag_should_work_with_a_url_and_type_module( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) await page.add_script_tag(url="/es6/es6import.js", type="module") assert await page.evaluate("__es6injected") == 42 async def test_add_script_tag_should_work_with_a_path_and_type_module( page: Page, server: Server, assetdir: Path ) -> None: await page.goto(server.EMPTY_PAGE) await page.add_script_tag(path=assetdir / "es6" / "es6pathimport.js", type="module") await page.wait_for_function("window.__es6injected") assert await page.evaluate("__es6injected") == 42 async def test_add_script_tag_should_work_with_a_content_and_type_module( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) await page.add_script_tag( content="import num from '/es6/es6module.js';window.__es6injected = num;", type="module", ) await page.wait_for_function("window.__es6injected") assert await page.evaluate("__es6injected") == 42 async def test_add_script_tag_should_throw_an_error_if_loading_from_url_fail( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) with pytest.raises(Error) as exc_info: await page.add_script_tag(url="/nonexistfile.js") assert exc_info.value async def test_add_script_tag_should_work_with_a_path( page: Page, server: Server, assetdir: Path ) -> None: await page.goto(server.EMPTY_PAGE) script_handle = await page.add_script_tag(path=assetdir / "injectedfile.js") assert script_handle.as_element() assert await page.evaluate("__injected") == 42 @pytest.mark.skip_browser("webkit") async def test_add_script_tag_should_include_source_url_when_path_is_provided( page: Page, server: Server, assetdir: Path ) -> None: # Lacking sourceURL support in WebKit await page.goto(server.EMPTY_PAGE) await page.add_script_tag(path=assetdir / "injectedfile.js") result = await page.evaluate("__injectedError.stack") assert os.path.join("assets", "injectedfile.js") in result async def test_add_script_tag_should_work_with_content(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) script_handle = await page.add_script_tag(content="window.__injected = 35;") assert script_handle.as_element() assert await page.evaluate("__injected") == 35 @pytest.mark.skip_browser("firefox") async def test_add_script_tag_should_throw_when_added_with_content_to_the_csp_page( page: Page, server: Server ) -> None: # Firefox fires onload for blocked script before it issues the CSP console error. await page.goto(server.PREFIX + "/csp.html") with pytest.raises(Error) as exc_info: await page.add_script_tag(content="window.__injected = 35;") assert exc_info.value async def test_add_script_tag_should_throw_when_added_with_URL_to_the_csp_page( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/csp.html") with pytest.raises(Error) as exc_info: await page.add_script_tag(url=server.CROSS_PROCESS_PREFIX + "/injectedfile.js") assert exc_info.value async def test_add_script_tag_should_throw_a_nice_error_when_the_request_fails( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) url = server.PREFIX + "/this_does_not_exist.js" with pytest.raises(Error) as exc_info: await page.add_script_tag(url=url) assert url in exc_info.value.message async def test_add_style_tag_should_work_with_a_url(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) style_handle = await page.add_style_tag(url="/injectedstyle.css") assert style_handle.as_element() assert ( await page.evaluate( "window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')" ) == "rgb(255, 0, 0)" ) async def test_add_style_tag_should_throw_an_error_if_loading_from_url_fail( page: Page, server: Server ) -> None: await page.goto(server.EMPTY_PAGE) with pytest.raises(Error) as exc_info: await page.add_style_tag(url="/nonexistfile.js") assert exc_info.value async def test_add_style_tag_should_work_with_a_path( page: Page, server: Server, assetdir: Path ) -> None: await page.goto(server.EMPTY_PAGE) style_handle = await page.add_style_tag(path=assetdir / "injectedstyle.css") assert style_handle.as_element() assert ( await page.evaluate( "window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')" ) == "rgb(255, 0, 0)" ) async def test_add_style_tag_should_include_source_url_when_path_is_provided( page: Page, server: Server, assetdir: Path ) -> None: await page.goto(server.EMPTY_PAGE) await page.add_style_tag(path=assetdir / "injectedstyle.css") style_handle = await page.query_selector("style") style_content = await page.evaluate("style => style.innerHTML", style_handle) assert os.path.join("assets", "injectedstyle.css") in style_content async def test_add_style_tag_should_work_with_content(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) style_handle = await page.add_style_tag(content="body { background-color: green; }") assert style_handle.as_element() assert ( await page.evaluate( "window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')" ) == "rgb(0, 128, 0)" ) async def test_add_style_tag_should_throw_when_added_with_content_to_the_CSP_page( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/csp.html") with pytest.raises(Error) as exc_info: await page.add_style_tag(content="body { background-color: green; }") assert exc_info.value async def test_add_style_tag_should_throw_when_added_with_URL_to_the_CSP_page( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/csp.html") with pytest.raises(Error) as exc_info: await page.add_style_tag(url=server.CROSS_PROCESS_PREFIX + "/injectedstyle.css") assert exc_info.value async def test_url_should_work(page: Page, server: Server) -> None: assert page.url == "about:blank" await page.goto(server.EMPTY_PAGE) assert page.url == server.EMPTY_PAGE async def test_url_should_include_hashes(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE + "#hash") assert page.url == server.EMPTY_PAGE + "#hash" await page.evaluate("window.location.hash = 'dynamic'") assert page.url == server.EMPTY_PAGE + "#dynamic" async def test_title_should_return_the_page_title(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/title.html") assert await page.title() == "Woof-Woof" async def give_it_a_chance_to_fill(page: Page) -> None: for i in range(5): await page.evaluate( "() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))" ) async def test_fill_should_fill_textarea(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.fill("textarea", "some value") assert await page.evaluate("result") == "some value" async def test_fill_should_fill_input(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.fill("input", "some value") assert await page.evaluate("result") == "some value" async def test_fill_should_throw_on_unsupported_inputs(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") for type in [ "button", "checkbox", "file", "image", "radio", "reset", "submit", ]: await page.eval_on_selector( "input", "(input, type) => input.setAttribute('type', type)", type ) with pytest.raises(Error) as exc_info: await page.fill("input", "") assert f'Input of type "{type}" cannot be filled' in exc_info.value.message async def test_fill_should_fill_different_input_types(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") for type in ["password", "search", "tel", "text", "url"]: await page.eval_on_selector( "input", "(input, type) => input.setAttribute('type', type)", type ) await page.fill("input", "text " + type) assert await page.evaluate("result") == "text " + type async def test_fill_should_fill_date_input_after_clicking(page: Page, server: Server) -> None: await page.set_content("") await page.click("input") await page.fill("input", "2020-03-02") assert await page.eval_on_selector("input", "input => input.value") == "2020-03-02" @pytest.mark.skip_browser("webkit") async def test_fill_should_throw_on_incorrect_date(page: Page, server: Server) -> None: # Disabled as in upstream, we should validate time in the Playwright lib await page.set_content("") with pytest.raises(Error) as exc_info: await page.fill("input", "2020-13-05") assert "Malformed value" in exc_info.value.message async def test_fill_should_fill_time_input(page: Page, server: Server) -> None: await page.set_content("") await page.fill("input", "13:15") assert await page.eval_on_selector("input", "input => input.value") == "13:15" @pytest.mark.skip_browser("webkit") async def test_fill_should_throw_on_incorrect_time(page: Page, server: Server) -> None: # Disabled as in upstream, we should validate time in the Playwright lib await page.set_content("") with pytest.raises(Error) as exc_info: await page.fill("input", "25:05") assert "Malformed value" in exc_info.value.message async def test_fill_should_fill_datetime_local_input(page: Page, server: Server) -> None: await page.set_content("") await page.fill("input", "2020-03-02T05:15") assert await page.eval_on_selector("input", "input => input.value") == "2020-03-02T05:15" @pytest.mark.only_browser("chromium") async def test_fill_should_throw_on_incorrect_datetime_local(page: Page) -> None: await page.set_content("") with pytest.raises(Error) as exc_info: await page.fill("input", "abc") assert "Malformed value" in exc_info.value.message async def test_fill_should_fill_contenteditable(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.fill("div[contenteditable]", "some value") assert ( await page.eval_on_selector("div[contenteditable]", "div => div.textContent") == "some value" ) async def test_fill_should_fill_elements_with_existing_value_and_selection( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.eval_on_selector("input", "input => input.value = 'value one'") await page.fill("input", "another value") assert await page.evaluate("result") == "another value" await page.eval_on_selector( "input", """input => { input.selectionStart = 1 input.selectionEnd = 2 }""", ) await page.fill("input", "maybe this one") assert await page.evaluate("result") == "maybe this one" await page.eval_on_selector( "div[contenteditable]", """div => { div.innerHTML = 'some text some more text and even more text' range = document.createRange() range.selectNodeContents(div.querySelector('span')) selection = window.getSelection() selection.removeAllRanges() selection.addRange(range) }""", ) await page.fill("div[contenteditable]", "replace with this") assert ( await page.eval_on_selector("div[contenteditable]", "div => div.textContent") == "replace with this" ) async def test_fill_should_throw_when_element_is_not_an_input_textarea_or_contenteditable( page: Page, server: Server ) -> None: await page.goto(server.PREFIX + "/input/textarea.html") with pytest.raises(Error) as exc_info: await page.fill("body", "") assert "Element is not an " in exc_info.value.message async def test_fill_should_throw_if_passed_a_non_string_value(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") with pytest.raises(Error) as exc_info: await page.fill("textarea", 123) # type: ignore assert "expected string, got number" in exc_info.value.message async def test_fill_should_retry_on_disabled_element(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.eval_on_selector("input", "i => i.disabled = true") done = [] async def fill() -> None: await page.fill("input", "some value") done.append(True) promise = asyncio.create_task(fill()) await give_it_a_chance_to_fill(page) assert done == [] assert await page.evaluate("result") == "" await page.eval_on_selector("input", "i => i.disabled = false") await promise assert await page.evaluate("result") == "some value" async def test_fill_should_retry_on_readonly_element(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.eval_on_selector("textarea", "i => i.readOnly = true") done = [] async def fill() -> None: await page.fill("textarea", "some value") done.append(True) promise = asyncio.create_task(fill()) await give_it_a_chance_to_fill(page) assert done == [] assert await page.evaluate("result") == "" await page.eval_on_selector("textarea", "i => i.readOnly = false") await promise assert await page.evaluate("result") == "some value" async def test_fill_should_retry_on_invisible_element(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.eval_on_selector("input", "i => i.style.display = 'none'") done = [] async def fill() -> None: await page.fill("input", "some value") done.append(True) promise = asyncio.create_task(fill()) await give_it_a_chance_to_fill(page) assert done == [] assert await page.evaluate("result") == "" await page.eval_on_selector("input", "i => i.style.display = 'inline'") await promise assert await page.evaluate("result") == "some value" async def test_fill_should_be_able_to_fill_the_body(page: Page) -> None: await page.set_content('') await page.fill("body", "some value") assert await page.evaluate("document.body.textContent") == "some value" async def test_fill_should_fill_fixed_position_input(page: Page) -> None: await page.set_content('') await page.fill("input", "some value") assert await page.evaluate("document.querySelector('input').value") == "some value" async def test_fill_should_be_able_to_fill_when_focus_is_in_the_wrong_frame( page: Page, ) -> None: await page.set_content( """
""" ) await page.focus("iframe") await page.fill("div", "some value") assert await page.eval_on_selector("div", "d => d.textContent") == "some value" async def test_fill_should_be_able_to_fill_the_input_type_number_(page: Page) -> None: await page.set_content('') await page.fill("input", "42") assert await page.evaluate("input.value") == "42" async def test_fill_should_be_able_to_fill_exponent_into_the_input_type_number_( page: Page, ) -> None: await page.set_content('') await page.fill("input", "-10e5") assert await page.evaluate("input.value") == "-10e5" async def test_fill_should_be_able_to_fill_input_type_number__with_empty_string( page: Page, ) -> None: await page.set_content('') await page.fill("input", "") assert await page.evaluate("input.value") == "" async def test_fill_should_not_be_able_to_fill_text_into_the_input_type_number_( page: Page, ) -> None: await page.set_content('') with pytest.raises(Error) as exc_info: await page.fill("input", "abc") assert "Cannot type text into input[type=number]" in exc_info.value.message async def test_fill_should_be_able_to_clear_using_fill(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.fill("input", "some value") assert await page.evaluate("result") == "some value" await page.fill("input", "") assert await page.evaluate("result") == "" async def test_close_event_should_work_with_window_close(page: Page, server: Server) -> None: async with page.expect_popup() as popup_info: await page.evaluate("window['newPage'] = window.open('about:blank')") popup = await popup_info.value async with popup.expect_event("close"): await page.evaluate("window['newPage'].close()") async def test_close_event_should_work_with_page_close( context: BrowserContext, server: Server ) -> None: page = await context.new_page() async with page.expect_event("close"): await page.close() async def test_page_context_should_return_the_correct_browser_instance( page: Page, context: BrowserContext ) -> None: assert page.context == context async def test_frame_should_respect_name(page: Page, server: Server) -> None: await page.set_content("") assert page.frame(name="bogus") is None frame = page.frame(name="target") assert frame assert frame == page.main_frame.child_frames[0] async def test_frame_should_respect_url(page: Page, server: Server) -> None: await page.set_content(f'') assert page.frame(url=re.compile(r"bogus")) is None assert must(page.frame(url=re.compile(r"empty"))).url == server.EMPTY_PAGE async def test_press_should_work(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.press("textarea", "a") assert await page.evaluate("document.querySelector('textarea').value") == "a" async def test_frame_press_should_work(page: Page, server: Server) -> None: await page.set_content( f'' ) frame = page.frame("inner") assert frame await frame.press("textarea", "a") assert await frame.evaluate("document.querySelector('textarea').value") == "a" async def test_should_emulate_reduced_motion(page: Page, server: Server) -> None: assert await page.evaluate("matchMedia('(prefers-reduced-motion: no-preference)').matches") await page.emulate_media(reduced_motion="reduce") assert await page.evaluate("matchMedia('(prefers-reduced-motion: reduce)').matches") assert not await page.evaluate("matchMedia('(prefers-reduced-motion: no-preference)').matches") await page.emulate_media(reduced_motion="no-preference") assert not await page.evaluate("matchMedia('(prefers-reduced-motion: reduce)').matches") assert await page.evaluate("matchMedia('(prefers-reduced-motion: no-preference)').matches") async def test_input_value(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/input/textarea.html") await page.fill("input", "my-text-content") assert await page.input_value("input") == "my-text-content" await page.fill("input", "") assert await page.input_value("input") == "" async def test_drag_and_drop_helper_method(page: Page, server: Server) -> None: await page.goto(server.PREFIX + "/drag-n-drop.html") await page.drag_and_drop("#source", "#target") assert ( await page.eval_on_selector( "#target", "target => target.contains(document.querySelector('#source'))" ) is True ) async def test_drag_and_drop_with_position(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) await page.set_content( """
""" ) events_handle = await page.evaluate_handle( """ () => { const events = []; document.getElementById('red').addEventListener('mousedown', event => { events.push({ type: 'mousedown', x: event.offsetX, y: event.offsetY, }); }); document.getElementById('blue').addEventListener('mouseup', event => { events.push({ type: 'mouseup', x: event.offsetX, y: event.offsetY, }); }); return events; } """ ) await page.drag_and_drop( "#red", "#blue", source_position={"x": 34, "y": 7}, target_position={"x": 10, "y": 20}, ) assert await events_handle.json_value() == [ {"type": "mousedown", "x": 34, "y": 7}, {"type": "mouseup", "x": 10, "y": 20}, ] async def test_should_check_box_using_set_checked(page: Page) -> None: await page.set_content("``") await page.set_checked("input", True) assert await page.evaluate("checkbox.checked") is True await page.set_checked("input", False) assert await page.evaluate("checkbox.checked") is False async def test_should_set_bodysize_and_headersize(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request("*/**") as request_info: await page.evaluate( "() => fetch('./get', { method: 'POST', body: '12345'}).then(r => r.text())" ) request = await request_info.value sizes = await request.sizes() assert sizes["requestBodySize"] == 5 assert sizes["requestHeadersSize"] >= 300 async def test_should_set_bodysize_to_0(page: Page, server: Server) -> None: await page.goto(server.EMPTY_PAGE) async with page.expect_request("*/**") as request_info: await page.evaluate("() => fetch('./get').then(r => r.text())") request = await request_info.value sizes = await request.sizes() assert sizes["requestBodySize"] == 0 assert sizes["requestHeadersSize"] >= 200 @pytest.mark.skip_browser("webkit") # https://bugs.webkit.org/show_bug.cgi?id=225281 async def test_should_emulate_forced_colors(page: Page) -> None: assert await page.evaluate("matchMedia('(forced-colors: none)').matches") await page.emulate_media(forced_colors="none") assert await page.evaluate("matchMedia('(forced-colors: none)').matches") assert not await page.evaluate("matchMedia('(forced-colors: active)').matches") await page.emulate_media(forced_colors="active") assert await page.evaluate("matchMedia('(forced-colors: active)').matches") assert not await page.evaluate("matchMedia('(forced-colors: none)').matches") async def test_should_not_throw_when_continuing_while_page_is_closing( page: Page, server: Server ) -> None: done: Optional[asyncio.Future] = None def handle_route(route: Route) -> None: nonlocal done done = asyncio.gather(route.continue_(), page.close()) await page.route("**/*", handle_route) with pytest.raises(Error): await page.goto(server.EMPTY_PAGE) await must(done) async def test_should_not_throw_when_continuing_after_page_is_closed( page: Page, server: Server ) -> None: done: "asyncio.Future[bool]" = asyncio.Future() async def handle_route(route: Route) -> None: await page.close() await route.continue_() nonlocal done done.set_result(True) await page.route("**/*", handle_route) with pytest.raises(Error): await page.goto(server.EMPTY_PAGE) await done async def test_expose_binding_should_serialize_cycles(page: Page) -> None: binding_values = [] def binding(source: Dict, o: Dict) -> None: binding_values.append(o) await page.expose_binding("log", lambda source, o: binding(source, o)) await page.evaluate("const a = {}; a.b = a; window.log(a)") assert binding_values[0]["b"] == binding_values[0] async def test_page_pause_should_reset_default_timeouts( page: Page, headless: bool, server: Server ) -> None: if not headless: pytest.skip() await page.goto(server.EMPTY_PAGE) await page.pause() with pytest.raises(Error, match="Timeout 30000ms exceeded."): await page.get_by_text("foo").click() async def test_page_pause_should_reset_custom_timeouts( page: Page, headless: bool, server: Server ) -> None: if not headless: pytest.skip() page.set_default_timeout(123) page.set_default_navigation_timeout(456) await page.goto(server.EMPTY_PAGE) await page.pause() with pytest.raises(Error, match="Timeout 123ms exceeded."): await page.get_by_text("foo").click() server.set_route("/empty.html", lambda route: None) with pytest.raises(Error, match="Timeout 456ms exceeded."): await page.goto(server.EMPTY_PAGE)