The Avalonia port had placed the UI app in an ILSpy.* namespace tree,
while the csproj RootNamespace and every prior release (through 10.1)
use ICSharpCode.ILSpy.*. Restoring the historical namespace reduces the
public API diff against release/10.1 for plugin authors and removes the
shadowing that forced global:: qualifiers in the test project. The
Images class and AccessOverlayIcon enum move back into the root
namespace (as in 10.1), since an ICSharpCode.ILSpy.Images namespace
would shadow the Images class for all code inside ICSharpCode.ILSpy.
Assisted-by: Claude:claude-fable-5:Claude Code
The startup welcome page renders the same About content in the reusable main tab. Help > About opened a second, static About tab beside it, so the user saw the page twice. Track the welcome content and, while it is still the live main-tab content, have Help > About activate it rather than open the singleton. Falls through to the singleton once a tree-node selection has replaced the welcome page.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Reopening_About_Reuses_The_Same_Tab matched any tab whose Title is "About", but
two tabs can carry that title: the singleton menu-About (IsStaticContent: true,
opened via OpenSingletonTab) and the transient boot welcome page (ShowWelcome,
IsStaticContent: false, a non-static main-tab page shown when nothing is
restored). The welcome page's presence races with boot, so when it lingered the
test's .Single(IsAbout) saw two tabs and threw "Sequence contains more than one
matching element" -- a ~50% order-dependent flake.
The singleton is what the test means to verify, so match it precisely
(IsStaticContent: true). OpenSingletonTab/CloseDockable reuse was always sound;
this was purely an imprecise test predicate. Deterministic now (5/5 full-suite
runs green; was ~50%).
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.
Closing and reopening Options or About built a fresh ContentTabPage (and a
fresh owned view) each time, losing the selected options page and
re-rendering the About output. Retain these static-content tabs by key on
DockWorkspace and re-add the same instance on reopen, so the dockable --
and the view it owns via IDockableViewOwner -- persists for the session.
Embedded About resource pages (license, third-party notices) are
singletons too.
Assisted-by: Claude:claude-opus-4-8:Claude Code