Repository: microsoft/playwright-python

Issue:
Test coverage plan
- [x] browser
- [x] defaultbrowsercontext wip

---
- [x] cookies
- [x] emulation
- [x] dispatchevent
- [x] headful
- [x] dialog
- [x] focus
- [x] geolocation
- [x] accessibility
- [x] add_init_script
- [x] browsercontext
- [x] check
- [x] click
- [x] console
- [x] download
- [x] element_handle
- [x] evaluate
- [x] fill
- [x] frames
- [x] generation
- [x] ignore_https_errors
- [x] input
- [x] interception
- [x] jshandle
- [x] keyboard
- [x] launcher
- [x] navigation
- [x] network
- [x] page
- [x] pdf
- [x] popup
- [x] queryselector
- [x] worker



Discussion:
# missing tests

## inputs.spec.js

- [ ] should detect mime type
- [ ] should respect timeout
- [ ] should respect default timeout when there is no custom timeout
- [ ] should prioritize exact timeout over default timeout
- [ ] should work with no timeout

reason: timeout needs to be implemented and setRoute too on the mockserver.

## downloads.spec.js
- [x] should expose stream

## network.spec.js
- [x] test_response_text_should_wait_until_responses_completes  - https://github.com/microsoft/playwright-python/pull/44#issue-447818639 is missing

## keyboard.spec.js

- [x] mostly missing
- [x]Request.postDataJSON is missing


Resolving patch:
diff --git a/playwright/browser_type.py b/playwright/browser_type.py
--- a/playwright/browser_type.py
+++ b/playwright/browser_type.py
@@ -29,6 +29,7 @@
     locals_to_params,
     not_installed_error,
 )
+from playwright.network import serialize_headers
 
 
 class BrowserType(ChannelOwner):
@@ -134,6 +135,8 @@ async def launchPersistentContext(
     ) -> BrowserContext:
         userDataDir = str(Path(userDataDir))
         params = locals_to_params(locals())
+        if extraHTTPHeaders:
+            params["extraHTTPHeaders"] = serialize_headers(extraHTTPHeaders)
         normalize_launch_params(params)
         try:
             return from_channel(


Test patch:
diff --git a/tests/async/test_defaultbrowsercontext.py b/tests/async/test_defaultbrowsercontext.py
new file mode 100644
--- /dev/null
+++ b/tests/async/test_defaultbrowsercontext.py
@@ -0,0 +1,376 @@
+# 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 pytest
+
+from playwright.helper import Error
+
+
+@pytest.fixture()
+async def launch_persistent(tmpdir, launch_arguments, browser_type):
+    context = None
+
+    async def _launch(**options):
+        nonlocal context
+        if context:
+            raise ValueError("can only launch one persitent context")
+        context = await browser_type.launchPersistentContext(
+            str(tmpdir), **{**launch_arguments, **options}
+        )
+        return (context.pages[0], context)
+
+    yield _launch
+    await context.close()
+
+
+async def test_context_cookies_should_work(server, launch_persistent):
+    (page, context) = await launch_persistent()
+    await page.goto(server.EMPTY_PAGE)
+    document_cookie = await page.evaluate(
+        """() => {
+    document.cookie = 'username=John Doe';
+    return document.cookie;
+  }"""
+    )
+
+    assert document_cookie == "username=John Doe"
+    assert await page.context.cookies() == [
+        {
+            "name": "username",
+            "value": "John Doe",
+            "domain": "localhost",
+            "path": "/",
+            "expires": -1,
+            "httpOnly": False,
+            "secure": False,
+            "sameSite": "None",
+        }
+    ]
+
+
+async def test_context_add_cookies_should_work(server, launch_persistent):
+    (page, context) = await launch_persistent()
+    await page.goto(server.EMPTY_PAGE)
+    await page.context.addCookies(
+        [{"url": server.EMPTY_PAGE, "name": "username", "value": "John Doe"}]
+    )
+    assert await page.evaluate("() => document.cookie") == "username=John Doe"
+    assert await page.context.cookies() == [
+        {
+            "name": "username",
+            "value": "John Doe",
+            "domain": "localhost",
+            "path": "/",
+            "expires": -1,
+            "httpOnly": False,
+            "secure": False,
+            "sameSite": "None",
+        }
+    ]
+
+
+async def test_context_clear_cookies_should_work(server, launch_persistent):
+    (page, context) = await launch_persistent()
+    await page.goto(server.EMPTY_PAGE)
+    await page.context.addCookies(
+        [
+            {"url": server.EMPTY_PAGE, "name": "cookie1", "value": "1"},
+            {"url": server.EMPTY_PAGE, "name": "cookie2", "value": "2"},
+        ]
+    )
+    assert await page.evaluate("document.cookie") == "cookie1=1; cookie2=2"
+    await page.context.clearCookies()
+    await page.reload()
+    assert await page.context.cookies([]) == []
+    assert await page.evaluate("document.cookie") == ""
+
+
+async def test_should_not_block_third_party_cookies(
+    server, launch_persistent, is_chromium, is_firefox
+):
+    (page, context) = await launch_persistent()
+    await page.goto(server.EMPTY_PAGE)
+    await page.evaluate(
+        """src => {
+    let fulfill;
+    const promise = new Promise(x => fulfill = x);
+    const iframe = document.createElement('iframe');
+    document.body.appendChild(iframe);
+    iframe.onload = fulfill;
+    iframe.src = src;
+    return promise;
+  }""",
+        server.CROSS_PROCESS_PREFIX + "/grid.html",
+    )
+    document_cookie = await page.frames[1].evaluate(
+        """() => {
+    document.cookie = 'username=John Doe';
+    return document.cookie;
+  }"""
+    )
+
+    await page.waitForTimeout(2000)
+    allows_third_party = is_chromium or is_firefox
+    assert document_cookie == ("username=John Doe" if allows_third_party else "")
+    cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + "/grid.html")
+    if allows_third_party:
+        assert cookies == [
+            {
+                "domain": "127.0.0.1",
+                "expires": -1,
+                "httpOnly": False,
+                "name": "username",
+                "path": "/",
+                "sameSite": "None",
+                "secure": False,
+                "value": "John Doe",
+            }
+        ]
+    else:
+        assert cookies == []
+
+
+async def test_should_support_viewport_option(launch_persistent, utils):
+    (page, context) = await launch_persistent(viewport={"width": 456, "height": 789})
+    await utils.verify_viewport(page, 456, 789)
+    page2 = await context.newPage()
+    await utils.verify_viewport(page2, 456, 789)
+
+
+async def test_should_support_device_scale_factor_option(launch_persistent, utils):
+    (page, context) = await launch_persistent(deviceScaleFactor=3)
+    assert await page.evaluate("window.devicePixelRatio") == 3
+
+
+async def test_should_support_user_agent_option(launch_persistent, server):
+    (page, context) = await launch_persistent(userAgent="foobar")
+    assert await page.evaluate("() => navigator.userAgent") == "foobar"
+    [request, _] = await asyncio.gather(
+        server.wait_for_request("/empty.html"), page.goto(server.EMPTY_PAGE),
+    )
+    assert request.getHeader("user-agent") == "foobar"
+
+
+async def test_should_support_bypass_csp_option(launch_persistent, server):
+    (page, context) = await launch_persistent(bypassCSP=True)
+    await page.goto(server.PREFIX + "/csp.html")
+    await page.addScriptTag(content="window.__injected = 42;")
+    assert await page.evaluate("() => window.__injected") == 42
+
+
+async def test_should_support_javascript_enabled_option(launch_persistent, is_webkit):
+    (page, context) = await launch_persistent(javaScriptEnabled=False)
+    await page.goto('data:text/html, <script>var something = "forbidden"</script>')
+    with pytest.raises(Error) as exc:
+        await page.evaluate("something")
+    if is_webkit:
+        assert "Can't find variable: something" in exc.value.message
+    else:
+        assert "something is not defined" in exc.value.message
+
+
+async def test_should_support_http_credentials_option(server, launch_persistent):
+    (page, context) = await launch_persistent(
+        httpCredentials={"username": "user", "password": "pass"}
+    )
+    server.set_auth("/playground.html", b"user", b"pass")
+    response = await page.goto(server.PREFIX + "/playground.html")
+    assert response.status == 200
+
+
+async def test_should_support_offline_option(server, launch_persistent):
+    (page, context) = await launch_persistent(offline=True)
+    with pytest.raises(Error):
+        await page.goto(server.EMPTY_PAGE)
+
+
+async def test_should_support_has_touch_option(server, launch_persistent):
+    (page, context) = await launch_persistent(hasTouch=True)
+    await page.goto(server.PREFIX + "/mobile.html")
+    assert await page.evaluate('() => "ontouchstart" in window')
+
+
+@pytest.mark.skip_browser("firefox")
+async def test_should_work_in_persistent_context(server, launch_persistent):
+    # Firefox does not support mobile.
+    (page, context) = await launch_persistent(
+        viewport={"width": 320, "height": 480}, isMobile=True
+    )
+    await page.goto(server.PREFIX + "/empty.html")
+    assert await page.evaluate("() => window.innerWidth") == 980
+
+
+async def test_should_support_color_scheme_option(server, launch_persistent):
+    (page, context) = await launch_persistent(colorScheme="dark")
+    assert (
+        await page.evaluate('() => matchMedia("(prefers-color-scheme: light)").matches')
+        is False
+    )
+    assert await page.evaluate(
+        '() => matchMedia("(prefers-color-scheme: dark)").matches'
+    )
+
+
+async def test_should_support_timezone_id_option(launch_persistent):
+    (page, context) = await launch_persistent(timezoneId="America/Jamaica")
+    assert (
+        await page.evaluate("() => new Date(1479579154987).toString()")
+        == "Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)"
+    )
+
+
+async def test_should_support_locale_option(launch_persistent):
+    (page, context) = await launch_persistent(locale="fr-CH")
+    assert await page.evaluate("() => navigator.language") == "fr-CH"
+
+
+async def test_should_support_geolocation_and_permission_option(
+    server, launch_persistent
+):
+    (page, context) = await launch_persistent(
+        geolocation={"longitude": 10, "latitude": 10}, permissions=["geolocation"]
+    )
+    await page.goto(server.EMPTY_PAGE)
+    geolocation = await page.evaluate(
+        """() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
+    resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
+  }))"""
+    )
+    assert geolocation == {"latitude": 10, "longitude": 10}
+
+
+async def test_should_support_ignore_https_errors_option(
+    https_server, launch_persistent
+):
+    (page, context) = await launch_persistent(ignoreHTTPSErrors=True)
+    response = await page.goto(https_server.EMPTY_PAGE)
+    assert response.ok
+
+
+async def test_should_support_extra_http_headers_option(server, launch_persistent):
+    (page, context) = await launch_persistent(extraHTTPHeaders={"foo": "bar"})
+    [request, _] = await asyncio.gather(
+        server.wait_for_request("/empty.html"), page.goto(server.EMPTY_PAGE),
+    )
+    assert request.getHeader("foo") == "bar"
+
+
+async def test_should_accept_user_data_dir(server, tmpdir, launch_persistent):
+    (page, context) = await launch_persistent()
+    # Note: we need an open page to make sure its functional.
+    assert len(os.listdir(tmpdir)) > 0
+    await context.close()
+    assert len(os.listdir(tmpdir)) > 0
+
+
+async def test_should_restore_state_from_userDataDir(
+    browser_type, launch_arguments, server, tmp_path_factory
+):
+    user_data_dir1 = tmp_path_factory.mktemp("test")
+    browser_context = await browser_type.launchPersistentContext(
+        user_data_dir1, **launch_arguments
+    )
+    page = await browser_context.newPage()
+    await page.goto(server.EMPTY_PAGE)
+    await page.evaluate('() => localStorage.hey = "hello"')
+    await browser_context.close()
+
+    browser_context2 = await browser_type.launchPersistentContext(
+        user_data_dir1, **launch_arguments
+    )
+    page2 = await browser_context2.newPage()
+    await page2.goto(server.EMPTY_PAGE)
+    assert await page2.evaluate("() => localStorage.hey") == "hello"
+    await browser_context2.close()
+
+    user_data_dir2 = tmp_path_factory.mktemp("test")
+    browser_context3 = await browser_type.launchPersistentContext(
+        user_data_dir2, **launch_arguments
+    )
+    page3 = await browser_context3.newPage()
+    await page3.goto(server.EMPTY_PAGE)
+    assert await page3.evaluate("() => localStorage.hey") != "hello"
+    await browser_context3.close()
+
+
+async def test_should_restore_cookies_from_userDataDir(
+    browser_type,
+    launch_arguments,
+    tmp_path_factory,
+    server,
+    is_chromium,
+    is_win,
+    is_mac,
+):
+    if is_chromium and (is_win or is_mac):
+        pytest.skip()
+    userDataDir = tmp_path_factory.mktemp("1")
+    browser_context = await browser_type.launchPersistentContext(
+        userDataDir, **launch_arguments
+    )
+    page = await browser_context.newPage()
+    await page.goto(server.EMPTY_PAGE)
+    document_cookie = await page.evaluate(
+        """() => {
+    document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT';
+    return document.cookie;
+  }"""
+    )
+
+    assert document_cookie == "doSomethingOnlyOnce=true"
+    await browser_context.close()
+
+    browser_context2 = await browser_type.launchPersistentContext(
+        userDataDir, **launch_arguments
+    )
+    page2 = await browser_context2.newPage()
+    await page2.goto(server.EMPTY_PAGE)
+    assert await page2.evaluate("() => document.cookie") == "doSomethingOnlyOnce=true"
+    await browser_context2.close()
+
+    userDataDir2 = tmp_path_factory.mktemp("2")
+    browser_context3 = await browser_type.launchPersistentContext(
+        userDataDir2, **launch_arguments
+    )
+    page3 = await browser_context3.newPage()
+    await page3.goto(server.EMPTY_PAGE)
+    assert await page3.evaluate("() => document.cookie") != "doSomethingOnlyOnce=true"
+    await browser_context3.close()
+
+
+async def test_should_have_default_url_when_launching_browser(launch_persistent):
+    (page, context) = await launch_persistent()
+    urls = list(map(lambda p: p.url, context.pages))
+    assert urls == ["about:blank"]
+
+
+@pytest.mark.skip_browser("firefox")
+async def test_should_throw_if_page_argument_is_passed(
+    browser_type, server, tmpdir, launch_arguments
+):
+    options = {**launch_arguments, "args": [server.EMPTY_PAGE]}
+    with pytest.raises(Error) as exc:
+        await browser_type.launchPersistentContext(tmpdir, **options)
+    assert "can not specify page" in exc.value.message
+
+
+async def test_should_fire_close_event_for_a_persistent_context(launch_persistent):
+    (page, context) = await launch_persistent()
+    fired_event = asyncio.Future()
+    context.on("close", lambda: fired_event.set_result(True))
+    await context.close()
+    await fired_event