| name | windows-gui-manual-testing |
| description | Windows GUI testing: verify desktop UI with visual evidence. |
Windows GUI Manual Testing
Overview
Use this skill to inspect Windows desktop UI behavior visually and report concrete findings. Focus on reproducible evidence: screenshots, extracted video frames, exact control names, theme/state context, and before/after comparisons.
This is a narrow evidence-based verification specialist for Windows desktop UI, including Qt, Avalonia, WinUI/WPF, WebView/native-child, and other native or hosted Windows surfaces. Return one visual findings package with concrete observations, not codebase exploration or implementation ownership.
Hard crop gate: before using any cropped screenshot, video, or extracted frame as full-window evidence, inspect a verification frame and prove it contains the entire target window. For normal overlapped windows, the top-right close button must be visible. If any edge is clipped or the close-button anchor is missing, discard the crop and use full-desktop evidence or recalculate the rectangle before proceeding.
Scope
- Use when visual/manual evidence is needed before a fix, after a fix, or when the bug is only visible through screenshots, video, or frame-by-frame inspection.
- Keep the work grounded in running UI behavior, control state, theme context, and reproducible visual evidence.
- Do not treat this skill as a codebase explorer, a generic implementation owner, a global UX reviewer, or a lead.
Related skills
$analyzing-video-bugs — frame extraction, scene-change detection, and dense sampling for any video file (user-provided or captured here in step 3). Always route video evidence through that skill rather than reading raw video.
$bug-hunting — broader diagnostic-logging methodology for runtime bugs whose cause is not obvious from visual evidence alone. Use when visual inspection narrowed the symptom but the underlying gate/state needs runtime confirmation.
Workflow
1. Prepare the context
Record the environment before drawing conclusions:
- app/build being tested
- theme or style mode (
system, custom, CAD, dark/light)
- actual per-monitor scale from
GetDpiForWindow, plus the target process's DPI-awareness mode from GetProcessDpiAwareness or DPI_AWARENESS_CONTEXT
- window size and whether the panel is docked, floating, or maximally narrowed
- exact control path, for example
Analysis -> Data -> Resampling -> Mode
When the issue is about interaction timing, note the pointer location and whether the bug happens on first open only or on every open.
2. Prefer the right evidence type
Use the lightest artifact that can prove the problem:
- single screenshot: static layout, wrong colors, clipped text, mismatched checkbox style
- two screenshots: before/after state, theme comparison, collapsed vs expanded panel
- short video: popup jerk, animation glitch, text reflow, control motion under the mouse
- exported frame sequence: detailed timing analysis when one bad intermediate frame matters
If the user gives a local image path, Read the frame image (Read tool on Claude Code; view_image on Codex).
If the user gives a local video path, extract frames first, then inspect representative frames instead of guessing from the video description.
3. Obtain and analyze video evidence
When the bug is animation-, timing-, or sequence-dependent, video evidence is required. The video may come from one of two sources:
-
The user records and shares the file themselves. Skip directly to frame analysis via $analyzing-video-bugs using the provided path.
-
The agent captures the video itself when no recording exists. Probe the installed ffmpeg build first with ffmpeg -hide_banner -filters | Select-String ddagrab (or grep ddagrab). Prefer the Desktop Duplication / Direct3D 11 ddagrab filter when present for hardware-composited Direct3D, OpenGL, WebView2, and Qt Rendering Hardware Interface surfaces; use the installed build's documented ddagrab input syntax. Keep gdigrab as the fallback and for window-title or region convenience.
# Full desktop, 30 fps, 10 seconds, H.264 mp4
ffmpeg -f gdigrab -draw_mouse 1 -framerate 30 -i desktop -t 10 -c:v libx264 -preset ultrafast -pix_fmt yuv420p .scratch/capture.mp4
# One-frame artifacts: use 60 fps and keep the clip short
ffmpeg -f gdigrab -draw_mouse 1 -framerate 60 -i desktop -t 9 -c:v libx264 -preset ultrafast -pix_fmt yuv420p .scratch/capture.mp4
# Specific window by title (window must be visible and named):
ffmpeg -f gdigrab -draw_mouse 0 -framerate 30 -i title="ExactWindowTitle" -t 10 -c:v libx264 -preset ultrafast -pix_fmt yuv420p .scratch/capture.mp4
# Region of screen:
ffmpeg -f gdigrab -draw_mouse 0 -framerate 30 -offset_x 100 -offset_y 100 -video_size 1280x720 -i desktop -t 10 -c:v libx264 -preset ultrafast -pix_fmt yuv420p .scratch/capture.mp4
Correct full-window capture recipe:
- Find the target top-level HWND, then call
GetWindowRect and the virtual-screen metrics (SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN). On Windows with DPI scaling or extended glass/shadow bounds, also call DwmGetWindowAttribute(..., DWMWA_EXTENDED_FRAME_BOUNDS, ...) and prefer the DWM extended-frame rectangle when GetWindowRect clips the right or bottom edge.
- If any side of the window rect is outside the visible virtual screen, restore/move/resize the window into a known visible rectangle before recording.
- Normalize capture width and height to even values for H.264.
- Capture one still frame with the exact intended
-offset_x, -offset_y, and -video_size.
- Read the frame image (Read tool on Claude Code; view_image on Codex). Confirm the entire app surface is present from left edge to right edge and from title bar to bottom edge. For normal overlapped windows, the top-right close button must be visible; if the close button is missing, the crop is wrong and cannot be used as full-window evidence.
Once a video file exists on disk, hand it off to $analyzing-video-bugs for the systematic ffprobe → coarse extraction → scene-change detection → dense sampling → native-resolution verification pipeline. Do not read raw video files; do not extract frames ad hoc here when the specialized skill already owns that workflow.
4. Inspect dropdowns and popup animation
For combo boxes and popup lists, check these phases explicitly:
- closed resting state
- first visible popup frame
- mid-open frame
- “almost open” frame
- fully open frame
- selected state after close
Look for:
- popup geometry changing after it is already visible
- selected text duplicating, disappearing, or repainting late
- popup chrome and content moving in separate stages
- shadow/background arriving after the list content
- popup width snapping after open
- theme mismatch between closed combo and opened popup
If menus behave correctly but combo boxes do not, treat them as separate popup paths and do not assume a style-only bug.
5. Inspect layout shifts and text reflow
When a control “moves under the mouse”, inspect neighboring labels and summaries, not just the clicked widget.
Specifically check for:
- word-wrap adding/removing lines
- conditional help text appearing/disappearing
- summary labels changing height
- scrollbars appearing and shrinking the viewport
- combo open/close changing available width and forcing relayout
When stabilizing text, prefer preserving readable content over blindly truncating it. If a compact one-line summary is needed, keep the full text in a tooltip or a dedicated detail surface.
6. Compare themes intentionally
For theme-dependent bugs, check at least:
system
- the project’s custom/styled mode
If the project has an additional theme family such as CAD, check that too. A fix that only improves one theme but leaves another on a different popup path is incomplete.
For before/after or cross-theme no-change claims, use the pixel-diff method in step 8; side-by-side inspection alone cannot support a no-visual-regression PASS.
7. Report findings cleanly
Return one findings package with the tested control path; environment (app/build, theme, actual DPI and DPI-awareness mode, window state); evidence type; concrete observations; structural-vs-cosmetic classification; theme-specificity; residual risk; and a final gate decision of PASS, REVISE, or BLOCKED. Report findings as concrete UI observations, not guesses:
- what control was tested
- what frame/state is wrong
- what moved, clipped, duplicated, or repainted late
- whether the bug is theme-specific
- whether the issue is structural (
popup path, relayout, text wrap, stale style path) or merely visual
- the evidence file path plus frame number or timestamp for every observation, not only intermediate-frame findings
- whether blur or fractional-scale behavior comes from Desktop Window Manager bitmap stretching of a DPI-unaware/system-aware process (an app-manifest finding) or from the paint path
Prefer short cause statements like:
Popup opens in two geometry stages.
Summary label changes height when the combo opens, moving the next control.
System theme is still using the custom combo popup path.
7.1 Persist non-passing findings
On REVISE or BLOCKED, file each finding in work-items/bugs/<date>-<slug>.md using the qa-engineer-owned format with found-by: windows-gui-manual-testing before returning the verdict. Cite volatile machine-local evidence by .scratch/ path and frame timestamp, but make the bug's repro steps, control path, theme, actual DPI, DPI-awareness mode, and window state sufficient to re-derive that evidence.
8. Re-test after a fix
After a GUI fix, re-check the exact failure path first, then verify nearby regressions:
- same control
- same theme
- same window width
- same first-open interaction
- neighboring controls that used to shift
For dropdown fixes, always verify both:
- first open after window creation/theme switch
- reopen after one successful interaction
For still-image before/after, theme, or no-change claims, produce a machine diff such as magick compare -metric AE before.png after.png diff.png or an ffmpeg difference blend. Mask only named legitimately volatile regions such as clocks, carets, or cursors, and report the changed-pixel count and affected regions. An eyeball-only comparison cannot establish a no-visual-regression PASS.
Guardrails
- Do not call a UI issue “fixed” from code inspection alone when visual evidence is available.
- Do not hide structural GUI problems with cosmetic text tweaks unless the user explicitly asks for a stopgap.
- Do not rely on a single screenshot for animation bugs when a short video or frame sequence exists.
- When a visual issue depends on an intermediate frame, say that explicitly and keep the evidence path plus frame numbers or timestamps in the notes.