Skip to content

feat(mvux): advanced mocking & previews — spec 013 + implementation (WIP) (backport #3165) - #3176

Open
mergify[bot] wants to merge 19 commits into
servicing/7.4from
mergify/bp/servicing/7.4/pr-3165
Open

feat(mvux): advanced mocking & previews — spec 013 + implementation (WIP) (backport #3165)#3176
mergify[bot] wants to merge 19 commits into
servicing/7.4from
mergify/bp/servicing/7.4/pr-3165

Conversation

@mergify

@mergify mergify Bot commented Sep 4, 2026

Copy link
Copy Markdown

Summary

Draft PR tracking the "Advanced MVUX mocking & previews" work.

Lands Spec 013 – MVUX Mocking & Previews (design under
specs/013-mvux-mocking-previews/) and will carry the implementation on top.
Kept as draft until the implementation is in.

Scope

  • Spec 013 design (spec.md, architecture.md, implementation.md, history.md)
  • Implementation — 3-tier design:
    • Tier 1: non-generic MessageEntry CLR type in Core
    • Tiers 2/3: strongly-typed, mock at the Model-feed cache level, derived feeds preserved
    • Generated mocking on the external side
  • Tests
  • Docs

Related

Closes #3150
Depends on #3149 (per issue, should land first)


This is an automatic backport of pull request #3165 done by [Mergify](https://mergify.com).

Restored state = end of discussion (lost commits 8d589d9 -> 292fb5f ->
2618def -> cd4c9ad), merged from local recovery notes + David's VS Code
copies (his architecture.md was the most recent, post-2618def).

- spec.md: 3 tiers, real VM + real Model with null-injected services,
  derived feeds survive via Model-feed cache swap anchor, external
  mocking generator (consumer test project), D1-D9 decision log,
  open question: context-wide scope / ambient MockingService.Enable()
- architecture.md: seams, swap anchor, MVUX gen (analysis + dependency
  attributes + hidden typed hooks) vs Mocking gen (metadata-driven
  {Model}Mock/Create/SetModel), tier-1 authorable plain-CLR MessageEntry
  (not a DependencyObject, not observable) with first-class custom axes,
  natural feed evolution contract, XAML examples
- implementation.md: package split, attribute shapes, hidden hooks,
  typed vocabulary, diagnostics FEED3201-3203/MOCK0001, P0 canaries
  (derived-swap gate, null-inject, identity matrix, context spike),
  test plan, docs plan
- history.md: full version/decision chronology of the spec discussion

(cherry picked from commit d72a52c)
- spec.md: audience/deliverable split, derived-feeds-survive flow,
  three-tier layering, generator responsibility split, end-to-end
  sequence, tier-1 evolution sequence; risks and decision log as tables;
  status header cleaned for team/CEO review (history stays in history.md)
- architecture.md: swap-anchor mermaid in section 1, end-to-end ASCII
  flow replaced by a sequence diagram in section 5

(cherry picked from commit 49c37d3)
(cherry picked from commit cc4a510)
…he swap fail hard

Records the decision to drop the dedicated static flag in favour of a per-context bit, and
to reuse the hot-reload reflection driver with strict failure instead of best effort.

(cherry picked from commit 51c791c)
Adds a per-context bit inherited at creation and reads it where the wrap is decided, so only
contexts created under an activation scope wrap their feeds and a live application pays
nothing.

(cherry picked from commit 57c8d73)
Classifies every feed member as service dependent, derived or independent, and instruments
the constructors so an eagerly dereferenced service is reported. The metadata is what the
consumer generator reads.

(cherry picked from commit a5ed1d0)
Commands have no hot-swap state, so a dedicated seam reassigns the command property after
construction. Construction itself needs no seam: the public constructors plus the ambient
scope are enough.

(cherry picked from commit 007e88c)
Adds the activation scope, the swap engine and the typed feed vocabulary used by the
generated mocks.

(cherry picked from commit 5852d9b)
The consumer generator reads the app metadata and emits the mock record, the Create
factories and the SetModel facade.

StateImpl always implements IHotSwapState, so the previous type test never rejected a
non-wrapped feed and the swap silently did nothing. Added CanHotSwap and made the engine
throw when a mocked feed is not swappable.

(cherry picked from commit f4f5aff)
Adds Value, Empty, Undefined, Loading, Error and Refreshing for both scalar feeds and list
feeds, built on pinned messages and strongly typed end to end.

(cherry picked from commit 6510bbc)
…cabulary

The generated record exposes Empty with every input pinned to its empty state, Create()
builds from it, and commands can be overridden through the view-model seam.

(cherry picked from commit 90aea90)
Logs what landed for the tier 2 and 3 implementation and what is left out of scope.

(cherry picked from commit 7cd1650)
The mocked feed vocabulary already exists in Uno.HotTesting.Reactive, so the duplicate
assembly is removed and everything lives there: FeedMock and ListFeedMock are reused,
CommandMock is added, and the swap engine moves into MockingService as strongly typed
helpers. The consumer generator becomes Uno.HotTesting.Reactive.Generator and emits raw
string literals.

The MVUX instrumentation is now emitted by default, with an assembly level opt-out.

(cherry picked from commit fb88e5a)
…ourceContext

The ambient activation state is a mocking concern, not a Core one. MockingService now owns
the AsyncLocal and registers a probe; SourceContext only keeps the per-instance
IsMockingActive bit. In a live application the probe is never registered, so the bit stays
false and the cost is zero.

(cherry picked from commit fa1d928)
Extended the reactive testing reference with the generated tier 2 and 3 layer, and added a
named catalog sample together with its test. The generated factory class is now named
{Vm}Mock and Empty moved onto the record.

(cherry picked from commit 73b05a5)
… Create

Review feedback: the generated {Vm}Mock is now partial so applications can extend it with
named catalogs, SetModel is renamed to SetMock, Create only takes the mock record,
{Model}Mock.Empty stays on the record so it composes with `with`, and Create opens the
MockingService.Enable() scope itself so user code never has to.

Command mocking is deferred to a future version: the consumer generator no longer emits a
command member nor the __Mock_SetCommand wiring.

(cherry picked from commit ba05821)
Applied: combined the nested if in ViewModelGenTool_3.Mocking, projected
ctor.DeclaringSyntaxReferences, SimpleNameSyntax and the accesses with Select, and
filtered the nested types and the references explicitly in FeedsMockGenerator.

Declined, each answered in its thread: the readonly suggestion on Disposable._onDispose
is a false positive (the field is a ref argument of Interlocked.Exchange), the == false
simplification targets a bool? whose three-state semantics are intentional, and the Where
suggestion filters a computed value.

(cherry picked from commit 29714cd)
The repository enables GenerateDocumentationFile together with TreatWarningsAsErrors,
so a public member without an XML comment fails the Release build even though a local
Debug build stays green. Added /// <inheritdoc /> on FeedsMockGenerator.Initialize and
Execute, the same way FeedsGenerator already does.

Also fixed MD032, MD012, MD022 and MD031 on the four spec 013 documents.

(cherry picked from commit 149b987)
Everything pushed to GitHub must be in English. The spec 013 history document was
written in French during the design discussions, which failed the CI spell-checking
validation (333 unknown words). spec.md, architecture.md and the reference doc were
already clean.

- history.md fully translated to English, keeping every SHA, type name, file path and
  decision id unchanged.
- implementation.md: replaced three coined words the dictionary rejects (bikesheddable,
  Authorable, mockability) with plain English equivalents.

Verified with the exact CI commands: cspell over 236 markdown files reports 0 issue, and
markdownlint reports no violation.

(cherry picked from commit 4a3e1b3)
@mergify

mergify Bot commented Sep 4, 2026

Copy link
Copy Markdown
Author

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant