Skip to content

v1.1.4

Choose a tag to compare

@github-actions github-actions released this 20 Oct 17:42
· 64 commits to main since this release

This release lets a case move files in and out of the application: dropped onto it from outside,
and chosen through the common Open and Save dialogs. A window drawn by Direct3D can now be
photographed, a fixture can hand its staged directory to an application that takes paths as
flags, and an act no longer gives up on an element the application is still building. Two changes
are something an existing setup may notice, and they are marked.

A file dropped onto the application

{ "locator": "Pane#DropZone", "act": "drop file", "with": "{files}/photo.png", "named": "the photo is dropped" },
{ "locator": "Text#ImageSize", "act": "read", "reads": "name", "expect": "1920 x 1080" }

drop file drags the file in with onto the step's element, the way Explorer would. with is
relative to the project's root, or in the fixture's staged directory through {files}. The drag is
a real system drag with the file list Explorer offers (CF_HDROP), so the application's own drop
handler runs. The effect the target answers is the act's evidence: a target that answers with no
effect is a hole that says so, not a pass. It needs the foreground and is attempted once. A file
that is not there stops the case.

A file chosen through the Open or Save dialog

{ "locator": "Button#ExportButton", "act": "invoke" },
{ "locator": "Window[class=\"#32770\"]", "act": "choose file", "with": "{files}/out" },
{ "locator": "Window[class=\"#32770\"]", "act": "read", "absent": true, "named": "the dialog closed" },
{ "file": "{files}/out.webp", "act": "read", "sameBytesAs": "cases/expected/out.webp" }

choose file puts the path into the common dialog's name box and confirms, through the
controls' own patterns, so it needs no foreground. The locator is the dialog, #32770 by class.
Whether the dialog accepted the path is the next step's claim, not the act's: a read with
"absent": true says the dialog closed. Address the dialog's controls by id, never by name,
because the names are in the shell's language.

A Mica window with Direct3D content has a picture

A region drawn by a window of its own was a hole in the off-screen render: an HwndHost holding a
DXGI swapchain, a WinForms control or a browser is not in the visual tree. On a Mica window the
screen copy is refused for the glass as well, so such a window had no capture route at all.

The render now copies each such region from its window and draws it into the picture where the
tree reserved room for it. The copy asks the compositor for what it composed, so a Direct3D
swapchain is included and the glass around it is not. The receipt names each region it copied.
Three cases are refused instead of written:

  • a hosted region in no window that is shown, which has nothing to copy from;
  • a copy that comes back black throughout, which is what a surface the print could not reach
    looks like;
  • a D3DImage that drew nothing into the render. A render is a software pass, and a D3DImage
    reaches one only when its back buffer was set with enableSoftwareFallback: true.

A fixture's arguments can name the staged directory

{
  "name": "exporting into its own folder",
  "arguments": ["--open", "{files}/photo.jpg", "--export", "{files}/out.png", "--models", "{root}/models"],
  "files": ["cases/fixtures/photo.jpg"]
}

arguments now take the tokens a variable takes: {files}, {root} and {root/}. An application
whose paths come in as flags opens the staged copy and writes beside it, and its other paths still
resolve from the project's root. An argument naming {files} also counts for the check that refuses
files staged where nothing looks. Before, such a fixture was refused, and the token would have
reached the application literally.

An act waits for an element the application is still building

(May notice.) A select right after an expand on a WPF combo box failed about one run in
four, refused as "nothing matched". The look had found the list item, but the item's automation
peer was replaced before it could be read, and the act gave up on that look instead of polling on.

A look whose match is gone before it is read now counts as no look, and the act polls to its
deadline as it does for any absence. Where nothing arrives, the refusal says which absence it was:
nothing matched, in 80 looks over 2000 ms, or it matched on 3 of 80 looks and was gone each time
before it could be read
. The first points at the locator, the second at the application. A test
asserting the old wording, "nothing matched, or what matched has gone since", sees the new one. If
you put a read step between an expand and a select to get around this, you can delete it.

The plugin's marketplace is named winwright

(May notice.) The Claude Code marketplace was named alegauss, the same as roadkeep's, and
settings key marketplaces by name. A project using both could register only one of them: adding one
replaced the other. It is now named after the plugin:

claude plugin marketplace add alegauss/winwright --scope project
claude plugin install winwright@winwright --scope project

If your .claude/settings.json carries winwright@alegauss, remove that entry and its marketplace,
then run the two commands above.

Installing

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

Full Changelog: v1.1.3...v1.1.4