To audit what each UI test actually exercises, every step now snapshots the
live window to <TestFixtureName>/<TestName>_<NN>_<ShortDescription>.png: a
booted frame (emitted automatically by TestHarness.BootAsync), one after each
state-changing action, and one before each assertion. Flip ILSPY_TESTS_VISIBLE=1
to render the filmstrip; it lands under %TEMP%/ilspy-test-captures (overridable
via ILSPY_TEST_CAPTURES).
The step number and fixture/test name are derived automatically so inserting a
breakpoint never renumbers the rest. The identity is recorded up front from the
real ITest in an ITestAction hook rather than read live: NUnit's
TestContext.CurrentContext does not flow onto async continuations, so a capture
after an await would otherwise collide under the ad-hoc context. And when
rendering is off the whole call is a true no-op -- not even a dispatcher pump --
so instrumenting a test can never perturb the navigation/tab timing it asserts
on. Full headless suite stays green.
Nearly every headless UI test opened with the same four-line prologue
(resolve the shared MainWindow, show it, cast its DataContext, wait for the
assembly list), then repeated the corelib lookup, the EnsureLazyChildren +
Children.OfType<T>().Single() drill, the registry single-by-header lookups,
and the open-an-assembly-and-wait dance. The duplication made the intent of
each test hard to see and every signature tweak a suite-wide edit.
Collapse those into TestHarness (BootAsync, OpenAssemblyAsync, GetCommand,
GetEntry) and TreeNavigation extensions (FindCoreLib, GetChild<T>, Expand<T>),
then apply them across the suite. Net ~865 lines of boilerplate removed with
no change in behaviour; the full headless suite stays green.
DisplaySettings.SortResults was persisted but unused — RunningSearch hardcoded
ComparerByFitness. Now SearchPaneModel.RestartSearch reads the setting at
start-of-search and passes either ComparerByFitness (default) or ComparerByName
into RunningSearch. Capturing at start matches WPF — mid-run toggles only
affect the next search.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The user reported that expanding an enum (System.DayOfWeek) with a search
term active showed only Base/Derived nodes — every FieldTreeNode child
hidden. This is NOT a recent regression. It traces back to commit
45461ddde, which made LanguageSettings.SearchTermMatches honour the
SearchTerm and wired SearchPaneModel to push its term into
LanguageSettings on every keystroke.
The pane's watermark advertises a prefix DSL that the parser didn't
implement — only inassembly: and innamespace: were honoured. Typing
"t:String" treated the whole thing as a literal keyword and matched
nothing useful.
Ctrl+E / Ctrl+Shift+F previously activated the pane but left
keyboard focus wherever it was — the user still had to click the
TextBox before typing. Add a FocusRequested event on SearchPaneModel
that the view subscribes to and pushes Focus() on the SearchInput
TextBox through Dispatcher.UIThread.Post (a tick lets the freshly-
active pane surface in the layout so .Focus() actually takes).
Assisted-by: Claude:claude-opus-4-7:Claude Code
SearchPaneModel gains an Activate(SearchResult) method that resolves
result.Reference through AssemblyTreeModel.FindTreeNode and moves the
assembly-tree selection there — exactly the WPF NavigateToReferenceEventArgs
flow, minus the message-bus indirection. SearchPane.axaml.cs wires
DoubleTapped + Enter on the results ListBox to call Activate.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Flip LanguageSettings.SearchTerm from the always-empty placeholder
to a real ObservableProperty backing field, and replace the
always-true SearchTermMatches stub with a case-insensitive substring
contains. Empty term still matches everything (no filter active).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Wire the search pane to the shared ICSharpCode.ILSpyX.Search engine.
Setting SearchTerm or SelectedSearchMode now cancels any in-flight
run and starts a fresh one; cleared term tears the run down.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replace the placeholder TextBlock with the real layout: a TextBox at
the top bound two-way to SearchPaneModel.SearchTerm (UpdateSourceTrigger
PropertyChanged so the orchestrator reacts on every keystroke once it
lands), a ComboBox bound to SearchModes / SelectedSearchMode, and a
ListBox that renders each SearchResult as three trimmed columns
(Name / Location / Assembly).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replace the SearchPaneModel stub with the real shape that the pane's
view, the background streaming orchestrator, and the LanguageSettings
filter cascade will all bind against.
Assisted-by: Claude:claude-opus-4-7:Claude Code