mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 21:52:04 -08:00
1057 lines
38 KiB
Python
1057 lines
38 KiB
Python
# 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 re
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any, List, Optional
|
|
|
|
import pytest
|
|
|
|
from playwright.async_api import (
|
|
BrowserContext,
|
|
Error,
|
|
Page,
|
|
Request,
|
|
Response,
|
|
Route,
|
|
TimeoutError,
|
|
)
|
|
from tests.server import Server, TestServerRequest
|
|
|
|
|
|
async def test_goto_should_work(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_goto_should_work_with_file_URL(page: Page, assetdir: Path) -> None:
|
|
fileurl = (assetdir / "frames" / "two-frames.html").as_uri()
|
|
await page.goto(fileurl)
|
|
assert page.url.lower() == fileurl.lower()
|
|
assert len(page.frames) == 3
|
|
|
|
|
|
async def test_goto_should_use_http_for_no_protocol(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE[7:])
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_goto_should_work_cross_process(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
url = server.CROSS_PROCESS_PREFIX + "/empty.html"
|
|
request_frames = []
|
|
|
|
def on_request(r: Request) -> None:
|
|
if r.url == url:
|
|
request_frames.append(r.frame)
|
|
|
|
page.on("request", on_request)
|
|
|
|
response = await page.goto(url)
|
|
assert response
|
|
assert page.url == url
|
|
assert response.frame == page.main_frame
|
|
assert request_frames[0] == page.main_frame
|
|
assert response.url == url
|
|
|
|
|
|
async def test_goto_should_capture_iframe_navigation_request(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
request_frames = []
|
|
|
|
def on_request(r: Request) -> None:
|
|
if r.url == server.PREFIX + "/frames/frame.html":
|
|
request_frames.append(r.frame)
|
|
|
|
page.on("request", on_request)
|
|
|
|
response = await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
assert response
|
|
assert page.url == server.PREFIX + "/frames/one-frame.html"
|
|
assert response.frame == page.main_frame
|
|
assert response.url == server.PREFIX + "/frames/one-frame.html"
|
|
|
|
assert len(page.frames) == 2
|
|
assert request_frames[0] == page.frames[1]
|
|
|
|
|
|
async def test_goto_should_capture_cross_process_iframe_navigation_request(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
request_frames = []
|
|
|
|
def on_request(r: Request) -> None:
|
|
if r.url == server.CROSS_PROCESS_PREFIX + "/frames/frame.html":
|
|
request_frames.append(r.frame)
|
|
|
|
page.on("request", on_request)
|
|
|
|
response = await page.goto(server.CROSS_PROCESS_PREFIX + "/frames/one-frame.html")
|
|
assert response
|
|
assert page.url == server.CROSS_PROCESS_PREFIX + "/frames/one-frame.html"
|
|
assert response.frame == page.main_frame
|
|
assert response.url == server.CROSS_PROCESS_PREFIX + "/frames/one-frame.html"
|
|
|
|
assert len(page.frames) == 2
|
|
assert request_frames[0] == page.frames[1]
|
|
|
|
|
|
async def test_goto_should_work_with_anchor_navigation(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert page.url == server.EMPTY_PAGE
|
|
await page.goto(server.EMPTY_PAGE + "#foo")
|
|
assert page.url == server.EMPTY_PAGE + "#foo"
|
|
await page.goto(server.EMPTY_PAGE + "#bar")
|
|
assert page.url == server.EMPTY_PAGE + "#bar"
|
|
|
|
|
|
async def test_goto_should_work_with_redirects(page: Page, server: Server) -> None:
|
|
server.set_redirect("/redirect/1.html", "/redirect/2.html")
|
|
server.set_redirect("/redirect/2.html", "/empty.html")
|
|
response = await page.goto(server.PREFIX + "/redirect/1.html")
|
|
assert response
|
|
assert response.status == 200
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_goto_should_navigate_to_about_blank(page: Page, server: Server) -> None:
|
|
response = await page.goto("about:blank")
|
|
assert response is None
|
|
|
|
|
|
async def test_goto_should_return_response_when_page_changes_its_url_after_load(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
response = await page.goto(server.PREFIX + "/historyapi.html")
|
|
assert response
|
|
assert response.status == 200
|
|
|
|
|
|
@pytest.mark.skip_browser("firefox")
|
|
async def test_goto_should_work_with_subframes_return_204(page: Page, server: Server) -> None:
|
|
def handle(request: TestServerRequest) -> None:
|
|
request.setResponseCode(204)
|
|
request.finish()
|
|
|
|
server.set_route("/frames/frame.html", handle)
|
|
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
|
|
|
|
async def test_goto_should_fail_when_server_returns_204(
|
|
page: Page, server: Server, is_chromium: bool, is_webkit: bool
|
|
) -> None:
|
|
# WebKit just loads an empty page.
|
|
def handle(request: TestServerRequest) -> None:
|
|
request.setResponseCode(204)
|
|
request.finish()
|
|
|
|
server.set_route("/empty.html", handle)
|
|
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert exc_info.value
|
|
if is_chromium:
|
|
assert "net::ERR_ABORTED" in exc_info.value.message
|
|
elif is_webkit:
|
|
assert "Aborted: 204 No Content" in exc_info.value.message
|
|
else:
|
|
assert "NS_BINDING_ABORTED" in exc_info.value.message
|
|
|
|
|
|
async def test_goto_should_navigate_to_empty_page_with_domcontentloaded(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
response = await page.goto(server.EMPTY_PAGE, wait_until="domcontentloaded")
|
|
assert response
|
|
assert response.status == 200
|
|
|
|
|
|
async def test_goto_should_work_when_page_calls_history_api_in_beforeunload(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.evaluate(
|
|
"""() => {
|
|
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false)
|
|
}"""
|
|
)
|
|
|
|
response = await page.goto(server.PREFIX + "/grid.html")
|
|
assert response
|
|
assert response.status == 200
|
|
|
|
|
|
async def test_goto_should_fail_when_navigating_to_bad_url(
|
|
page: Page, is_chromium: bool, is_webkit: bool
|
|
) -> None:
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto("asdfasdf")
|
|
if is_chromium or is_webkit:
|
|
assert "Cannot navigate to invalid URL" in exc_info.value.message
|
|
else:
|
|
assert "Invalid url" in exc_info.value.message
|
|
|
|
|
|
async def test_goto_should_fail_when_navigating_to_bad_ssl(
|
|
page: Page, https_server: Server, browser_name: str
|
|
) -> None:
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(https_server.EMPTY_PAGE)
|
|
expect_ssl_error(exc_info.value.message, browser_name)
|
|
|
|
|
|
async def test_goto_should_fail_when_navigating_to_bad_ssl_after_redirects(
|
|
page: Page, server: Server, https_server: Server, browser_name: str
|
|
) -> None:
|
|
server.set_redirect("/redirect/1.html", "/redirect/2.html")
|
|
server.set_redirect("/redirect/2.html", "/empty.html")
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(https_server.PREFIX + "/redirect/1.html")
|
|
expect_ssl_error(exc_info.value.message, browser_name)
|
|
|
|
|
|
async def test_goto_should_not_crash_when_navigating_to_bad_ssl_after_a_cross_origin_navigation(
|
|
page: Page, server: Server, https_server: Server
|
|
) -> None:
|
|
await page.goto(server.CROSS_PROCESS_PREFIX + "/empty.html")
|
|
with pytest.raises(Error):
|
|
await page.goto(https_server.EMPTY_PAGE)
|
|
|
|
|
|
async def test_goto_should_throw_if_networkidle2_is_passed_as_an_option(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.EMPTY_PAGE, wait_until="networkidle2") # type: ignore
|
|
assert (
|
|
"wait_until: expected one of (load|domcontentloaded|networkidle|commit)"
|
|
in exc_info.value.message
|
|
)
|
|
|
|
|
|
async def test_goto_should_fail_when_main_resources_failed_to_load(
|
|
page: Page, is_chromium: bool, is_webkit: bool, is_win: bool
|
|
) -> None:
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto("http://localhost:44123/non-existing-url")
|
|
if is_chromium:
|
|
assert "net::ERR_CONNECTION_REFUSED" in exc_info.value.message
|
|
elif is_webkit and is_win:
|
|
assert "Couldn't connect to server" in exc_info.value.message
|
|
elif is_webkit:
|
|
assert "Could not connect" in exc_info.value.message
|
|
else:
|
|
assert "NS_ERROR_CONNECTION_REFUSED" in exc_info.value.message
|
|
|
|
|
|
async def test_goto_should_fail_when_exceeding_maximum_navigation_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html", timeout=1)
|
|
assert "Timeout 1ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_fail_when_exceeding_default_maximum_navigation_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
page.context.set_default_navigation_timeout(2)
|
|
page.set_default_navigation_timeout(1)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert "Timeout 1ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_fail_when_exceeding_browser_context_navigation_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
page.context.set_default_navigation_timeout(2)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert "Timeout 2ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_fail_when_exceeding_default_maximum_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
page.context.set_default_timeout(2)
|
|
page.set_default_timeout(1)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert "Timeout 1ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_fail_when_exceeding_browser_context_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
page.context.set_default_timeout(2)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert "Timeout 2ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_prioritize_default_navigation_timeout_over_default_timeout(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
# Hang for request to the empty.html
|
|
server.set_route("/empty.html", lambda request: None)
|
|
page.set_default_timeout(0)
|
|
page.set_default_navigation_timeout(1)
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert "Timeout 1ms exceeded" in exc_info.value.message
|
|
assert server.PREFIX + "/empty.html" in exc_info.value.message
|
|
assert isinstance(exc_info.value, TimeoutError)
|
|
|
|
|
|
async def test_goto_should_disable_timeout_when_its_set_to_0(page: Page, server: Server) -> None:
|
|
loaded: List[bool] = []
|
|
page.once("load", lambda _: loaded.append(True))
|
|
await page.goto(server.PREFIX + "/grid.html", timeout=0, wait_until="load")
|
|
assert loaded == [True]
|
|
|
|
|
|
async def test_goto_should_work_when_navigating_to_valid_url(page: Page, server: Server) -> None:
|
|
response = await page.goto(server.EMPTY_PAGE)
|
|
assert response
|
|
assert response.ok
|
|
|
|
|
|
async def test_goto_should_work_when_navigating_to_data_url(page: Page, server: Server) -> None:
|
|
response = await page.goto("data:text/html,hello")
|
|
assert response is None
|
|
|
|
|
|
async def test_goto_should_work_when_navigating_to_404(page: Page, server: Server) -> None:
|
|
response = await page.goto(server.PREFIX + "/not-found")
|
|
assert response
|
|
assert response.ok is False
|
|
assert response.status == 404
|
|
|
|
|
|
async def test_goto_should_return_last_response_in_redirect_chain(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
server.set_redirect("/redirect/1.html", "/redirect/2.html")
|
|
server.set_redirect("/redirect/2.html", "/redirect/3.html")
|
|
server.set_redirect("/redirect/3.html", server.EMPTY_PAGE)
|
|
response = await page.goto(server.PREFIX + "/redirect/1.html")
|
|
assert response
|
|
assert response.ok
|
|
assert response.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_goto_should_navigate_to_data_url_and_not_fire_dataURL_requests(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
requests = []
|
|
page.on("request", lambda request: requests.append(request))
|
|
dataURL = "data:text/html,<div>yo</div>"
|
|
response = await page.goto(dataURL)
|
|
assert response is None
|
|
assert requests == []
|
|
|
|
|
|
async def test_goto_should_navigate_to_url_with_hash_and_fire_requests_without_hash(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
requests = []
|
|
page.on("request", lambda request: requests.append(request))
|
|
response = await page.goto(server.EMPTY_PAGE + "#hash")
|
|
assert response
|
|
assert response.status == 200
|
|
assert response.url == server.EMPTY_PAGE
|
|
assert len(requests) == 1
|
|
assert requests[0].url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_goto_should_work_with_self_requesting_page(page: Page, server: Server) -> None:
|
|
response = await page.goto(server.PREFIX + "/self-request.html")
|
|
assert response
|
|
assert response.status == 200
|
|
assert "self-request.html" in response.url
|
|
|
|
|
|
async def test_goto_should_fail_when_navigating_and_show_the_url_at_the_error_message(
|
|
page: Page, https_server: Server
|
|
) -> None:
|
|
url = https_server.PREFIX + "/redirect/1.html"
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(url)
|
|
assert url in exc_info.value.message
|
|
|
|
|
|
async def test_goto_should_be_able_to_navigate_to_a_page_controlled_by_service_worker(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.PREFIX + "/serviceworkers/fetch/sw.html")
|
|
await page.evaluate("window.activationPromise")
|
|
await page.goto(server.PREFIX + "/serviceworkers/fetch/sw.html")
|
|
|
|
|
|
async def test_goto_should_send_referer(page: Page, server: Server) -> None:
|
|
[request1, request2, _] = await asyncio.gather(
|
|
server.wait_for_request("/grid.html"),
|
|
server.wait_for_request("/digits/1.png"),
|
|
page.goto(server.PREFIX + "/grid.html", referer="http://google.com/"),
|
|
)
|
|
assert request1.getHeader("referer") == "http://google.com/"
|
|
# Make sure subresources do not inherit referer.
|
|
assert request2.getHeader("referer") == server.PREFIX + "/grid.html"
|
|
assert page.url == server.PREFIX + "/grid.html"
|
|
|
|
|
|
async def test_goto_should_reject_referer_option_when_set_extra_http_headers_provides_referer(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.set_extra_http_headers({"referer": "http://microsoft.com/"})
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(server.PREFIX + "/grid.html", referer="http://google.com/")
|
|
assert '"referer" is already specified as extra HTTP header' in exc_info.value.message
|
|
assert server.PREFIX + "/grid.html" in exc_info.value.message
|
|
|
|
|
|
async def test_goto_should_work_with_commit(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE, wait_until="commit")
|
|
assert page.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_network_idle_should_navigate_to_empty_page_with_networkidle(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
response = await page.goto(server.EMPTY_PAGE, wait_until="networkidle")
|
|
assert response
|
|
assert response.status == 200
|
|
|
|
|
|
async def test_wait_for_nav_should_work(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_navigation() as response_info:
|
|
await page.evaluate("url => window.location.href = url", server.PREFIX + "/grid.html")
|
|
response = await response_info.value
|
|
assert response.ok
|
|
assert "grid.html" in response.url
|
|
|
|
|
|
async def test_wait_for_nav_should_respect_timeout(page: Page, server: Server) -> None:
|
|
with pytest.raises(Error) as exc_info:
|
|
async with page.expect_navigation(url="**/frame.html", timeout=2500):
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert "Timeout 2500ms exceeded" in exc_info.value.message
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_both_domcontentloaded_and_load(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
async with page.expect_navigation(wait_until="domcontentloaded"), page.expect_navigation(
|
|
wait_until="load"
|
|
):
|
|
await page.goto(server.PREFIX + "/one-style.html")
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_clicking_on_anchor_links(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content('<a href="#foobar">foobar</a>')
|
|
async with page.expect_navigation() as response_info:
|
|
await page.click("a")
|
|
response = await response_info.value
|
|
assert response is None
|
|
assert page.url == server.EMPTY_PAGE + "#foobar"
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_clicking_on_links_which_do_not_commit_navigation(
|
|
page: Page, server: Server, https_server: Server, browser_name: str
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content(f"<a href='{https_server.EMPTY_PAGE}'>foobar</a>")
|
|
with pytest.raises(Error) as exc_info:
|
|
async with page.expect_navigation():
|
|
await page.click("a")
|
|
expect_ssl_error(exc_info.value.message, browser_name)
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_history_push_state(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content(
|
|
"""
|
|
<a onclick='javascript:pushState()'>SPA</a>
|
|
<script>
|
|
function pushState() { history.pushState({}, '', 'wow.html') }
|
|
</script>
|
|
"""
|
|
)
|
|
async with page.expect_navigation() as response_info:
|
|
await page.click("a")
|
|
response = await response_info.value
|
|
assert response is None
|
|
assert page.url == server.PREFIX + "/wow.html"
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_history_replace_state(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content(
|
|
"""
|
|
<a onclick='javascript:replaceState()'>SPA</a>
|
|
<script>
|
|
function replaceState() { history.replaceState({}, '', '/replaced.html') }
|
|
</script>
|
|
"""
|
|
)
|
|
async with page.expect_navigation() as response_info:
|
|
await page.click("a")
|
|
response = await response_info.value
|
|
assert response is None
|
|
assert page.url == server.PREFIX + "/replaced.html"
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_dom_history_back_forward(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content(
|
|
"""
|
|
<a id=back onclick='javascript:go_back()'>back</a>
|
|
<a id=forward onclick='javascript:go_forward()'>forward</a>
|
|
<script>
|
|
function go_back() { history.back(); }
|
|
function go_forward() { history.forward(); }
|
|
history.pushState({}, '', '/first.html')
|
|
history.pushState({}, '', '/second.html')
|
|
</script>
|
|
"""
|
|
)
|
|
assert page.url == server.PREFIX + "/second.html"
|
|
async with page.expect_navigation() as back_response_info:
|
|
await page.click("a#back")
|
|
back_response = await back_response_info.value
|
|
assert back_response is None
|
|
assert page.url == server.PREFIX + "/first.html"
|
|
async with page.expect_navigation() as forward_response_info:
|
|
await page.click("a#forward")
|
|
forward_response = await forward_response_info.value
|
|
assert forward_response is None
|
|
assert page.url == server.PREFIX + "/second.html"
|
|
|
|
|
|
@pytest.mark.skip_browser("webkit") # WebKit issues load event in some cases, but not always
|
|
async def test_wait_for_nav_should_work_when_subframe_issues_window_stop(
|
|
page: Page, server: Server, is_webkit: bool
|
|
) -> None:
|
|
server.set_route("/frames/style.css", lambda _: None)
|
|
done = False
|
|
|
|
async def nav_and_mark_done() -> None:
|
|
nonlocal done
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
done = True
|
|
|
|
task = asyncio.create_task(nav_and_mark_done())
|
|
await asyncio.sleep(0)
|
|
async with page.expect_event("frameattached") as frame_info:
|
|
pass
|
|
frame = await frame_info.value
|
|
|
|
async with page.expect_event("framenavigated", lambda f: f == frame):
|
|
pass
|
|
await frame.evaluate("() => window.stop()")
|
|
await page.wait_for_timeout(2000) # give it some time to erroneously resolve
|
|
assert done == (not is_webkit) # Chromium and Firefox issue load event in this case.
|
|
if is_webkit:
|
|
task.cancel()
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_url_match(page: Page, server: Server) -> None:
|
|
responses: List[Optional[Response]] = [None, None, None]
|
|
|
|
async def wait_for_nav(url: Any, index: int) -> None:
|
|
async with page.expect_navigation(url=url) as response_info:
|
|
pass
|
|
responses[index] = await response_info.value
|
|
|
|
response0_promise = asyncio.create_task(wait_for_nav(re.compile(r"one-style\.html"), 0))
|
|
response1_promise = asyncio.create_task(wait_for_nav(re.compile(r"\/frame.html"), 1))
|
|
response2_promise = asyncio.create_task(wait_for_nav(lambda url: "foo=bar" in url, 2))
|
|
assert responses == [None, None, None]
|
|
await page.goto(server.EMPTY_PAGE)
|
|
assert responses == [None, None, None]
|
|
await page.goto(server.PREFIX + "/frame.html")
|
|
assert responses[0] is None
|
|
await response1_promise
|
|
assert responses[1] is not None
|
|
assert responses[2] is None
|
|
await page.goto(server.PREFIX + "/one-style.html")
|
|
await response0_promise
|
|
assert responses[0] is not None
|
|
assert responses[1] is not None
|
|
assert responses[2] is None
|
|
await page.goto(server.PREFIX + "/frame.html?foo=bar")
|
|
await response2_promise
|
|
assert responses[0] is not None
|
|
assert responses[1] is not None
|
|
assert responses[2] is not None
|
|
await page.goto(server.PREFIX + "/empty.html")
|
|
assert responses[0].url == server.PREFIX + "/one-style.html"
|
|
assert responses[1].url == server.PREFIX + "/frame.html"
|
|
assert responses[2].url == server.PREFIX + "/frame.html?foo=bar"
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_url_match_for_same_document_navigations(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_navigation(url=re.compile(r"third\.html")) as response_info:
|
|
assert not response_info.is_done()
|
|
await page.evaluate("history.pushState({}, '', '/first.html')")
|
|
assert not response_info.is_done()
|
|
await page.evaluate("history.pushState({}, '', '/second.html')")
|
|
assert not response_info.is_done()
|
|
await page.evaluate("history.pushState({}, '', '/third.html')")
|
|
assert response_info.is_done()
|
|
|
|
|
|
async def test_wait_for_nav_should_work_for_cross_process_navigations(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
url = server.CROSS_PROCESS_PREFIX + "/empty.html"
|
|
async with page.expect_navigation(wait_until="domcontentloaded") as response_info:
|
|
await page.goto(url)
|
|
response = await response_info.value
|
|
assert response.url == url
|
|
assert page.url == url
|
|
assert await page.evaluate("document.location.href") == url
|
|
|
|
|
|
async def test_expect_navigation_should_work_for_cross_process_navigations(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
url = server.CROSS_PROCESS_PREFIX + "/empty.html"
|
|
async with page.expect_navigation(wait_until="domcontentloaded") as response_info:
|
|
goto_task = asyncio.create_task(page.goto(url))
|
|
response = await response_info.value
|
|
assert response.url == url
|
|
assert page.url == url
|
|
assert await page.evaluate("document.location.href") == url
|
|
await goto_task
|
|
|
|
|
|
async def test_wait_for_nav_should_work_with_commit(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_navigation(wait_until="commit") as response_info:
|
|
await page.evaluate("url => window.location.href = url", server.PREFIX + "/grid.html")
|
|
response = await response_info.value
|
|
assert response.ok
|
|
assert "grid.html" in response.url
|
|
|
|
|
|
async def test_wait_for_load_state_should_respect_timeout(page: Page, server: Server) -> None:
|
|
requests = []
|
|
|
|
def handler(request: Any) -> None:
|
|
requests.append(request)
|
|
|
|
server.set_route("/one-style.css", handler)
|
|
|
|
await page.goto(server.PREFIX + "/one-style.html", wait_until="domcontentloaded")
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.wait_for_load_state("load", timeout=1)
|
|
assert "Timeout 1ms exceeded." in exc_info.value.message
|
|
|
|
|
|
async def test_wait_for_load_state_should_resolve_immediately_if_loaded(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.PREFIX + "/one-style.html")
|
|
await page.wait_for_load_state()
|
|
|
|
|
|
async def test_wait_for_load_state_should_throw_for_bad_state(page: Page, server: Server) -> None:
|
|
await page.goto(server.PREFIX + "/one-style.html")
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.wait_for_load_state("bad") # type: ignore
|
|
assert (
|
|
"state: expected one of (load|domcontentloaded|networkidle|commit)"
|
|
in exc_info.value.message
|
|
)
|
|
|
|
|
|
async def test_wait_for_load_state_should_resolve_immediately_if_load_state_matches(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
|
|
requests = []
|
|
|
|
def handler(request: Any) -> None:
|
|
requests.append(request)
|
|
|
|
server.set_route("/one-style.css", handler)
|
|
|
|
await page.goto(server.PREFIX + "/one-style.html", wait_until="domcontentloaded")
|
|
await page.wait_for_load_state("domcontentloaded")
|
|
|
|
|
|
async def test_wait_for_load_state_networkidle(page: Page, server: Server) -> None:
|
|
wait_for_network_idle_future = asyncio.create_task(page.wait_for_load_state("networkidle"))
|
|
await page.goto(server.PREFIX + "/networkidle.html")
|
|
await wait_for_network_idle_future
|
|
|
|
|
|
async def test_wait_for_load_state_should_work_with_pages_that_have_loaded_before_being_connected_to(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate("window._popup = window.open(document.location.href)")
|
|
|
|
# The url is about:blank in FF.
|
|
popup = await popup_info.value
|
|
assert popup.url == server.EMPTY_PAGE
|
|
await popup.wait_for_load_state()
|
|
assert popup.url == server.EMPTY_PAGE
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_empty_url_popup(
|
|
page: Page, is_firefox: bool
|
|
) -> None:
|
|
ready_state = []
|
|
async with page.expect_popup() as popup_info:
|
|
ready_state.append(
|
|
await page.evaluate(
|
|
"""() => {
|
|
popup = window.open('')
|
|
return popup.document.readyState
|
|
}"""
|
|
)
|
|
)
|
|
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert ready_state == ["uninitialized"] if is_firefox else ["complete"]
|
|
assert await popup.evaluate("() => document.readyState") == ready_state[0]
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_about_blank_popup_(
|
|
page: Page,
|
|
) -> None:
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate("window.open('about:blank') && 1")
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert await popup.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_about_blank_popup_with_noopener(
|
|
page: Page,
|
|
) -> None:
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate("window.open('about:blank', null, 'noopener') && 1")
|
|
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert await popup.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_popup_with_network_url_(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate("url => window.open(url) && 1", server.EMPTY_PAGE)
|
|
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert await popup.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_popup_with_network_url_and_noopener_(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate("url => window.open(url, null, 'noopener') && 1", server.EMPTY_PAGE)
|
|
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert await popup.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_should_work_with_clicking_target__blank(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.set_content('<a target=_blank rel="opener" href="/one-style.html">yo</a>')
|
|
async with page.expect_popup() as popup_info:
|
|
await page.click("a")
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert await popup.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_should_wait_for_load_state_of_new_page(
|
|
context: BrowserContext,
|
|
) -> None:
|
|
async with context.expect_page() as page_info:
|
|
await context.new_page()
|
|
new_page = await page_info.value
|
|
await new_page.wait_for_load_state()
|
|
assert await new_page.evaluate("document.readyState") == "complete"
|
|
|
|
|
|
async def test_wait_for_load_state_in_popup(context: BrowserContext, server: Server) -> None:
|
|
page = await context.new_page()
|
|
await page.goto(server.EMPTY_PAGE)
|
|
css_requests = []
|
|
|
|
def handle_request(request: TestServerRequest) -> None:
|
|
css_requests.append(request)
|
|
request.write(b"body {}")
|
|
request.finish()
|
|
|
|
server.set_route("/one-style.css", handle_request)
|
|
|
|
async with page.expect_popup() as popup_info:
|
|
await page.evaluate(
|
|
"url => window.popup = window.open(url)", server.PREFIX + "/one-style.html"
|
|
)
|
|
|
|
popup = await popup_info.value
|
|
await popup.wait_for_load_state()
|
|
assert len(css_requests)
|
|
|
|
|
|
async def test_go_back_should_work(page: Page, server: Server) -> None:
|
|
assert await page.go_back() is None
|
|
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.goto(server.PREFIX + "/grid.html")
|
|
|
|
response = await page.go_back()
|
|
assert response
|
|
assert response.ok
|
|
assert server.EMPTY_PAGE in response.url
|
|
|
|
response = await page.go_forward()
|
|
assert response
|
|
assert response.ok
|
|
assert "/grid.html" in response.url
|
|
|
|
response = await page.go_forward()
|
|
assert response is None
|
|
|
|
|
|
async def test_go_back_should_work_with_history_api(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.evaluate(
|
|
"""() => {
|
|
history.pushState({}, '', '/first.html')
|
|
history.pushState({}, '', '/second.html')
|
|
}"""
|
|
)
|
|
assert page.url == server.PREFIX + "/second.html"
|
|
|
|
await page.go_back()
|
|
assert page.url == server.PREFIX + "/first.html"
|
|
await page.go_back()
|
|
assert page.url == server.EMPTY_PAGE
|
|
await page.go_forward()
|
|
assert page.url == server.PREFIX + "/first.html"
|
|
|
|
|
|
async def test_frame_goto_should_navigate_subframes(page: Page, server: Server) -> None:
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
assert "/frames/one-frame.html" in page.frames[0].url
|
|
assert "/frames/frame.html" in page.frames[1].url
|
|
|
|
response = await page.frames[1].goto(server.EMPTY_PAGE)
|
|
assert response
|
|
assert response.ok
|
|
assert response.frame == page.frames[1]
|
|
|
|
|
|
async def test_frame_goto_should_reject_when_frame_detaches(
|
|
page: Page, server: Server, browser_name: str
|
|
) -> None:
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
|
|
server.set_route("/one-style.css", lambda _: None)
|
|
wait_for_request_task = asyncio.create_task(server.wait_for_request("/one-style.css"))
|
|
navigation_task = asyncio.create_task(page.frames[1].goto(server.PREFIX + "/one-style.html"))
|
|
await wait_for_request_task
|
|
|
|
await page.eval_on_selector("iframe", "frame => frame.remove()")
|
|
with pytest.raises(Error) as exc_info:
|
|
await navigation_task
|
|
if browser_name == "chromium":
|
|
assert "net::ERR_FAILED" in exc_info.value.message or (
|
|
"frame was detached" in exc_info.value.message.lower()
|
|
)
|
|
else:
|
|
assert "frame was detached" in exc_info.value.message.lower()
|
|
|
|
|
|
async def test_frame_goto_should_continue_after_client_redirect(page: Page, server: Server) -> None:
|
|
server.set_route("/frames/script.js", lambda _: None)
|
|
url = server.PREFIX + "/frames/child-redirect.html"
|
|
|
|
with pytest.raises(Error) as exc_info:
|
|
await page.goto(url, timeout=5000, wait_until="networkidle")
|
|
|
|
assert "Timeout 5000ms exceeded." in exc_info.value.message
|
|
assert f'navigating to "{url}", waiting until "networkidle"' in exc_info.value.message
|
|
|
|
|
|
async def test_frame_wait_for_nav_should_work(page: Page, server: Server) -> None:
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
frame = page.frames[1]
|
|
async with frame.expect_navigation() as response_info:
|
|
await frame.evaluate("url => window.location.href = url", server.PREFIX + "/grid.html")
|
|
response = await response_info.value
|
|
assert response.ok
|
|
assert "grid.html" in response.url
|
|
assert response.frame == frame
|
|
assert "/frames/one-frame.html" in page.url
|
|
|
|
|
|
async def test_frame_wait_for_nav_should_fail_when_frame_detaches(
|
|
page: Page, server: Server
|
|
) -> None:
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
frame = page.frames[1]
|
|
server.set_route("/empty.html", lambda _: None)
|
|
server.set_route("/one-style.css", lambda _: None)
|
|
with pytest.raises(Error) as exc_info:
|
|
async with frame.expect_navigation():
|
|
|
|
async def after_it() -> None:
|
|
await server.wait_for_request("/one-style.html")
|
|
await page.eval_on_selector(
|
|
"iframe", "frame => setTimeout(() => frame.remove(), 0)"
|
|
)
|
|
|
|
await asyncio.gather(
|
|
page.eval_on_selector(
|
|
"iframe",
|
|
"frame => frame.contentWindow.location.href = '/one-style.html'",
|
|
),
|
|
after_it(),
|
|
)
|
|
assert "frame was detached" in exc_info.value.message
|
|
|
|
|
|
async def test_frame_wait_for_load_state_should_work(page: Page, server: Server) -> None:
|
|
await page.goto(server.PREFIX + "/frames/one-frame.html")
|
|
frame = page.frames[1]
|
|
|
|
request_future: "asyncio.Future[Route]" = asyncio.Future()
|
|
await page.route(
|
|
server.PREFIX + "/one-style.css",
|
|
lambda route, request: request_future.set_result(route),
|
|
)
|
|
|
|
await frame.goto(server.PREFIX + "/one-style.html", wait_until="domcontentloaded")
|
|
request = await request_future
|
|
load_task = asyncio.create_task(frame.wait_for_load_state())
|
|
# give the promise a chance to resolve, even though it shouldn't
|
|
await page.evaluate("1")
|
|
assert not load_task.done()
|
|
asyncio.create_task(request.continue_())
|
|
await load_task
|
|
|
|
|
|
async def test_reload_should_work(page: Page, server: Server) -> None:
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.evaluate("window._foo = 10")
|
|
await page.reload()
|
|
assert await page.evaluate("window._foo") is None
|
|
|
|
|
|
async def test_reload_should_work_with_data_url(page: Page, server: Server) -> None:
|
|
await page.goto("data:text/html,hello")
|
|
assert "hello" in await page.content()
|
|
assert await page.reload() is None
|
|
assert "hello" in await page.content()
|
|
|
|
|
|
async def test_should_work_with__blank_target(page: Page, server: Server) -> None:
|
|
def handler(request: TestServerRequest) -> None:
|
|
request.write(f'<a href="{server.EMPTY_PAGE}" target="_blank">Click me</a>'.encode())
|
|
request.finish()
|
|
|
|
server.set_route("/empty.html", handler)
|
|
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.click('"Click me"')
|
|
|
|
|
|
async def test_should_work_with_cross_process__blank_target(page: Page, server: Server) -> None:
|
|
def handler(request: TestServerRequest) -> None:
|
|
request.write(
|
|
f'<a href="{server.CROSS_PROCESS_PREFIX}/empty.html" target="_blank">Click me</a>'.encode()
|
|
)
|
|
request.finish()
|
|
|
|
server.set_route("/empty.html", handler)
|
|
|
|
await page.goto(server.EMPTY_PAGE)
|
|
await page.click('"Click me"')
|
|
|
|
|
|
def expect_ssl_error(error_message: str, browser_name: str) -> None:
|
|
if browser_name == "chromium":
|
|
assert "net::ERR_CERT_AUTHORITY_INVALID" in error_message
|
|
elif browser_name == "webkit":
|
|
if sys.platform == "darwin":
|
|
assert "The certificate for this server is invalid" in error_message
|
|
elif sys.platform == "win32":
|
|
assert "SSL peer certificate or SSH remote key was not OK" in error_message
|
|
else:
|
|
assert "Unacceptable TLS certificate" in error_message
|
|
else:
|
|
assert "SSL_ERROR_UNKNOWN" in error_message
|