Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1c660be
docs(developer/tasks): add task template with WHY-WHAT-HOW structure
mettta Jul 4, 2026
9fc0745
docs(developer/tasks): add task.md
mettta Jul 4, 2026
4387030
chore(.gitignore): added /tests/screencast/output/ so generated .webm…
mettta Jul 4, 2026
6c6f2aa
chore(tests/screencast): drop Node.js pipeline and stale fixture cruft
mettta Jul 4, 2026
ad1484a
build(deps): add Playwright to end2end/check dependencies
mettta Jul 4, 2026
a615b5d
refactor(tests/screencast): start the demo server via SDocTestServer
mettta Jul 4, 2026
b236d4f
feat(tests/screencast): port demo scenarios to Playwright Python pyte…
mettta Jul 4, 2026
9c37ed6
feat(tests/screencast): port demo scenarios to Playwright Python pyte…
mettta Jul 4, 2026
84436eb
feat(tests/screencast): add --strictdoc-record-video mode to scenarios
mettta Jul 4, 2026
cd66b2a
feat(tasks): add invoke test-screencast task
mettta Jul 5, 2026
49b14dd
docs(tests/screencast): rewrite README for the Python/Playwright pipe…
mettta Jul 5, 2026
b42342a
docs(developer/tasks/20260704_screencast): tighten task.md to the tem…
mettta Jul 5, 2026
299e1bb
feat(tests/screencast): auto-stop or safely report the process on the…
mettta Jul 5, 2026
7a8c286
feat(tasks): add invoke screencast-server; fix README's launch instru…
mettta Jul 5, 2026
685ecd0
fix(tests/screencast): move dev/record ports off StrictDoc's own defa…
mettta Jul 5, 2026
d009d81
ci(screencast): add dedicated GitHub Actions workflow for invoke test…
mettta Jul 5, 2026
577806c
feat(tests/screencast): add focusable manual server
mettta Jul 6, 2026
2c249c0
feat(tests/screencast): add hello_world scenario
mettta Jul 6, 2026
d0b82eb
feat(tests/screencast): add cursor/highlight pointer and pacing helpers
mettta Jul 7, 2026
cd6c707
refactor(tests/screencast): extract pointer styles into pointer.css
mettta Jul 7, 2026
183853e
feat(tests/screencast): syntax-highlight the editor scene with Strict…
mettta Jul 7, 2026
254f4f9
docs(tests/screencast): document Pointer, pacing, scene styling, and …
mettta Jul 7, 2026
155aa0b
docs(tests/screencast): add Quick reference to README
mettta Jul 7, 2026
3cea13a
docs(tests/screencast): update README
mettta Jul 7, 2026
b27a165
feat(tests/screencast): default manual server to a disposable copy, a…
mettta Jul 7, 2026
f32fa04
fix(tests/screencast): use modern typing syntax to satisfy ruff (UP00…
mettta Jul 10, 2026
7cd72a0
fix(tests/screencast): Add web-optimized video export to the screenca…
mettta Jul 14, 2026
2348daf
feat(tests/screencast): edit an existing Requirement in hello_world i…
mettta Jul 11, 2026
906918d
fix(tests/screencast): hide fake cursor until first use; short relati…
mettta Jul 11, 2026
4fe68f6
fix(tests/screencast): avoid a blank white flash at the start of the …
mettta Jul 11, 2026
4ae5d3f
feat(tests/screencast): follow parent/child relation links in hello_w…
mettta Jul 11, 2026
2e6c709
fix(export/html): stop .document_title overflowing its flex parent on…
mettta Jul 11, 2026
d1e56f4
feat(tests/screencast): shrink recording resolution to 1024x768
mettta Jul 11, 2026
0dfd6b9
fix(export/html): disable .content's margin-bottom: 300px pending a r…
mettta Jul 11, 2026
bba3deb
feat(tests/screencast): scroll content to bottom before editing Ratio…
mettta Jul 13, 2026
1bd61ff
fix(tests/screencast): wrap long editor lines instead of letting them…
mettta Jul 14, 2026
b2e304f
fix(tests/screencast): retheme terminal scene colors/font, hide title…
mettta Jul 14, 2026
0abdfcb
chore(.gitignore): added /tests/screencast/temp/
mettta Jul 14, 2026
757b783
refactor(tests/screencast): update pauses
mettta Jul 14, 2026
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
42 changes: 42 additions & 0 deletions .github/workflows/screencast-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: "StrictDoc - Screencast tests"

on:
pull_request:
branches: [ "**" ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
tests_screencast:
name: Screencast
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v7
with:
fetch-depth: 5

- name: Set up Python 3.10
uses: actions/setup-python@v6
with:
python-version: "3.10"

- name: Upgrade pip
run: |
python -m pip install --upgrade pip

- name: Install minimal Python packages
run: |
pip install -r requirements.bootstrap.txt

- name: Prepare the check tox environment and Playwright's browser binary
run: |
tox -e py310-check --notest
./build/tox/py310-check/bin/python developer/pip_install_strictdoc_deps.py
./build/tox/py310-check/bin/python -m playwright install --with-deps chromium

- name: Run screencast tests
run: |
invoke test-screencast
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
__pycache__

/tests/unit_server/**/output/
/tests/screencast/output/
/tests/screencast/temp/

### LIT/FileCheck integration tests' artifacts. ###
/tests/integration/**/Output/**
Expand Down
68 changes: 68 additions & 0 deletions developer/tasks/20260704_screencast/task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Screencast generator: move demo-video pipeline into StrictDoc

## WHAT

- Demo screencasts (`.webm` videos) can be generated entirely from within
the StrictDoc repository, without depending on another repository or a
separate checkout.
- The generator has no Node.js/npm dependency.
- Each demo scenario is an automated check that:
- passes or fails according to whether it still reflects real, current
UI behavior, and
- can optionally run in a recording mode that (re)generates the
scenario's video.
- A failing scenario means its video can no longer be trusted and must be
reviewed before being re-recorded.
- Screencast scenarios run independently from the main end-to-end test
suite, without being folded into it.
- Each scenario runs against a real, live instance of the application.
- The project used for recording is self-contained within the repository.
- Delivering generated videos to the product website is OUT OF SCOPE for
this task; this task only produces the videos within the StrictDoc
repository.

## WHY

StrictDoc needs a maintainable way to generate product demo videos that stay
synchronized with the actual UI.

Video scenarios should live next to the features they demonstrate and run
through the same automation pipeline as UI tests.

A single scenario definition can then serve both as a source for generating
videos and as a regression check for the demonstrated workflow.

## HOW

- **Language**: the generator is written in Python using the Playwright
Python API (`playwright.sync_api`), including `record_video_dir` for
video capture. No Node.js/npm dependency exists in the project.
- **Server lifecycle**: scenarios start/stop a real StrictDoc server through
`tests/end2end/server.py::SDocTestServer`, in-process to this repository.
- **Scenario structure**: each demo scene is its own pytest test case,
following the `tests/end2end/screens/*/test_case.py` convention.
Playwright-native Page Object helpers live under
`tests/screencast/helpers/` (e.g. `Screen`, `ViewTypeSelector`), for reuse
across screencast scenarios and as a foundation for a wider Playwright
suite later. Standalone HTML/IDE-style scenes (fake typing effect) use a
local HTML playground (`demo.html`).
- **Dual-purpose run modes**:
- default run: scenario executes as a normal pass/fail check, fast, with
no video output;
- recording run: the `--strictdoc-record-video` pytest option makes the
same scenario also capture and save a `.webm` to
`tests/screencast/output/`.
- **Invoke task**: `invoke test-screencast` (alias `tsc`) in `tasks.py`,
following the conventions of `test_end2end`/`test_unit_server` (focus
filter, junit output under `TEST_REPORTS_DIR`). Kept separate from
`invoke test-end2end`: screencast runs are slower and produce binary
artifacts.
- **Dependencies**: `playwright` (Python package) is a `check`-environment
dependency (`requirements.check.txt`); the Chromium browser binary is
installed separately via `playwright install chromium`.
- **Fixtures**: `tests/screencast/fixtures/strictdoc-demo-project/` is the
demo content used by scenarios, shaped like other `tests/end2end`
fixtures.
- **Out of scope**: publishing/copying generated `.webm` files to
`strictdoc-project.github.io`; migrating the rest of the e2e suite off
SeleniumBase (a separate, future initiative).
23 changes: 23 additions & 0 deletions developer/tasks/task.template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# <Task title>

<!--
Describe the task using the WHY–WHAT–HOW structure. Separate the problem, requirements, and solution details.
-->

## WHAT

<!--
Define the expected system behavior, requirements, constraints, and success criteria. Describe what must be achieved, not how it is implemented.
-->

## WHY

<!--
Explain the context, motivation, user needs, and the problem being solved. Do not describe the solution here.
-->

## HOW

<!--
Describe the chosen technical approach, architecture, design decisions, implementation details, and trade-offs.
-->
3 changes: 3 additions & 0 deletions requirements.check.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ openpyxl>=3.0.5
# End2end tests
seleniumbase

# Screencast video generation (tests/screencast)
playwright

# psutil is needed to reap Uvicorn's zombie processes when running end2end
# tests. One day someone finds a better solution.
psutil
Expand Down
2 changes: 1 addition & 1 deletion strictdoc/export/html/_static/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
[data-viewtype="document"] .content {
display: block;
max-width: 900px;
margin-bottom: 300px;
/* margin-bottom: 300px; */
margin-left: auto;
margin-right: auto;
}
Expand Down
4 changes: 4 additions & 0 deletions strictdoc/export/html/_static/element.css
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,10 @@ a.tree_item,
font-size: var(--font-size-sm);
font-weight: 700;
width: 99%; /* To calculate cite overflow */
/* Flex items default to min-width: auto, which floors width at the
content's intrinsic size and makes width: 99% overflow the parent on
narrow viewports (flexbox min-width:auto pitfall). */
min-width: 0;
}

.document_title[data-file_name]::after {
Expand Down
67 changes: 67 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,29 @@ def server(context, input_path=".", config=None, port=None):
)


@task(aliases=["scs"])
def screencast_server(context, focus=None, edit=False):
"""
Manual dev server for tests/screencast scenarios: starts StrictDoc on a
scenario's project (the shared demo fixture by default, or another
scenario's project via --focus), for inspecting it in the browser.

By default this serves a disposable copy, rebuilt fresh every time, so
nothing done through the UI persists. Pass --edit to serve the real,
persistent files instead (the shared fixture itself, or a generated
project reused across restarts) when intentionally editing them.
"""

focus_argument = f"--focus {focus}" if focus is not None else ""
edit_argument = "--edit" if edit else ""

run_invoke_with_tox(
context,
ToxEnvironment.CHECK,
f"python tests/screencast/run_server.py {focus_argument} {edit_argument}",
)


@task(aliases=["d"])
def docs(context):
run_invoke_with_tox(
Expand Down Expand Up @@ -378,6 +401,50 @@ def test_end2end(
)


@task(aliases=["tsc"])
def test_screencast(context, *, focus=None, record_video=False):
"""
Runs the tests/screencast scenarios: fast pass/fail checks by default,
or (re)recording of the corresponding .webm videos with --record-video.
"""

focus_argument = f"-k {focus}" if focus is not None else ""
record_video_argument = "--strictdoc-record-video" if record_video else ""

Path(TEST_REPORTS_DIR).mkdir(parents=True, exist_ok=True)

test_command = f"""
pytest
--capture=no
{focus_argument}
{record_video_argument}
--junit-xml={TEST_REPORTS_DIR}/tests_screencast.pytest.junit.xml
-o junit_suite_name="StrictDoc Screencast Tests"
-o cache_dir=build/pytest_screencast
tests/screencast/scenarios
"""

run_invoke_with_tox(context, ToxEnvironment.CHECK, test_command)


@task(aliases=["scov"])
def screencast_optimize_video(context, focus=None):
"""
Converts recorded tests/screencast/output/*.webm into muted,
web-optimized .webm (VP9) and .mp4 (H.264) pairs under
tests/screencast/output/web/, ready to hand off to the product website.

Run `invoke test-screencast --record-video` first to produce the raw
recordings this reads from.
"""

focus_argument = f"--focus {focus}" if focus is not None else ""

run_invoke(
context, f"python tests/screencast/optimize_video.py {focus_argument}"
)


@task(aliases=["tu"])
def test_unit(context, coverage=False, focus=None, path=None, output=False):
"""
Expand Down
Loading
Loading