ci: run the unit suite on Windows and smoke the Playwright sample there - #582
Open
chinmayajha wants to merge 3 commits into
Open
chinmayajha wants to merge 3 commits into
chinmayajha wants to merge 3 commits into
Conversation
chinmayajha
force-pushed
the
ci/windows-playwright-sample
branch
from
September 22, 2026 20:40
112fc6a to
7e9e2e9
Compare
added 3 commits
September 23, 2026 02:17
First Windows run of the suite: 28 failures, none of them product bugs.
Four harness assumptions, each of which quietly passed on POSIX.
`monkeypatch.setenv("HOME", ...)` does not redirect `expanduser` on
Windows — ntpath reads USERPROFILE, then HOMEDRIVE/HOMEPATH, and ignores
HOME — so the onboarding and doctor tests were asserting against the
runner's real home directory. `set_home` now sets both.
Constructing a prompt_toolkit `Application` resolves an output eagerly,
which on Windows with no console raises NoConsoleScreenBufferError; POSIX
falls back to a vt100 writer, which is why the live TUI tests never
needed a session before. They now run inside one bound to a DummyOutput.
The remaining two asserted on POSIX-shaped strings where the product is
already platform-correct: a `/` separator in the autocompletion source
line, and `python3 -m venv` where Windows is told `python -m venv` and
`Scripts\Activate.ps1`.
The Playwright browser-cache test also gains the Windows cache location;
`_playwright_browser_dirs` has always listed it, but nothing covered it.
Windows coverage stopped at `optics --version` and `optics list` in the installer CI (mozarkai/optics-framework-org): proof the CLI starts, not proof the framework runs. The suite has never been run on Windows, so POSIX-separator assumptions, console encoding and rmtree behaviour were all unverified. Matrixing the job we already have is the cheapest way to cover that: it reports a precise failing test instead of one opaque end-to-end step. `fail-fast: false` so a Windows failure still reports the Linux result.
The unit matrix covers path handling and encoding, but nothing in it launches a browser or writes a real artefact. This job scaffolds the shipped Playwright sample, diagnoses it and runs it end to end, which is the only way to reach `execution_output/` on Windows. Two checks beyond the exit code, because neither would surface as a failure on its own: `optics execute` exits 0 on an empty result set, and `save_screenshot` swallows a rejected path because `cv2.imwrite` returns False rather than raising. So the job reads back the JUnit report and asserts a screenshot reached disk. It skips pull requests: the matrix above carries the regression load, and a ~150 MB Chromium download plus a live example.com fetch is a poor trade on every PR. `workflow_dispatch` covers running it on demand.
chinmayajha
force-pushed
the
ci/windows-playwright-sample
branch
from
September 22, 2026 20:47
7e9e2e9 to
da926ec
Compare
|
This was referenced Sep 22, 2026
Non-UTF-8 stdout kills every command, and Windows-reserved timestamps silently drop screenshots
#587
Open
malto101
requested changes
Sep 23, 2026
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [ubuntu-latest, windows-latest] |
Comment on lines
+71
to
+98
| run: | | ||
| $out = Join-Path 'demo' 'execution_output' | ||
| $report = Join-Path $out 'junit_output.xml' | ||
| if (-not (Test-Path -LiteralPath $report)) { | ||
| throw "No JUnit report at $report - the run wrote nothing under execution_output." | ||
| } | ||
| [xml]$junit = Get-Content -LiteralPath $report | ||
| $tests = 0; $failures = 0; $errors = 0 | ||
| foreach ($suite in @($junit.testsuites.testsuite)) { | ||
| $tests += [int]$suite.tests | ||
| $failures += [int]$suite.failures | ||
| $errors += [int]$suite.errors | ||
| } | ||
| # `optics execute` exits 0 on an empty result set, so without this an | ||
| # empty report would read as a green run that executed nothing. | ||
| if ($tests -eq 0) { | ||
| throw "JUnit report lists no test cases." | ||
| } | ||
| if ($failures -ne 0 -or $errors -ne 0) { | ||
| throw "JUnit report lists $failures failure(s) and $errors error(s)." | ||
| } | ||
| # save_screenshot swallows a rejected path (cv2.imwrite returns False | ||
| # rather than raising), so a dropped capture would still exit 0. | ||
| $shots = @(Get-ChildItem -LiteralPath $out -Filter '*.jpg' -File) | ||
| if ($shots.Count -eq 0) { | ||
| throw "No screenshot under execution_output - capture dropped its output silently." | ||
| } | ||
| Write-Host "$tests test case(s) passed, $($shots.Count) screenshot(s) saved." |
Member
There was a problem hiding this comment.
remove the comments, seems quite obvious
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes #580
What Windows coverage was before
The installer CI in
mozarkai/optics-framework-orgruns on a realwindows-latestrunner and verifies install →optics --version→optics list→ idempotent re-run → uninstall. That proves the CLI starts on Windows. It does not prove the framework works there. The macOS and Linux journeys were walked by hand end to end; Windows had no equivalent, and the suite had never been run on it at all.What this adds
1. The existing
pytestjob now runs onubuntu-latestandwindows-latest(fail-fast: false, so a Windows failure still reports the Linux result). This is the bulk of the value: ~1393 tests covering path handling, encoding and cleanup across the whole codebase, each reporting a precise failing assertion, rather than one opaque end-to-end step.2. A
playwright-samplejob for the one thing unit tests cannot reach — a real browser launch writing real artefacts:Each command is its own step, so any non-zero exit fails the job.
executedoes propagate failure —execute_main(optics_framework/helper/execute.py) callssys.exit(1)when any test case is notPASS, and anything raised out of a subcommand is caught bycli.main's handler, which also exits non-zero.Two things the exit code alone would not catch, so they are asserted explicitly:
_has_failed_results({})isFalse, sooptics executeexits 0 on an empty result set. The job readsexecution_output/junit_output.xmland fails if it lists zero test cases, or any failure/error.utils.save_screenshotwrapscv2.imwritein atry/except, andcv2.imwritereturnsFalserather than raising on a rejected path. A write Windows refuses would be dropped silently and still exit 0. The job fails if no screenshot landed underexecution_output/.On failure,
execution_output/is uploaded as an artifact — the only way to diagnose a Windows-only failure from a non-Windows machine.Both jobs live in
tests.ymlSame triggers, same purpose, one checks list, one set of pinned action SHAs to maintain. #585 put its bare-image smoke in the same file for the same reason.
Why the e2e job skips pull requests
if: github.event_name != 'pull_request'— it runs on push tomainand onworkflow_dispatch.The unit matrix now carries the regression-catching load, and it catches more, faster, with better failure messages. Against that, the e2e job downloads ~150 MB of Chromium and fetches a live
example.comon every run: it would be the only network-dependent check in the repo, and making it a per-PR check couples PR throughput to a third party's uptime. It is also the slowest job here by a wide margin.The trade is real and worth naming: a regression that only the e2e catches now surfaces on
mainrather than on the PR that caused it. Three things make that acceptable — the unit matrix covers the same defect classes at a finer grain,workflow_dispatchlets anyone run the e2e on demand before merging something risky, andmainis still upstream of every release. If it turns out to catch things the matrix misses, moving it back to per-PR is a one-line change.What the Windows matrix found: 28 failures, all of them test bugs
None were product bugs. Four harness assumptions, each of which passed on POSIX for the wrong reason (fixed in the first commit):
monkeypatch.setenv("HOME", ...)does not redirectexpanduseron Windows —ntpathreadsUSERPROFILE, thenHOMEDRIVE/HOMEPATH, and ignoresHOME. The tests were asserting against the runner's real home.test_onboarding.py,test_doctor.py)Applicationresolves an output eagerly; with no Windows console that raisesNoConsoleScreenBufferError. POSIX falls back to a vt100 writer, so no session was ever needed.test_live_keywords.py,test_live_driver_agnostic.py)/hardcoded in an assertion where the product correctly writesos.path.join.test_autocompletion.py)python3 -m venvasserted where the product already, correctly, tells Windowspython -m venvandScripts\Activate.ps1.test_environment.py)In the two cases where a platform difference was involved, the product was already the correct side of it. The Playwright browser-cache test also gains the Windows cache location —
_playwright_browser_dirshas always listedAppData\Local\ms-playwright, but nothing covered it.The e2e job is expected to stay red
It fails at
optics doctorwith:U+1FA7Ais the stethoscope indoctor.py:498. On Windowssys.stdoutis UTF-8 only when attached to a console; redirected (CI,optics doctor > log.txt, any build system) Python falls back to the ANSI code page and Rich writes the emoji straight through. Rich'ssafe_boxdegrades box-drawing to ASCII — visible in theoptics initstep, which printed+---+— but it does not transcode emoji. It is unconditional:_STATUS_GLYPH(doctor.py:41) puts✅/⚠️/❌on every row.This needs no Windows runner to reproduce:
That fix, and the
save_screenshottimestamp sanitisation (utils.get_timestamp()returns...T16:51:50.397462+05:30, andutils.save_screenshotinterpolates a caller-supplied timestamp into the filename unsanitised — colons are reserved on Windows), belong in the Windows-correctness PR, not in a CI change. This job goes green when that lands. The assertions are deliberately left intact rather than relaxed to make it pass.