Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Andrew/browser pool #123

Merged
merged 10 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ jobs:
timeout-minutes: 1
- name: Test Process Control DEBUG
if: runner.debug
run: xvfb-run pytest -W error -n auto -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 1
run: xvfb-run pytest -W error -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 5
- name: Test Process Control
if: ${{ ! runner.debug }}
run: xvfb-run pytest -W error -n auto -v -rfE --capture=fd tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
- name: Test the Rest
run: pytest -vv -rfE --ignore=tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
test-windows:
runs-on: windows-latest
steps:
Expand All @@ -41,15 +41,15 @@ jobs:
timeout-minutes: 1
- name: Test Process Control DEBUG
if: runner.debug
run: pytest -W error -n auto -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 1
run: pytest -W error -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 5
- name: Test Process Control
if: ${{ ! runner.debug }}
run: pytest -W error -n auto -v -rFe --capture=fd tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
- name: Test the Rest
run: pytest -vv -rfE --ignore=tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
test-mac:
runs-on: macos-latest
steps:
Expand All @@ -65,12 +65,12 @@ jobs:
timeout-minutes: 1
- name: Test Process Control DEBUG
if: runner.debug
run: pytest -W error -n auto -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 1
run: pytest -W error -vvv -rA --capture=tee-sys tests/test_process.py
timeout-minutes: 5
- name: Test Process Control
if: ${{ ! runner.debug }}
run: pytest -W error -n auto -v -rFe --capture=fd tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
- name: Test the Rest
run: pytest -vv -rfE --ignore=tests/test_process.py
timeout-minutes: 1
timeout-minutes: 2
6 changes: 1 addition & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,4 @@ enabled = true

[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
# addopts = "-v -n auto --timeout=4"
## Verbosity:
# -vv -rA
## Parallelism:
# -n auto

23 changes: 23 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Testing

## Running:

### Process Control Tests

- Verbose: `pytest -W error -n auto -vvv -rA --capture=tee-sys tests/test_process.py`
- Quiet:`pytest -W error -n auto -v -rFe --capture=fd tests/test_process.py`

### Browser Interaction Tests

- Verbose: `pytest --debug -n auto -W error -vvv -rA --capture=tee-sys --ignore=tests/test_process.py`
- Quiet :`pytest -W error -n auto -v -rFe --capture=fd --ignore=tests/test_process.py`

You can also add "--no-headless" to these if you want to see the browser pop up

## Writing Tests:

Put async and sync tests in different files. Add `_sync.py` to synchronous tests.

If doing process tests, maybe use the same decorators and fixtures in the `test_process.py` file.

If doing browser interaction tests, use `test_placeholder.py` as the minimum template.
49 changes: 48 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import asyncio
import pytest

import pytest_asyncio
import devtools

@pytest.fixture(params=[True, False], ids=["headless", ""])
def headless(request):
Expand All @@ -14,3 +16,48 @@ def debug(request):
@pytest.fixture(params=[True, False], ids=["debug_browser", ""])
def debug_browser(request):
return request.param

@pytest_asyncio.fixture(scope="function", loop_scope="function")
async def browser():
# this needs also to be set by command line TODO
browser = await devtools.Browser()
yield browser
await browser.close()

@pytest_asyncio.fixture(scope="function", loop_scope="function")
async def browser_verbose():
browser = await devtools.Browser(debug=True, debug_browser=True)
yield browser
await browser.close()

# add a 2 second timeout if there is a browser fixture
# we do timeouts manually in test_process because it
# does/tests its own browser.close()
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_setup(item: pytest.Item):
yield

if "browser" in item.funcargs or "browser_verbose" in item.funcargs:
raw_test_fn = item.obj
timeouts = [k for k in item.funcargs if k.startswith("timeout")]
timeout = item.funcargs[timeouts[-1]] if len(timeouts) else pytest.default_timeout
if item.get_closest_marker("asyncio") and timeout:
async def wrapped_test_fn(*args, **kwargs):
try:
return await asyncio.wait_for(
raw_test_fn(*args, **kwargs), timeout=timeout
)
except TimeoutError:
pytest.fail(f"Test {item.name} failed a timeout. This can be extended, but shouldn't be. See conftest.py.")
item.obj = wrapped_test_fn

def pytest_configure():
# change this by command line TODO
pytest.default_timeout = 3

# add this fixture to extend timeout
# there is 6 second max test length for all
# which kills all tests
@pytest.fixture(scope="session")
def timeout_long():
return 6
15 changes: 13 additions & 2 deletions tests/test_placeholder.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
def test_placeholder():
pass
import pytest
import asyncio

# allows to create a browser pool for tests
pytestmark = pytest.mark.asyncio(loop_scope="function")

async def test_placeholder(capsys, browser):
print("")
assert capsys.readouterr().out == "\n", "stdout should be silent!"

assert "result" in await browser.send_command("Target.getTargets")

await asyncio.sleep(0)
18 changes: 12 additions & 6 deletions tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,40 @@
from async_timeout import timeout


@pytest.mark.asyncio
async def test_context(capsys, headless, debug, debug_browser):
@pytest.mark.asyncio(loop_scope="function")
async def test_context(headless, debug, debug_browser):
async with devtools.Browser(
headless=headless,
debug=debug,
debug_browser=debug_browser,
) as browser, timeout(2):
) as browser, timeout(pytest.default_timeout):
response = await browser.send_command(command="Target.getTargets")
assert "result" in response and "targetInfos" in response["result"]
assert (len(response["result"]["targetInfos"]) != 0) != headless
if not headless:
assert isinstance(browser.get_tab(), devtools.tab.Tab)
assert len(browser.get_tab().sessions) == 1
print("") # this makes sure that capturing is working
# stdout should be empty, but not because capsys is broken, because nothing was print
assert capsys.readouterr().out == "\n", "stdout should be silent!"
#assert capsys.readouterr().out == "\n", "stdout should be silent!"
# let asyncio do some cleaning up if it wants, may prevent warnings
await asyncio.sleep(0)

@pytest.mark.asyncio
@pytest.mark.asyncio(loop_scope="function")
async def test_no_context(capsys, headless, debug, debug_browser):
browser = await devtools.Browser(
headless=headless,
debug=debug,
debug_browser=debug_browser,
)
try:
async with timeout(2):
async with timeout(pytest.default_timeout):
response = await browser.send_command(command="Target.getTargets")
assert "result" in response and "targetInfos" in response["result"]
assert (len(response["result"]["targetInfos"]) != 0) != headless
if not headless:
assert isinstance(browser.get_tab(), devtools.tab.Tab)
assert len(browser.get_tab().sessions) == 1
finally:
await browser.close()
print("") # this make sure that capturing is working
Expand Down
Loading