Skip to content

Commit 2e0253e

Browse files
Add end-to-end tests for JaSketch application
- Created test suite for JaSketch with comprehensive E2E tests covering UI rendering, tool selection, shape drawing, text editing, undo/redo, deletion, export, and zoom/pan functionalities. - Implemented shared fixtures in `conftest.py` to manage JaSketch server lifecycle and provide a base URL for tests. - Added various test cases to validate the behavior of drawing tools, element manipulation, context menu operations, and persistence across page reloads. - Ensured tests are structured for clarity and maintainability, with helper functions for common actions.
1 parent 35dff60 commit 2e0253e

5 files changed

Lines changed: 1156 additions & 0 deletions

File tree

.github/workflows/e2e-tests.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: JaSketch E2E Tests
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
paths:
7+
- "jasketch/**"
8+
pull_request:
9+
branches: [main]
10+
paths:
11+
- "jasketch/**"
12+
workflow_dispatch:
13+
14+
defaults:
15+
run:
16+
working-directory: jasketch
17+
18+
jobs:
19+
e2e-tests:
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 15
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
27+
- name: Set up Python 3.12
28+
uses: actions/setup-python@v5
29+
with:
30+
python-version: "3.12"
31+
32+
- name: Set up Node.js 20
33+
uses: actions/setup-node@v4
34+
with:
35+
node-version: "20"
36+
37+
- name: Install Jac and dependencies
38+
run: |
39+
pip install jaclang jac-client jac-scale pytest pytest-playwright pytest-timeout
40+
41+
- name: Install Playwright browsers
42+
run: python -m playwright install --with-deps chromium
43+
44+
- name: Install app dependencies
45+
run: jac install
46+
47+
- name: Run E2E tests
48+
run: pytest tests/e2e/ -v --timeout=300
49+
50+
- name: Upload test results
51+
if: failure()
52+
uses: actions/upload-artifact@v4
53+
with:
54+
name: e2e-test-results
55+
path: jasketch/tests/e2e/
56+
retention-days: 7

tests/__init__.py

Whitespace-only changes.

tests/e2e/__init__.py

Whitespace-only changes.

tests/e2e/conftest.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Shared fixtures for JaSketch E2E tests."""
2+
3+
import os
4+
import socket
5+
import subprocess
6+
import time
7+
8+
import pytest
9+
from playwright.sync_api import Page
10+
11+
JASKETCH_DIR = os.path.abspath(
12+
os.path.join(os.path.dirname(__file__), "..", "..")
13+
)
14+
APP_READY_TIMEOUT = 60_000 # 1 min for app to render
15+
16+
17+
def _get_free_port() -> int:
18+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
19+
s.bind(("127.0.0.1", 0))
20+
return s.getsockname()[1]
21+
22+
23+
def _wait_for_port(port: int, timeout: float = 60.0) -> bool:
24+
deadline = time.time() + timeout
25+
while time.time() < deadline:
26+
try:
27+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
28+
s.connect(("127.0.0.1", port))
29+
return True
30+
except OSError:
31+
time.sleep(0.5)
32+
return False
33+
34+
35+
@pytest.fixture(scope="session")
36+
def base_url():
37+
"""Start the JaSketch server, yield its base URL."""
38+
port = _get_free_port()
39+
proc = subprocess.Popen(
40+
["jac", "start", "--port", str(port)],
41+
cwd=JASKETCH_DIR,
42+
stdout=subprocess.PIPE,
43+
stderr=subprocess.PIPE,
44+
)
45+
assert _wait_for_port(port), f"Server failed to start on port {port}"
46+
yield f"http://127.0.0.1:{port}"
47+
proc.terminate()
48+
try:
49+
proc.wait(timeout=10)
50+
except subprocess.TimeoutExpired:
51+
proc.kill()
52+
53+
54+
@pytest.fixture(scope="module")
55+
def app(browser, base_url) -> Page:
56+
"""Navigate to JaSketch, wait for canvas to be ready."""
57+
context = browser.new_context(permissions=["clipboard-read", "clipboard-write"])
58+
page = context.new_page()
59+
page.goto(base_url, wait_until="load")
60+
# Wait for the canvas element to be visible (app is rendered)
61+
page.locator("canvas").first.wait_for(state="visible", timeout=APP_READY_TIMEOUT)
62+
yield page
63+
context.close()

0 commit comments

Comments
 (0)