Conversation
evg4b
commented
Sep 15, 2026
evg4b
commented
Sep 15, 2026
The suite drives a real Chromium with dist/ loaded as an unpacked extension, so it needs a production build rather than the rstest environment. Visual baselines are kept per platform and only the Linux ones are committed, since that is where CI compares them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Playwright locators stop at closed shadow roots, and the content script puts its whole UI behind two of them. CDP still walks through, so the `shadow` fixture resolves paths by hand: segments separated by `>>>`, each queried inside the shadow root of the previous match. Matches are turned into remote object handles right away, because node ids are discarded every time the document is re-read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Checks the rendered tree against a document with values the formatter handles specially — a number larger than JavaScript can hold, null, empty containers — plus collapsing and expanding, and that pages without JSON are left untouched. The screenshots cover both themes, so a styling change that survives the text assertions still has to be looked at. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Asserts the raw tab hands back the response byte for byte, that switching tabs keeps the formatted tree, and screenshots the open download dropdown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runs an expression through the input the way a user would — typing and pressing Enter — and checks both the rendered result and the inline message an undefined filter produces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beyond rendering, unchecking a toolbar button on the options page has to reach the content script through synced storage, which is the one path no unit test can exercise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The job reuses the artifact the build job already uploads and runs in the same Playwright image the committed screenshots were generated in — otherwise the runner's fonts leak into the comparison and every visual assertion fails. `make e2e-update` pins the same image for regenerating them locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Selecting the tree and copying it has to round-trip through JSON.parse, including with a node collapsed, where the `...` and `// N items` markers sit in the middle of the text and are kept out of the selection by `user-select: none`. Chromium handles select all and copy as editing commands rather than as plain key presses, and the selection only reaches a closed shadow root once it already sits inside one, so the fixture clicks into the tree first and drives both commands over CDP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Text rendering depends on the fonts installed on the machine, so a baseline is only meaningful next to the renderer that produced it. That was handled by suffixing every image with its platform and committing only the Linux ones, which left macOS and Windows runs silently writing baselines nobody compares against. Both `make e2e` and `make e2e-update` now run in the image CI uses, so there is one renderer and one image per view. Repeat runs reuse a named volume for node_modules rather than reinstalling each time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…talled `@font-face` is document scoped — Chrome ignores it inside a shadow root — and `__MSG_@@extension_id__` is substituted only in CSS the manifest declares, not in styles injected at runtime. The rule shipped into the shadow root with the placeholder intact, so the bundled face never loaded and the view fell back to sans-serif. macOS and any other machine with Monaco installed matched on `local()` and hid both faults. The face now goes into the document head with the URL resolved through `chrome.runtime.getURL`, and `Monaco.woff` is web accessible so pages being formatted can fetch it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The commands act on whatever the page shows at that instant, so a view still settling copied nothing and left the assertion to fail on an empty string. Seeding a sentinel first makes "nothing was copied" distinct from a stale read, and the copy is retried until the clipboard moves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`maxDiffPixelRatio: 0.005` allowed ~4600 differing pixels on a 1280x720 frame, which is more than the whole text area of the query view. Both of its screenshots kept matching through the font change that rewrote every other image, so a regression there would have passed. Rendering is pinned to one container, so there is nothing for the tolerance to absorb. The two images it had been masking are regenerated here; the suite is stable over repeated runs without it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Playwright hides the text caret by injecting CSS into the document, which never reaches the input inside the closed shadow root. It blinked between captures and left a 14 pixel diff — invisible under the old tolerance, a flaky failure without it. `ShadowElement.evaluate` runs a function against a pierced element, with `text` now going through it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collapsing sets a class the browser then has to lay out, and both the rendered text and a copy of it read that layout, not the class. Asserting straight after the click caught the tree still expanded, which showed up as a copy containing the items that were supposed to be hidden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`clipboard.ts` becomes `selection.ts` now that it covers making a selection as well as copying one: `dragSelect` sweeps the mouse between two elements the way a reader would, and `copySelection` copies whatever is selected rather than the whole document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collapsing was only checked one way and only on a whole property. These add expanding a node back, collapsing a single array item while its siblings stay open, and dragging across one nested object to confirm the copy holds that object alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Strings that look like a URL or an address render as links and light up while the modifier is held, and a jq expression yielding more than one value renders each result rather than only the first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both settings only mean anything once they reach another part of the extension: the download mode changes the toolbar button on a formatted page, and a query run there has to travel through the background worker into the history the options page lists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sidebar was only checked for existing and for scrolling to one section. It is built from the headings the markdown produces, tracks where the reader is, and is how the query panel's help icon is meant to land somewhere useful — none of which was covered. Sidebar entries are now checked against the headings actually rendered rather than a fixed list, so adding a section to the manual does not quietly go unlisted. The mark on the current section is asserted after navigating and after scrolling, and the help icon has to open the manual itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Playwright settles a locator's box before clicking, and none of that applies to an element resolved by hand over CDP: the box was measured once and the mouse sent to wherever it had been. Layout keeps moving after the tree first renders — the bundled font finishing loading reflows all of it — so the click could land on nothing, leaving a collapse that never happened. Seen twice on CI and never reproduced locally, including at ten repeats across four workers, so this closes the gap in the helper rather than a diagnosed race. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was left uncovered because an earlier attempt never opened anything: `page.mouse.click` ignores a `modifiers` option, which only exists on locator clicks, so the modifier was never actually held and the handler never ran. Holding it through the keyboard works. The tab is given something to land on by serving the address from the test, so nothing leaves the machine. A plain click is covered alongside it, since "opens on modifier-click" means little without "does not open otherwise". The address form stays uncovered: it hands a `mailto:` to whatever the machine has registered, which is not something to invoke from a test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reasoning they carried is in the commit messages and in the two guides; the code reads on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Only the formatted view was captured light, and it took a duplicated test to do it. The suite now runs as two projects, and a test tagged `@screenshot` runs in both, so each view is committed once per theme and the project name supplies the suffix. Everything else stays on one theme rather than running twice for no gain. The light theme was carrying six uncovered views, including the options page and the manual. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Back to a stylesheet, as asked, but not the one it came from: `content-script.scss` is injected into the closed shadow root, where Chrome ignores `@font-face` entirely. Declaring the file through `content_scripts.css` puts it in the document and is also the one place Chrome substitutes `__MSG_@@extension_id__`, so the original rule works unchanged and the runtime-built stylesheet is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
evg4b
force-pushed
the
test/e2e-playwright
branch
from
September 17, 2026 18:20
1258734 to
3a46d16
Compare
|
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.



Adds an end-to-end suite that runs the packed extension in a real Chromium, plus committed screenshots so any change that alters rendering shows up as a CI failure with a diff.
What it covers
formatter.spec.tstoolbar.spec.tsquery.spec.tsoptions.spec.tsfaq.spec.ts20 tests, ~7s.
Reaching the UI
The content script builds everything inside closed shadow roots, which Playwright locators cannot enter.
e2e/support/shadow.tsresolves paths over CDP instead — segments separated by>>>, each queried inside the shadow root of the previous match:Clicks go through
page.mouseat the element's real box, so they are genuine user input. Known paths live ine2e/support/ui.tsrather than in the specs. The options and FAQ pages use open shadow roots and need none of this.Pages are served by fulfilling the request in Playwright, so there is no fixture server to manage.
Visual comparison
Baselines are committed under
e2e/*.spec.ts-snapshots/. Font rendering is host-specific, so the CI job runs insidemcr.microsoft.com/playwright:v1.63.0-noble— the same imagemake e2e-updateuses to regenerate them. Only the Linux PNGs are committed; macOS and Windows ones are ignored.CI
New
E2E Testsjob, depending onbuildand reusing the extension artifact it already uploads. The Playwright HTML report (with actual/diff images) is uploaded on failure.Noticed while doing this
The Linux baselines render in a fallback font rather than the bundled Monaco:
chrome-extension://__MSG_@@extension_id__/Monaco.woffships verbatim incontent-script.js, and that placeholder is only substituted in manifest-declared CSS, not in styles injected at runtime. macOS hides it vialocal('Monaco'). Left alone here — fixing it would rewrite these baselines — but worth a follow-up.🤖 Generated with Claude Code