test: consolidate interop contract DSL and unify the spec runner - #290
Merged
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
damyanpetev
force-pushed
the
dpetev/interop-consolidate-update
branch
from
August 5, 2026 07:52
bae2964 to
d428fbb
Compare
damyanpetev
marked this pull request as ready for review
August 5, 2026 07:52
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the Ignite UI for Blazor interop contract testing DSL and runner to reduce duplication and unify the way “values only knowable after render” are expressed and resolved. It primarily affects the unit-test infrastructure and updates contract authoring guidance accordingly.
Changes:
- Introduces
FromRender<T>+FromRender.Of(...)as the single mechanism for late-bound contract values (event payloads, prop wire values, stubs, and expected args). - Consolidates the spec runner by merging method/getter execution paths into a single
RunSpec+ shared assertion flow. - Simplifies/normalizes contract DSL overloads (notably
arrange:/elements:on method specs) and updates existing test contracts + docs to the new pattern.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/IgniteUI.Blazor.Tests/Interop/ComponentContract.cs | Adds FromRender<T> + updates contract spec models and DSL overloads to use it consistently. |
| tests/IgniteUI.Blazor.Tests/ComponentWithContractTestBase.cs | Unifies method/getter runner path; resolves late values via IFromRender. |
| tests/IgniteUI.Blazor.Tests/DropdownTests.cs | Updates dropdown contract specs to use FromRender.Of(...) for render-settled values. |
| tests/IgniteUI.Blazor.Tests/TreeTests.cs | Wraps render-dependent args/stubs with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/TileManagerTests.cs | Wraps render-dependent args/stubs with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/TabsTests.cs | Wraps render-dependent event payload with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/StepperTests.cs | Wraps render-dependent getter stub with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/SelectTests.cs | Wraps render-dependent stubs/payloads with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/ComboTests.cs | Wraps render-dependent stubs/payloads/prop wire values with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/ChatTests.cs | Wraps arranged getter stub with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/CalendarTests.cs | Wraps arranged getter stub with FromRender.Of(...). |
| tests/IgniteUI.Blazor.Tests/AccordionTests.cs | Wraps arranged event payloads with FromRender.Of(...). |
| skills/igniteui-blazor-lite-testing/references/interop-contracts.md | Updates guidance/examples to the unified FromRender<T> mechanism. |
damyanpetev
enabled auto-merge (squash)
August 5, 2026 07:59
MayaKirova
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up on #288 since that required some additions to the contract and it made sense to clean up the impl. some, but moved into separate PR.
Why
Covering
IgbDropdown'sShow/Toggletarget overloads in the previous PR needed an argument that only exists once the component has rendered — a sibling component instance or a capturedElementReference— whose wire form, the target's interop instance id, is assigned at that same moment. Supporting it first looked like it needed a new.Methodoverload, a new generic parameter and its own compile-time drift guard: that would have been the fourth distinct way of expressing "settle this value after the render", in a builder already at 25 overloads whose count was driven by three orthogonal dimensions expressed as overloads ({async-only | twin}×{void | scalar | explicit wire return}×{plain | arranged | hosted}), so they multiplied rather than added.The cause was mundane. Method and getter specs already share one spec type — on this stack a getter is an
invokeMethodwith ap:-prefixed identifier — but the runner had two duplicated paths for them, which is why render arrangement worked for method specs only by accident (the runner honored the field; no.Methodoverload exposed it). This PR removes both causes. Pure test-infrastructure refactor, no behavior change.What changed
FromRender<T>is implicitly convertible fromT, so every spec stating a fixed value is untouched; a late one is built withFromRender.Of((interop, cut) => …). It now types every parameter that could need it — stubbed returns, event payloads, prop wire values, and individual elements of a method's expected arguments — replacingStubFactory,ArgsJsonFactory,ExpectedValueFactoryand the one-off marker type added in the previous PR. The next kind of late value needs no new API.RunMethodSpecandRunGetterSpecmerge intoRunSpec+AssertObserved, ~50 lines of duplication gone. A current-state read now differs from an API call in exactly two places, both named in the code: who owns the wire identifier, and that a read has no wire shape to pin. Capabilities added to one kind of spec now exist for the other automatically.arrangedis no longer an overload dimension.arrange:andelements:are optional parameters on all six real.Methodoverloads instead of one,.Prop's arranged overload folds into its explicit-wire form, and stubs can be late on methods as well as getters.[Obsolete(error: true)]guards that fail the build when a contract's member starts returning a value now mirror the new optional parameters — without that, passingarrange:would quietly make a guard inapplicable and let an unstated return decode slip through. Re-verified by probe.Net API surface: 25 overloads → 24. The value is one concept replacing four and one runner replacing two, not the count. The authoring reference gains a short section on the single mechanism and its stale examples are updated.
Not changed, deliberately
.Getter's arranged and explicit-wire overloads cannot merge: they are distinguished by whetherexpect:is present, and "absent" cannot be spellednullbecause three specs passexpect: null!to assert a null decode — the meaning would invert silently..Eventcannot lose an overload either: its forms are distinguished by assert shape, and giving two of them the same non-assert signature makes an implicitly-typedassert: (cut, args) => …ambiguous at existing call sites. Both want their own review rather than being forced through here.Testing
Full suite on net8.0, net9.0 and net10.0: 903 tests, 881 passing, 22 skipped — identical before and after, which is the whole signal for a refactor of this kind. No new build warnings in the touched files. Call-site churn is mechanical: 41 factory sites across 10 suites gained a
FromRender.Of(...)wrapper. Guards were checked by temporarily declaring a spec whose member pair returns a value and confirming the build fails withCS0619.