Skip to content

Persistent context example throws a ScopeMismatch exception #73

Closed
@nathonfowlie

Description

@nathonfowlie

Using pytest-playwright 0.1.1 with playwright 1.11.0, I used to be able to create a persistent context like shown below -

import pytest

from playwright.sync_api import Playwright

from typing import Dict

@pytest.fixture(scope="session")
def context(
    playwright: Playwright,
    browser_name: str,
    browser_type_launch_args: Dict,
    browser_context_args: Dict
):
    context = getattr(playwright, browser_name).launch_persistent_context("./foobar", **{
        **browser_type_launch_args,
        **browser_context_args
    })

    context.set_default_navigation_timeout(90000)
    context.set_default_timeout(90000)

    yield context
    context.close()

def test_example(page):
    page.goto("https://example.com")
    assert page.inner_text('h1') == 'Example Domain'
    page.click("text=More information")

I recently upgraded to pytest-playwright 0.2.0 and playwright 1.14.1, and took the persistent context snippet published on https://playwright.dev/python/docs/test-runners#persistent-context to understand how it's done on the latest version. Unfortunately the snippet seems to throw a ScopeMismatch error, because browse_context_args has function scope -

# conftest.py
import pytest
from playwright.sync_api import BrowserType
from typing import Dict


@pytest.fixture(scope="session")
def context(
    browser_type: BrowserType,
    browser_type_launch_args: Dict,
    browser_context_args: Dict
):
    context = browser_type.launch_persistent_context("./foobar", **{
        **browser_type_launch_args,
        **browser_context_args,
        "locale": "de-DE",
    })
    yield context
    context.close()


def test_example(page):
    page.goto("https://example.com")
    assert page.inner_text('h1') == 'Example Domain'
    page.click("text=More information")

Sample Output:

$ pytest .
========================================================================================== test session starts ==========================================================================================
platform win32 -- Python 3.9.6, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: C:\Users\fowliena\Documents\repositories\persistentcontext
plugins: asyncio-0.15.1, base-url-1.4.2, forked-1.3.0, json-report-1.4.0, metadata-1.11.0, playwright-0.2.0, xdist-2.2.1
collected 1 item

test_stuff.py E                                                                                                                                                                                    [100%]

================================================================================================ ERRORS =================================================================================================
________________________________________________________________________________ ERROR at setup of test_example[chromium] ________________________________________________________________________________
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x0000011E28530DC0>
Traceback (most recent call last):
  File "c:\users\fowliena\appdata\local\programs\python\python39\lib\asyncio\base_subprocess.py", line 126, in __del__
    self.close()
  File "c:\users\fowliena\appdata\local\programs\python\python39\lib\asyncio\base_subprocess.py", line 104, in close
    proto.pipe.close()
  File "c:\users\fowliena\appdata\local\programs\python\python39\lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "c:\users\fowliena\appdata\local\programs\python\python39\lib\asyncio\base_events.py", line 746, in call_soon
    self._check_closed()
  File "c:\users\fowliena\appdata\local\programs\python\python39\lib\asyncio\base_events.py", line 510, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
ScopeMismatch: You tried to access the 'function' scoped fixture 'browser_context_args' with a 'session' scoped request object, involved factories
test_stuff.py:7:  def context(browser_type: playwright.sync_api._generated.BrowserType, browser_type_launch_args: Dict, browser_context_args: Dict)
..\..\..\appdata\local\programs\python\python39\lib\site-packages\pytest_playwright\pytest_playwright.py:135:  def browser_context_args(pytestconfig: Any, playwright: playwright.sync_api._generated.Playwright, device: Optional[str]) -> Dict
======================================================================================== short test summary info ========================================================================================
ERROR test_stuff.py::test_example[chromium]

Is this due to the snippet being incorrect, or am I missing something with the way I'm creating a persistent context?

I'm running Python 3.9.6, with the following packages installed -

$ pip list installed
Package                    Version
-------------------------- ---------
apipkg                     1.5
atomicwrites               1.4.0
attrs                      21.2.0
autopep8                   1.5.6
certifi                    2020.12.5
chardet                    4.0.0
click                      8.0.1
colorama                   0.4.4
coverage                   5.5
execnet                    1.8.1
flake8                     3.9.1
flake8-docstrings          1.6.0
flake8-import-order        0.18.1
flake8-junit-report        2.1.0
flake8-polyfill            1.0.2
future                     0.18.2
gitdb                      4.0.7
GitPython                  3.1.18
greenlet                   1.1.0
hvac                       0.10.10
idna                       2.10
iniconfig                  1.1.1
Jinja2                     2.11.3
joblib                     1.0.1
livereload                 2.6.3
lunr                       0.5.8
Markdown                   3.3.4
MarkupSafe                 2.0.1
mccabe                     0.6.1
mkdocs                     1.1.2
mkdocs-autorefs            0.2.1
mkdocs-gen-files           0.3.3
mkdocs-material            7.1.6
mkdocs-material-extensions 1.0.1
mkdocstrings               0.15.1
mypy                       0.910
mypy-extensions            0.4.3
nltk                       3.6.2
packaging                  20.9
pathspec                   0.8.1
pep8-naming                0.11.1
pip                        21.1.3
playwright                 1.14.1
pluggy                     0.13.1
py                         1.10.0
pycodestyle                2.7.0
pydocstyle                 6.0.0
pyee                       8.1.0
pyflakes                   2.3.1
pygit                      0.1
Pygments                   2.9.0
pymdown-extensions         8.2
pyparsing                  2.4.7
pytest                     6.2.3
pytest-asyncio             0.15.1
pytest-base-url            1.4.2
pytest-forked              1.3.0
pytest-json-report         1.4.0
pytest-metadata            1.11.0
pytest-playwright          0.2.0
pytest-xdist               2.2.1
python-slugify             5.0.2
pytkdocs                   0.11.1
PyYAML                     5.4.1
regex                      2021.4.4
requests                   2.25.1
semver                     2.13.0
setuptools                 56.0.0
six                        1.16.0
smmap                      4.0.0
snowballstemmer            2.1.0
tblib                      1.7.0
text-unidecode             1.3
toml                       0.10.2
tornado                    6.1
tqdm                       4.61.0
typed-ast                  1.4.3
typing-extensions          3.10.0.0
urllib3                    1.26.4
websockets                 9.0.1
yamllint                   1.26.1

Activity

nathonfowlie

nathonfowlie commented on Aug 31, 2021

@nathonfowlie
Author

Believe I may have found the issue. In #70, the browser_context_args fixture was changed from session scoped to the default scope (function).

So to use a persistent context we' d have to change the scope of browse_context_args back to session. Is there a nicer way to do this other than copy & pasting the existing fixture and modifying the annotation?

Maybe by using a dynamic scope so the scope of browser_context_args dynamically adjusts based on ...something? I only just discovered this feature so not sure if it could be used here. Looks interesting though...

https://docs.pytest.org/en/stable/fixture.html#dynamic-scope

import pytest
from playwright.sync_api import BrowserType, Playwright
from typing import Any, Dict, Optional

@pytest.fixture(scope="session")
def browser_context_args(
    pytestconfig: Any,
    playwright: Playwright,
    device: Optional[str],
) -> Dict:
    context_args = {}
    if device:
        context_args.update(playwright.devices[device])
    base_url = pytestconfig.getoption("--base-url")
    if base_url:
        context_args["base_url"] = base_url

    video_option = pytestconfig.getoption("--video")
    capture_video = video_option in ["on", "retain-on-failure"]
    if capture_video:
        context_args["record_video_dir"] = artifacts_folder

    return context_args


@pytest.fixture(scope="session")
def context(
    browser_type: BrowserType,
    browser_type_launch_args: Dict,
    browser_context_args: Dict
):
    context = browser_type.launch_persistent_context("./foobar", **{
        **browser_type_launch_args,
        **browser_context_args,
        "locale": "de-DE",
    })
    yield context
    context.close()


def test_example(page):
    page.goto("https://example.com")
    assert page.inner_text('h1') == 'Example Domain'
    page.click("text=More information")
mxschmitt

mxschmitt commented on Aug 31, 2021

@mxschmitt
Member

Hi, thank you for your great verbose bug report. You are correct, since one of the last releases we changed the scopes from browser_type_launch_args from session to function.

We advice people to use context on the function scope what we by default use to enable context isolation per test. Are you intentionally having context on the session scope?

nathonfowlie

nathonfowlie commented on Aug 31, 2021

@nathonfowlie
Author

I was under the belief it needed to be session scope to use a persistent context (so I can login exactly once at the start of the test session, and preserve the authentication state for subsequent tests). If I can achieve the same thing with function scope then I'll give that a bash.

Thanks for the quick response!

marklane2001

marklane2001 commented on Sep 6, 2021

@marklane2001

When I change the browser_type_launch_args from session to function, my tests now load a new browser context for each test rather than 1 for all the tests. This has slowed the run time quite a bit

marklane2001

marklane2001 commented on Sep 24, 2021

@marklane2001

I'm just wondering what I need to do to open a new tab for each test in my Test Class, rather than a new Browser for each one? Since the change from Session to Function scope for the browser_type_launch_arg I have not been able to do this.

jnns

jnns commented on Oct 11, 2021

@jnns

Are you intentionally having context on the session scope?

It's adviced like this in the documentation section about creating Persistent Context. So as it is right now, the documentation doesn't align with the new function scope of browser_context_args.

What is the rationale behind the scope change? I see that @dgozman already anticipated breaking user code during the review. @nathonfowlie suggested to use the new dynamic context. Would that be a good compromise?

EDIT: Thank you for collecting feedback on this 🙏🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @jnns@nathonfowlie@marklane2001@mxschmitt

      Issue actions

        Persistent context example throws a ScopeMismatch exception · Issue #73 · microsoft/playwright-pytest