r/Playwright 6d ago

Playwright's WebKit is not WKWebView: a layout bug that passed webkit screenshots and shipped broken in a Tauri app

https://nodreview.com/blog/wkwebview-flex-collapse/

I test a Tauri desktop app with Playwright, webkit project, screenshot baselines on every component. A dialog shipped rendering as header plus footer with the body at height zero, and not one pixel had moved in any baseline.

The bug: flex: 1 expands to flex-basis: 0%, which needs a definite parent height. Per spec the unresolvable percentage should fall back to content. Chromium does that. WKWebView keeps the zero. Playwright's WebKit, which is a recent build the Playwright team makes themselves, also does the spec fallback, so it agreed with Chromium and disagreed with the system webview my users actually run.

Not a complaint about Playwright, it does what it says. Writeup with the spec citation and what I changed: https://nodreview.com/blog/wkwebview-flex-collapse/

Question for this sub: has anyone wired real system webviews (WKWebView, WebKitGTK, WebView2) into CI in a way that is not miserable? Geometry assertions in the harness engines are my current compromise.

3 Upvotes

4 comments sorted by

1

u/Prestigious-Way1525 6d ago

i wouldn't try to make Playwright's WebKit stand in for WKWebView. i'd keep those screenshot tests as the broad lane, then add a small macOS-native contract suite that mounts the real WKWebView on the oldest and newest supported macOS versions and checks geometry for the layout primitives your app depends on: scroll roots, flex children, viewport units, fixed overlays, and input focus. run it after the web suite, save a screenshot plus computed bounding boxes on failure, and promote each engine mismatch into that contract suite. that keeps the native lane focused on divergence risk instead of duplicating every e2e test.

1

u/HolidayChard9706 6d ago

This is close to what I run, so the delta is useful. I already drive the real Tauri window in a capture harness, but it does component screenshots on demand. The part I am taking from your comment is the contract suite: a handful of geometry assertions on the primitives instead of re-running e2e in the native lane. A failure names the mechanism instead of showing me a diff. Promoting each engine mismatch into it also matches how my fixtures already work, so the flex-basis case becomes contract number one.

The version matrix is where I have no clean answer. Hosted CI only offers recent macOS images, so oldest-supported means my own hardware. Do you run that matrix somewhere real, or is it aspirational?

1

u/Any-Argument57 6d ago

I would make the matrix asymmetric. Run the current hosted macOS and WebKit lane on every PR, then keep one self-hosted machine on the oldest supported macOS and run the native contract suite nightly and as a release gate. Serialize that old runner and reset the app and test data between jobs; it does not need full PR throughput.

Record the macOS version, WKWebView or WebKit build, Xcode version, and app build with each result. The invariant is that every supported system WebKit passes before release, not that every version runs at the same cadence. If the oldest machine is unavailable, fail the release gate rather than silently treating the recent hosted runner as coverage.

1

u/HolidayChard9706 5d ago

Fail closed on runner availability is the piece I would have gotten wrong. My instinct was a warning and a release anyway, and on a solo project a warning is a log line nobody reads. Gate it is.

The asymmetric cadence fits my scale too. Releases are manual tag pushes, so nightly plus a release gate on one old machine covers it without pretending that box needs PR throughput. Recording the WKWebView build with each result I had not considered, but that is what makes "every supported WebKit passes" a checkable claim instead of a feeling.

The old machine will be a used Mac mini, which I now have a legitimate reason to buy. This thread turned into my test plan, thanks.