Files
PowerToys/.github/skills/ui-tests-local-vm/scripts/Invoke-GuestScript.ps1
Gleb Khmyznikov e48152c52d [UITests] Add UITest.Next suites (Image Resizer, Peek, File Explorer add‑ons, File Locksmith) + local‑VM tooling and CI test‑signing (#49671)
## Summary

Adds end‑to‑end UI tests on the `Microsoft.PowerToys.UITest.Next`
(winappcli) framework for three
modules, grows the shared `.Next` test framework with the helpers those
suites needed, and adds the
CI plumbing that lets shell‑extension tests exercise the **real**
Windows 11 modern context menu.
Also ships two agent skills that document how to write and run these
tests.

Product runtime behavior is **unchanged** — the only product edits are
test‑observability hooks in Peek
and a unit‑test project exclude.

Closes: https://github.com/microsoft/PowerToys/issues/40660
https://github.com/microsoft/PowerToys/issues/49424
https://github.com/microsoft/PowerToys/issues/40661

## What's added

### New UI test suites
- **Image Resizer** —
`src/modules/imageresizer/tests/ImageResizer.UITests`: context‑menu
enable/disable
tracking, the resize dialog, custom presets, every fit mode, every unit,
filename format, keep‑date,
  shrink‑only, replace‑in‑place, and orientation.
- **Peek** — `src/modules/peek/Peek.UITests.Next`: file‑preview coverage
across image/text/archive/
  markdown types with per‑arch visual baselines.
- **File Explorer add‑ons** —
`src/modules/previewpane/PreviewPane.UITests`: Preview Pane handlers and
  thumbnail providers.

### `UITestAutomation.Next` framework
- New helpers: `ExplorerShell` (Shell selection/view‑mode interop),
`WaitHelper` (structured stable
waits), `WindowControl` (foreground/context‑menu/process control),
`VisualAssert` (image compare),
  `WindowHelper`.
- Updates to `Session`, `UITestBase`, `SettingsConfigHelper`,
`WinappCli`.
- New `UITestAutomation.Next.UnitTests` project covering the new
wait/settings/CLI helpers.

### CI — sign sparse MSIX so the modern menu registers
- **`.pipelines/signSparsePackages.ps1`** — self‑signs each sparse
context‑menu MSIX with a
publisher‑matching test certificate and force‑trusts it (machine
stores), so
`AddPackageByUriAsync` succeeds on otherwise‑unsigned PR builds. Robust
`signtool` discovery with a
  NuGet fallback; test‑only trust that asserts no security.
- Wired into **`.pipelines/v2/templates/job-test-project.yml`** as a
best‑effort step covering the
run‑in‑place, machine‑install, and per‑user‑install locations. Signs
nothing it can't (skips
  already‑signed packages) and never fails the job.

### Product changes (test observability only)
- **Peek `FilePreview.xaml` / `.xaml.cs`** — a named `LoadingIndicator`
and a hidden automation peer
that exposes the current preview state as text, so tests can read load
state deterministically. No
  runtime behavior change.
- **`ImageResizer.UnitTests.csproj`** — exclude the sibling
`ImageResizer.UITests\**` folder from the
  unit‑test compilation.

### Agent skills & docs
- **New `ui-tests-local-vm` skill** — run `.Next` suites in persistent
dockur/windows VMs: setup,
agentic loop, image customization, troubleshooting, the shell‑extension
**signing** reference, plus
  controller/guest scripts and VM templates.
- **Updated `ui-tests-migration` skill** — WinAppDriver/Selenium →
`.Next` porting guidance
(CI stability, Explorer/shell‑extension test design, patterns &
pitfalls).
- **`doc/devdocs/development/ui-tests.md`** — updated for the `.Next`
workflow.

## Testing
- All three suites pass locally and in CI across **x64 Win10**, **x64
Win11**, and **arm64** (machine
  and per‑user install legs).

## Reviewer notes
- No product runtime behavior changes; product edits are limited to the
Peek test hooks above.
- The CI signing step is a **test‑only** trust anchor (self‑signed,
scoped to the agent) and is
best‑effort, so it can only add modern‑menu coverage and never regress
the job.
2026-08-10 19:55:03 +02:00

65 lines
2.5 KiB
PowerShell

# Copyright (c) Microsoft Corporation
# The Microsoft Corporation licenses this file to you under the MIT license.
# See the LICENSE file in the project root for more information.
<#
.SYNOPSIS
Runs a scriptblock inside the local UI-test guest over PowerShell Direct, handling the credential
import and PSSession lifecycle. Token-efficient replacement for the repeated
Import-Clixml / New-PSSession / Invoke-Command / Remove-PSSession boilerplate when inspecting or
mutating guest state (package registration, staged runtime files, registry, processes).
.PARAMETER ScriptBlock
The scriptblock to run in the guest. Its output is returned to the host.
.PARAMETER VmName
Name of the Hyper-V virtual machine. Hyper-V access is required: either an elevated shell or
membership in the local Hyper-V Administrators group.
.EXAMPLE
./Invoke-GuestScript.ps1 -VmName PowerToysUiTest-Win11 -ScriptBlock {
Get-AppxPackage *ImageResizerContextMenu* | Select-Object -Expand Name
}
.EXAMPLE
./Invoke-GuestScript.ps1 -VmName PowerToysUiTest-Win11 -ScriptBlock {
Get-Process explorer | Select-Object Id, SessionId
}
.EXAMPLE
# Neutralize a sparse package to reproduce CI's unsigned/classic scenario.
./Invoke-GuestScript.ps1 -VmName PowerToysUiTest-Win11 -ScriptBlock {
Get-AppxPackage -AllUsers *ImageResizerContextMenu* | ForEach-Object { Remove-AppxPackage -Package $_.PackageFullName -AllUsers }
Rename-Item C:\PowerToysUiTestRun\PowerToys\WinUI3Apps\ImageResizerContextMenuPackage.msix -NewName ImageResizerContextMenuPackage.msix.disabled
}
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][scriptblock]$ScriptBlock,
[Parameter(Mandatory)][string]$VmName,
[object[]]$ArgumentList = @(),
[string]$CredentialPath = (Join-Path $env:LOCALAPPDATA 'PowerToysUiTestVm\admin.credential.xml')
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path $CredentialPath)) {
throw "Credential file not found: $CredentialPath. Point -CredentialPath at the VM's admin.credential.xml."
}
$credential = Import-Clixml $CredentialPath
try {
Import-Module Hyper-V -ErrorAction Stop
Get-VM -ErrorAction Stop | Out-Null
}
catch {
throw 'BLOCKED: Hyper-V is not accessible from this shell. Run from an elevated PowerShell 7 terminal, or add this account to the local "Hyper-V Administrators" group.'
}
$session = New-PSSession -VMName $VmName -Credential $credential
try {
Invoke-Command -Session $session -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
}
finally {
Remove-PSSession $session -ErrorAction SilentlyContinue
}