Closed
Description
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 commentedon Aug 31, 2021
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
mxschmitt commentedon Aug 31, 2021
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
fromsession
tofunction
.We advice people to use
context
on thefunction
scope what we by default use to enable context isolation per test. Are you intentionally having context on thesession
scope?nathonfowlie commentedon Aug 31, 2021
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 commentedon Sep 6, 2021
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 commentedon Sep 24, 2021
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 commentedon Oct 11, 2021
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 🙏🏻