Skip to content

The in-app half

Everything else here runs against an application that references nothing. Winwright.InApp is the exception: it is the only thing this project asks you to put inside an application your users run, which is a different decision from taking a test dependency and deserves a different answer.

What it does in a release nobody is testing

Section titled “What it does in a release nobody is testing”

Nothing.

It runs no thread, watches no directory and holds no timer. Every surface below is armed by an environment variable, and the process that starts your application is the only thing that can set one — so a copy on somebody’s machine, started from their Start menu, answers nothing and writes no file. The work happens on the message loop your application already runs, only when a harness sends the message, and only where the variable named somewhere to write.

That is the property that makes the protocol safe to leave in a shipped build, and it is worth more than any feature list.

SurfaceWhat it answersArmed by
GeometryThe geometry of what was drawn, dumped for a harness that has no tree to read.WINWRIGHT_GEOMETRY
The variable naming the file to dump into. Unset means dump nowhere, for the reason PathVariable means report nothing: an application shipped to its users is not under test, and one writing files because it once was is worse than one that never dumped at all.
RendersRendering this application's own tree when a harness asks for it.WINWRIGHT_RENDERS
The variable naming the directory renders may be written into. Unset means answer nothing, for the reason PathVariable means report nothing.
SurfacesWhat the application drew, said out loud so a capture can be asserted against it.WINWRIGHT_SURFACES
The variable naming the file to report into. Unset means report nowhere: an application shipped to its users is not under test, and one writing files because it once was is worse than one that never reported at all.

2 of the engine’s 121 verbs need it. That is the shorter list than most people guess, and each of them prevents a specific way a harness gets the wrong picture:

  • A render of your own visual tree, rather than a copy of the screen. A copy carries whatever was standing in front of the window when it was taken — a notification, somebody else’s dialog, a screen saver — and a render cannot, because there is nothing in front of a visual tree.
  • A popup’s own tree, which is the one surface no copy of the screen can take. A popup is layered so it can draw a shadow, so the soft edge of a copy of it is the desktop behind it rather than the popup.
  • The rectangle your application actually painted, in physical pixels. A harness in another process can only guess at one, and a guess about a popup, a flyout or a page that scrolled is a capture asserted against a rectangle nobody drew. Layout happens in device-independent units and a copy works in pixels: a rectangle handed over in the wrong one is right at one hundred percent and wrong at every scaling a developer actually runs.

The two halves reference nothing of each other. They find each other by a registered window message and agree on nothing else:

  • Winwright.OwnRender
  • Winwright.OwnRender.Popup
  • Winwright.OwnRender.Why

Each ask is its own message rather than a field added to the one before it, so an application shipping an older half than the harness driving it simply does not answer the newer ask — rather than reading a two-field payload as a path. Which way that skew runs is not hypothetical: this half is what you ship, and it reaches your users by a release.

A run that asks for a render from an application with no in-app half does not fail, and does not quietly take a screen copy instead. It answers a hole, naming the condition:

an application that renders its own tree when asked

That is the desk-free half of reading the verdict: the assertion never ran, it is named in the summary, and the exit code is 2. If that is the message that brought you here, this page is the answer to it.

It needs <UseWPF>true</UseWPF> in the application's project.

<PackageReference Include="Winwright.InApp" Version="1.0.0" />

Then arm it once, wherever your application starts, and dispose what comes back when it stops. Everything after that is the harness’s business.

  • The verbs — the full list, with what each one needs.
  • Reading the verdict — what a hole is, and every condition that produces one.
  • The README — the arming call, and what each surface reports.