Skip to content
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
27 changes: 27 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,30 @@ jobs:

- name: Run Pytest Suite
run: uv run pytest

e2e:
name: Browser E2E on Ubuntu
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.12

- name: Create virtual environment
run: uv venv --python 3.12

- name: Install dependencies
run: uv pip install -e ".[dev,images]"

- name: Install Playwright browser
run: uv run python -m playwright install --with-deps chromium

- name: Run browser E2E suite
run: uv run pytest -m e2e tests/e2e -q
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,13 @@ Common tasks:

```bash
uv run python scripts/dev.py install # bootstrap .venv
uv run python scripts/dev.py test # run the full test suite
uv run python scripts/dev.py test # run the default test suite (excludes browser e2e)
uv run python scripts/dev.py test -- tests/test_check.py -q
uv run python scripts/dev.py run # defaults to: glissade demo
uv run python scripts/dev.py run -- start
uv run python scripts/dev.py build # build dist/ packages
uv run python scripts/dev.py e2e-install # install Playwright's browser binaries
uv run python scripts/dev.py e2e # run browser end-to-end tests
```

If you prefer the raw `uv` commands:
Expand All @@ -384,12 +386,18 @@ uv python install 3.12
uv venv .venv --python 3.12
uv pip install --python .venv/bin/python -e ".[dev,images]" # Windows: .venv\Scripts\python.exe
uv run --python .venv/bin/python pytest -q # Windows: .venv\Scripts\python.exe
uv run --python .venv/bin/python python -m playwright install chromium
uv run --python .venv/bin/python pytest -m e2e tests/e2e -q
```

`src/glissade/` is the package; `templates/` holds the deck and remote HTML;
`data/` holds the themes, JSON schema, `init` scaffold and demo decks — all
shipped inside the wheel so the tool works from any directory.

Browser behavior is covered by Playwright end-to-end tests in `tests/e2e/`.
They start a real `glissade` server, drive both the projector and remote UIs,
and verify live navigation, live notes, and watch-mode reloads.

```bash
uv build # sdist + universal wheel into dist/
```
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "glissade"
version = "0.13.0"
version = "0.14.0"
description = "Presentation decks written as JSON, driven from your phone, built into a single self-contained HTML file"
readme = "README.md"
requires-python = ">=3.10"
Expand Down Expand Up @@ -46,12 +46,17 @@ images = ["pillow>=10.0"]
dev = [
"pytest>=8.0",
"httpx>=0.27",
"playwright>=1.54.0",
"ruff>=0.6.0",
]

[tool.pytest.ini_options]
addopts = ["-m", "not e2e"]
testpaths = ["tests"]
pythonpath = ["src"]
markers = [
"e2e: browser end-to-end tests that require Playwright",
]

[project.urls]
Homepage = "https://github.com/techmuch/glissade"
Expand Down
48 changes: 48 additions & 0 deletions scripts/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ def cmd_build(args: argparse.Namespace) -> None:
run([uv(), "build", *passthrough(args.args)])


def cmd_e2e_install(args: argparse.Namespace) -> None:
python = require_venv(ROOT / args.venv)
run([str(python), "-m", "playwright", "install", args.browser])


def cmd_e2e(args: argparse.Namespace) -> None:
python = require_venv(ROOT / args.venv)
extra = passthrough(args.args)
cmd = [str(python), "-m", "pytest", "-m", "e2e"]
if not extra:
cmd.extend(["tests/e2e", "-q"])
else:
cmd.extend(extra)
run(cmd)


def parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
description="Cross-platform developer tasks for Glissade."
Expand Down Expand Up @@ -145,6 +161,38 @@ def parser() -> argparse.ArgumentParser:
)
build.set_defaults(func=cmd_build)

e2e_install = sub.add_parser(
"e2e-install",
help="Install Playwright browser binaries into the local .venv.",
)
e2e_install.add_argument(
"--venv",
default=".venv",
help="Virtualenv directory (default: .venv)",
)
e2e_install.add_argument(
"--browser",
default="chromium",
help="Browser to install for Playwright (default: chromium)",
)
e2e_install.set_defaults(func=cmd_e2e_install)

e2e = sub.add_parser(
"e2e",
help="Run browser end-to-end tests inside the local .venv.",
)
e2e.add_argument(
"--venv",
default=".venv",
help="Virtualenv directory (default: .venv)",
)
e2e.add_argument(
"args",
nargs=argparse.REMAINDER,
help="Arguments passed through to pytest for e2e tests",
)
e2e.set_defaults(func=cmd_e2e)

return p


Expand Down
2 changes: 1 addition & 1 deletion src/glissade/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
glissade check validate decks before you rely on them
"""

__version__ = "0.13.0"
__version__ = "0.14.0"

from .project import ( # noqa: F401
DATA_DIR,
Expand Down
2 changes: 1 addition & 1 deletion src/glissade/data/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://glissade.dev/schema/v1.json",
"x-glissade-schema": 1,
"x-glissade-version": "0.13.0",
"x-glissade-version": "0.14.0",
"title": "Glissade deck",
"description": "A presentation deck. Either an object with metadata and a slides array, or a bare array of slides.",
"oneOf": [
Expand Down
21 changes: 0 additions & 21 deletions src/glissade/templates/control.html
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,6 @@ <h2 id="nexttitle"></h2>
<span id="lnstatus">No live notes yet.</span>
<button id="showlive">Show on projector</button>
</div>
<<<<<<< HEAD

=======
>>>>>>> main
</div>
</main>

Expand Down Expand Up @@ -436,11 +432,7 @@ <h2 id="nexttitle"></h2>
let SLIDES = [], THEMES = [], DECKS = [];
let cur = 1, blanked = false, scale = 1, minScale = 0.7, maxScale = 1.6;
let painted = null, serverHasScale = true, themeId = null, deckId = null, rev = null;
<<<<<<< HEAD
let liveNoteText = '', liveNotesVisible = false, liveNoteSaveTimer = null, liveNoteDirty = false, allLiveNotes = [];
=======
let liveNoteText = '', liveNotesVisible = false, liveNoteSaveTimer = null, liveNoteDirty = false;
>>>>>>> main

const $ = id => document.getElementById(id);

Expand Down Expand Up @@ -484,10 +476,7 @@ <h2 id="nexttitle"></h2>
$('lnstatus').className = liveNoteDirty ? '' : (liveNoteText ? 'ok' : '');
$('showlive').classList.toggle('on', !!liveNotesVisible);
$('showlive').textContent = liveNotesVisible ? 'Hide on projector' : 'Show on projector';
<<<<<<< HEAD
renderLiveNotesReview();
=======
>>>>>>> main

if(nx){
$('nextbox').style.display = '';
Expand Down Expand Up @@ -694,7 +683,6 @@ <h2 id="nexttitle"></h2>
for(const k in map) if(map[k]) r.setProperty(k, map[k]);
}

<<<<<<< HEAD
function renderLiveNotesReview(){
const boxes = [$('lnreview'), $('lnreviewpanel')].filter(Boolean);
for(const box of boxes){
Expand All @@ -719,22 +707,16 @@ <h2 id="nexttitle"></h2>
}
}

=======
>>>>>>> main
function queueLiveNoteSave(){
liveNoteDirty = true;
paint();
clearTimeout(liveNoteSaveTimer);
liveNoteSaveTimer = setTimeout(() => {
post('/api/live-notes', {n: cur, text: $('livenotes').value}).then(r => {
liveNoteDirty = false;
<<<<<<< HEAD
if(r.ok) allLiveNotes = (r.all_live_notes || []);
if(!r.ok) $('lnstatus').textContent = 'Could not save live notes.';
renderLiveNotesReview();
=======
if(!r.ok) $('lnstatus').textContent = 'Could not save live notes.';
>>>>>>> main
paint();
});
}, 350);
Expand Down Expand Up @@ -875,10 +857,7 @@ <h2 id="nexttitle"></h2>
if(st.deck != null) deckId = st.deck;
liveNoteText = st.live_note || '';
liveNotesVisible = !!st.live_notes_visible;
<<<<<<< HEAD
if(st.all_live_notes) allLiveNotes = st.all_live_notes;
=======
>>>>>>> main
/* An older server reports no scale. Flag it as soon as we connect rather
than letting the presenter discover it by pressing a dead button. */
serverHasScale = st.scale != null;
Expand Down
Loading
Loading