mirror of
https://github.com/simonw/datasette.git
synced 2026-06-17 22:37:48 +02:00
parent
bba7e0b027
commit
6cd65cf4fb
5 changed files with 148 additions and 2 deletions
48
.github/workflows/playwright.yml
vendored
Normal file
48
.github/workflows/playwright.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
name: Playwright
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [chromium, firefox, webkit]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python 3.14
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.14"
|
||||
allow-prereleases: true
|
||||
cache: pip
|
||||
cache-dependency-path: pyproject.toml
|
||||
- name: Cache uv
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.cache/uv
|
||||
key: ${{ runner.os }}-py3.14-uv-${{ hashFiles('pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-py3.14-uv-
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ matrix.browser }}-${{ hashFiles('pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-playwright-${{ matrix.browser }}-
|
||||
- name: Install uv
|
||||
run: python -m pip install uv
|
||||
- name: Install dependencies
|
||||
run: uv sync --group dev --group playwright
|
||||
- name: Install ${{ matrix.browser }}
|
||||
run: uv run --group dev --group playwright playwright install --with-deps ${{ matrix.browser }}
|
||||
- name: Run Playwright tests
|
||||
run: uv run --group dev --group playwright pytest tests/test_playwright.py --playwright --browser ${{ matrix.browser }}
|
||||
|
|
@ -81,6 +81,9 @@ dev = [
|
|||
"ruamel.yaml",
|
||||
"psutil>=5.9",
|
||||
]
|
||||
playwright = [
|
||||
"pytest-playwright>=0.8.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
rich = ["rich"]
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ filterwarnings=
|
|||
ignore:Using or importing the ABCs::bs4.element
|
||||
markers =
|
||||
serial: tests to avoid using with pytest-xdist
|
||||
asyncio_mode = strict
|
||||
playwright: browser automation tests, skipped unless --playwright is passed
|
||||
asyncio_mode = strict
|
||||
|
|
|
|||
|
|
@ -96,6 +96,15 @@ def pytest_report_header(config):
|
|||
return "SQLite: {}".format(version)
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--playwright",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="run Playwright browser automation tests",
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
import sys
|
||||
|
||||
|
|
@ -108,7 +117,13 @@ def pytest_unconfigure(config):
|
|||
del sys._called_from_test
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(items):
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if not config.getoption("--playwright"):
|
||||
skip_playwright = pytest.mark.skip(reason="need --playwright option to run")
|
||||
for item in items:
|
||||
if "playwright" in item.keywords:
|
||||
item.add_marker(skip_playwright)
|
||||
|
||||
# Ensure test_cli.py and test_black.py and test_inspect.py run first before any asyncio code kicks in
|
||||
move_to_front(items, "test_cli")
|
||||
move_to_front(items, "test_black")
|
||||
|
|
|
|||
79
tests/test_playwright.py
Normal file
79
tests/test_playwright.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from datasette.fixtures import write_fixture_database
|
||||
|
||||
|
||||
def find_free_port():
|
||||
with socket.socket() as sock:
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
return sock.getsockname()[1]
|
||||
|
||||
|
||||
def wait_for_server(process, url, timeout=10):
|
||||
deadline = time.monotonic() + timeout
|
||||
last_error = None
|
||||
while time.monotonic() < deadline:
|
||||
if process.poll() is not None:
|
||||
stdout, stderr = process.communicate()
|
||||
raise AssertionError(
|
||||
"Datasette server exited early\n"
|
||||
f"stdout:\n{stdout}\n"
|
||||
f"stderr:\n{stderr}"
|
||||
)
|
||||
try:
|
||||
response = httpx.get(url, timeout=1.0)
|
||||
if response.status_code < 500:
|
||||
return
|
||||
last_error = f"HTTP {response.status_code}: {response.text[:200]}"
|
||||
except httpx.HTTPError as ex:
|
||||
last_error = repr(ex)
|
||||
time.sleep(0.1)
|
||||
raise AssertionError(f"Timed out waiting for {url}: {last_error}")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def datasette_server(tmp_path):
|
||||
db_path = tmp_path / "fixtures.db"
|
||||
write_fixture_database(str(db_path))
|
||||
port = find_free_port()
|
||||
process = subprocess.Popen(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"datasette",
|
||||
str(db_path),
|
||||
"--host",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
str(port),
|
||||
"--setting",
|
||||
"num_sql_threads",
|
||||
"1",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
url = f"http://127.0.0.1:{port}/"
|
||||
try:
|
||||
wait_for_server(process, url)
|
||||
yield url
|
||||
finally:
|
||||
process.terminate()
|
||||
try:
|
||||
process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
process.wait()
|
||||
|
||||
|
||||
@pytest.mark.playwright
|
||||
def test_datasette_homepage_contains_datasette(page, datasette_server):
|
||||
page.goto(datasette_server)
|
||||
assert "Datasette" in page.locator("body").inner_text()
|
||||
Loading…
Add table
Add a link
Reference in a new issue