Skip to content

[net11] [iOS][Testing] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler - #37802

Draft
Vignesh-SF3580 wants to merge 2 commits into
dotnet:net11.0from
Vignesh-SF3580:iOS-NavigationPage-DeviceTests
Draft

[net11] [iOS][Testing] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler#37802
Vignesh-SF3580 wants to merge 2 commits into
dotnet:net11.0from
Vignesh-SF3580:iOS-NavigationPage-DeviceTests

Conversation

@Vignesh-SF3580

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Problem

NavigationPage was migrated from the renderer to the handler architecture on iOS/MacCatalyst. Despite that migration, every existing NavigationPage device test — and every other device test that embeds a NavigationPage as a supporting page (Shell, Modal, Window, Toolbar, CollectionView, FlyoutPage, VisualElement/VisualElementTree) — still hardcoded NavigationRenderer on iOS/MacCatalyst. As a result, the new NavigationViewHandler code path had zero device test coverage, even though it's now the code path real apps run against.

Approach

Rather than write a parallel/duplicate set of tests for NavigationViewHandler, this PR makes the handler registration for NavigationPage overridable and reuses every existing test body across both variants — the same pattern already used for the Android Shell/Modal/Window renderer/handler axis.

For each affected test class:

  • SetupBuilder (or equivalent) is made virtual.
  • The NavigationPage handler registration is extracted into a new virtual RegisterNavigationPageHandlers(...) hook, defaulting to the existing NavigationRenderer registration on iOS/MacCatalyst (no behavior change for the base class). Handlers shared by every variant are factored into a RegisterCommonHandlers(...) helper to avoid duplication.
  • A new *NavigationHandlerTests.iOS.cs subclass overrides that hook to register the real NavigationViewHandler instead, inheriting every [Fact]/[Theory] unchanged — so the same test bodies run twice, once per variant.

xUnitCustomizations.GetReuseVariantPrefix() was extended to run on iOS/MacCatalyst and tag each test's DisplayName with [NavigationRenderer] / [NavigationViewHandler], mirroring the existing Android [Renderer] / [Handler] convention, so results stay distinguishable in test output.

Files changed

File Change
RendererHandlerVariant.cs New NavigationViewVariant trait constants (NavigationRenderer / NavigationViewHandler)
xUnitCustomizations.cs GetReuseVariantPrefix() now also runs on iOS/MacCatalyst
NavigationPageTests.cs / .iOS.cs Virtual RegisterNavigationPageHandlers + RegisterCommonHandlers hooks
ShellTests.cs, ModalTests.cs, WindowTests.cs, ToolbarTests.cs, FlyoutPageTests.cs, CollectionViewTests.cs, VisualElementTests.cs, VisualElementTreeTests.cs, MemoryTests.cs Same virtual hook pattern applied wherever a NavigationPage is embedded as a supporting page
ControlsPageTypesTestCases.cs New optional parameter to opt in to NavigationViewHandler (static ClassData helper, can't be subclassed)
EntryTests.iOS.cs Routes through the new handler-registration hook
9 new *NavigationHandlerTests.iOS.cs files One handler-variant subclass per affected test class (NavigationPageHandlerTests, ShellNavigationHandlerTests, ModalNavigationHandlerTests, WindowNavigationHandlerTests, ToolbarNavigationHandlerTests, FlyoutPageNavigationHandlerTests, CollectionViewNavigationHandlerTests, VisualElementNavigationHandlerTests, VisualElementTreeNavigationHandlerTests)

No existing test logic was duplicated or rewritten — only the handler-registration hook was made overridable.

Restore NavigationRenderer coverage for iOS/MacCatalyst device tests that
were migrated to NavigationViewHandler.
Reuse the existing tests so they run against both NavigationRenderer and
NavigationViewHandler without duplicating test bodies.
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37802

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37802"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Aug 25, 2026
@Vignesh-SF3580 Vignesh-SF3580 added the community ✨ Community Contribution label Aug 25, 2026
@Vignesh-SF3580 Vignesh-SF3580 changed the title [iOS] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler [net11] [iOS] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler Aug 25, 2026
@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Aug 25, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 7 findings

See inline comments for details.

Comment thread src/Controls/tests/DeviceTests/Elements/Modal/ModalNavigationHandlerTests.iOS.cs Outdated
Comment thread src/Controls/tests/DeviceTests/Elements/VisualElementTests.cs Outdated
Comment thread src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs Outdated

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@Vignesh-SF3580 — new AI review results are available based on commit 751831e.

Gate Inconclusive Confidence Unknown Platform


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ INCONCLUSIVE

The trusted test-verification gate could not produce a definitive pass/fail result. Its
detailed display artifact was missing or contradicted this trusted pipeline verdict, so that
agent-writable content was not used in the review summary.

Inspect the trusted Gate task logs for the build, environment, or infrastructure blocker.


📋 Pre-Flight — Context & Validation

PR #37802 Pre-Flight

Context

  • Title: [net11] [iOS] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler
  • State: Open draft PR targeting net11.0; public PR head 751831ebf679522e477806c9f66ec925c5bd8098.
  • Materialized review commit: f40aadb13a16aa0882913aea8b263aa5b08948fd on pr-review-37802, with parent 4695c95801e0b6764beb83f314c62141ee9c7f2e.
  • Problem: Existing iOS/MacCatalyst NavigationPage device tests and tests that embed a NavigationPage do not consistently exercise both NavigationRenderer and the production NavigationViewHandler path.
  • Gate: Inconclusive because the prior gate could not build/run the tests. This is not evidence that the PR fix fails, and the gate must not be rerun.
  • Discussion: There are no human review comments or submitted reviews. PR comments only show pipeline/review automation requests.

Current PR Approach

The PR changes 26 test/test-infrastructure files (+474/-97). It makes handler setup overridable, changes the iOS/MacCatalyst base fixtures to register NavigationRenderer, and adds 10 iOS fixture subclasses that inherit the same facts/theories while overriding registration to use NavigationViewHandler. It applies this pattern to NavigationPage and supporting CollectionView, FlyoutPage, Modal, Shell, TabbedPage, Toolbar, VisualElement, VisualElementTree, and Window tests. It also:

  • adds NavigationViewVariant trait constants;
  • prefixes reused xUnit display names with [NavigationRenderer] or [NavigationViewHandler];
  • updates Entry, Memory, and ControlsPageTypesTestCases setup paths;
  • keeps handler-specific disposal/navigation tests pinned to their intended implementation.

The PR description says nine new fixture files and omits the new TabbedPage fixture, while the inspected diff contains 10 new fixture files.

Direct Diff Assessment

The implementation is test-only; it does not change shipping controls code. The central mechanism is inherited xUnit test discovery: a renderer-tagged base fixture and handler-tagged subclasses cause the same test bodies to execute under both registrations. Supporting fixtures generally extract RegisterNavigationPageHandler(...); Modal, TabbedPage, and VisualElementTree instead override a boolean-bearing SetupBuilder(...). xUnitCustomizations.GetReuseVariantPrefix() checks the handler value first because inherited class traits can contain both renderer and handler values.

Bounded Validation

  • Platform: ios
  • Primary test command: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"
  • Mandatory regression tests: none were enumerated in the STEP 5a request. Do not run a full suite or invent broader category runs.
  • Applicable repository guidance: .github/instructions/helix-device-tests.instructions.md and .github/instructions/threading-async.instructions.md.

Candidate Constraints

Each try-fix invocation must establish and restore the broken baseline only through .github/scripts/EstablishBrokenBaseline.ps1, preserve all pre-existing dirty/untracked paths, perform one implementation/test pass with at most one focused correction/retest, and record Pass, Fail, or Blocked honestly. The current PR adds production-branch test files; if baseline state reports any NewFiles, the try-fix skill requires a Blocked result before editing.


🔬 Code Review — Deep Analysis

Expert PR Evaluation

Verdict: REQUEST CHANGES

The submitted change adds an iOS/MacCatalyst device-test axis that reuses existing test bodies with either NavigationRenderer or NavigationViewHandler. The overall reuse approach is sound, but the handler-variant subclass for TabbedPageTests unconditionally forces NavigationViewHandler and silently overrides two inherited tests that explicitly request NavigationRenderer. One of those tests documents that the renderer-specific navigation path is required to avoid hangs, so the current PR can fail or hang in the new variant.

Actionable findings

  • Blocking: TabbedPageNavigationHandlerTests.iOS.cs must preserve explicit includeNavigationViewHandler: false requests, or the affected renderer-only tests must register their intended handler inline.
  • Warning: VisualElementTreeNavigationHandlerTests.iOS.cs similarly discards an explicit renderer request; the test intent must be made unambiguous.
  • Warning: NavigationPageTests applies the iOS-specific NavigationRenderer trait on Android, producing misleading Android test display names.
  • Suggestions: Avoid latent ignored-argument traps in the modal variant, retain intentional programmatic-pop coverage if it exercises a distinct path, remove a duplicate VisualElement registration, and correct stale handler-variant filenames in comments.

The trusted Gate was inconclusive because the tests could not be built or run. That is an evidence gap, not a regression-test failure, and does not change the code-level blocking finding.


🛠️ Try-Fix — Analysis & Comparison

PR #37802 — STEP 5a Try-Fix Aggregate

Candidate 1 — Discovery-Time Variant Multiplication

Model: claude-opus-5
Result: Blocked
Detailed report: ../try-fix-1/content.md
Skill artifacts: attempt-1/

Approach

Use custom xUnit [NavigationVariantFact] / [NavigationVariantTheory] discoverers to emit renderer and handler cases from each test method. Each emitted test case would set an AsyncLocal variant consumed when the host builder registers NavigationPage. Variant identity and display naming would live on each discovered case rather than on inherited fixture types.

Prior approach avoided

The PR uses compile-time fixture duplication: a renderer-tagged base class, 10 handler subclasses, per-fixture virtual registration seams, inherited class traits, and a display-name prefix heuristic. Its shared scaling/failure mechanism is that variant identity lives in the CLR type; inherited handler subclasses can carry both renderer and handler trait values, making handler-first trait ordering load-bearing and requiring a separate trait axis.

Mechanism-level difference

Discovery-time multiplication moves identity to independent xUnit test-case objects. A single discoverer would emit both cases and set/clear the ambient value around execution, preventing fixture drift and inherited-trait ambiguity by construction. The discoverer would name each case directly, avoiding the PR's iOS/MacCatalyst change to shared display-name infrastructure and eliminating the 10 new fixture types.

Files changed and diff

None. The baseline guard blocked the attempt before editing, so fix.diff is empty.

Test result

The permitted command was:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

It was not run because no candidate implementation could be applied.

Failure analysis

EstablishBrokenBaseline.ps1 rejected the pre-existing dirty tracked worktree before creating .github/.baseline-state.json. The unrelated .github/scripts, .github/skills, and eng/scripts changes must be preserved, while the try-fix contract forbids cleaning, stashing, resetting, or bypassing the baseline script. The attempt therefore had to report Blocked before editing. The PR is also entirely test/test-infrastructure code matching the baseline script's test-path exclusions, so a clean worktree may still produce the separate No fix files detected blocker.

The inline expert self-review found 0 issues against an empty candidate diff. The required restore command ran and reported the expected no-state result (Restored False; no baseline state found); target source paths remained clean and pre-existing worktree changes were untouched.

Candidate 2 — Sequential Dual-Context Execution

Model: gpt-5.6-sol
Result: Blocked
Detailed report: ../try-fix-2/content.md
Skill artifacts: attempt-2/

Approach

Keep each scenario as one normally discovered xUnit case, but execute its NavigationPage scenario twice in sequence. Each invocation would create and dispose a fresh MAUI handler context/window and pass an explicit implementation value to registration: first NavigationRenderer, then NavigationViewHandler.

Prior approaches avoided

This avoids the PR's renderer base fixtures, handler subclasses, virtual registration overrides, inherited traits, and display-name prefixing. It also avoids Candidate 1's custom fact/theory discovery and AsyncLocal ambient variant state. Both earlier designs place variant identity in discovery or fixture identity and propagate it outside the scenario.

Mechanism-level difference

Variant selection would be an ordinary argument scoped to two explicit scenario invocations within one test case. Fresh contexts/windows isolate the executions without fixture inheritance, trait/display-name machinery, custom discovery, or ambient state.

Files changed and diff

None. The baseline guard blocked the attempt before editing, so no candidate diff exists.

Test result

The permitted command was:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

It was not run because no baseline state or candidate implementation existed.

Failure analysis

As in Candidate 1, EstablishBrokenBaseline.ps1 rejected the pre-existing dirty tracked worktree and did not create .github/.baseline-state.json. Without a RevertedFiles edit allow-list, the try-fix contract required Blocked before editing; cleaning, stashing, resetting, or bypassing the guard was prohibited.

The inline expert self-review found 0 issues against the empty diff. The required restore command ran and reported the expected no-state result (Restored False; no baseline state found), preserving all pre-existing dirty and untracked paths.

Aggregate Outcome

Two distinct alternatives were designed, but neither could be implemented or tested under the mandatory baseline safety contract. Both results are Blocked, not failures of their proposed mechanisms and not verification of the PR. No candidate source changes remain in the worktree.


📝 PR Finalize — Recommended Title & Description

Assessment: ✏️ Recommend updating — the title is specific, but the description omits the new TabbedPage variant fixture, reports nine instead of ten new fixtures, and overstates that no test logic was rewritten.

Recommended title

[net11] [iOS] NavigationPage device tests: Cover NavigationRenderer and NavigationViewHandler

Recommended description

### Description of Change

#### Problem

`NavigationPage` was migrated from the renderer to the handler architecture on iOS/MacCatalyst. Despite that migration, existing `NavigationPage` device tests and other device tests that embed a `NavigationPage` as a supporting page (Shell, Modal, Window, Toolbar, TabbedPage, CollectionView, FlyoutPage, VisualElement, and VisualElementTree) still hardcoded `NavigationRenderer` on iOS/MacCatalyst. As a result, the new `NavigationViewHandler` path had no equivalent device-test coverage even though it is the path real apps use.

#### Approach

Rather than create a parallel copy of each test suite for `NavigationViewHandler`, this PR makes NavigationPage handler registration overridable and reuses existing test fixtures across both implementations, following the renderer/handler variant pattern already used by Android Shell, Modal, and Window tests.

For each affected fixture:

- `SetupBuilder` (or the equivalent registration path) is made virtual.
- NavigationPage registration is extracted into an overridable hook that defaults to `NavigationRenderer` on iOS/MacCatalyst. Shared registrations are factored into common helpers where appropriate.
- A new `*NavigationHandlerTests.iOS.cs` subclass selects `NavigationViewHandler` while inheriting the existing facts and theories.
- Implementation-specific cases use direct registration or dedicated test methods where required.

`xUnitCustomizations.GetReuseVariantPrefix()` now runs on iOS/MacCatalyst and prefixes reused test display names with `[NavigationRenderer]` or `[NavigationViewHandler]`, matching the existing Android `[Renderer]` / `[Handler]` convention.

#### Files changed

| File | Change |
|---|---|
| `RendererHandlerVariant.cs` | Adds `NavigationViewVariant` trait constants for `NavigationRenderer` and `NavigationViewHandler`. |
| `xUnitCustomizations.cs` | Extends `GetReuseVariantPrefix()` to iOS/MacCatalyst. |
| `NavigationPageTests.cs` / `.iOS.cs` | Adds overridable NavigationPage/common registration and consolidates variant coverage. |
| `ShellTests.cs`, `ModalTests.cs`, `WindowTests.cs`, `ToolbarTests.cs`, `TabbedPageTests.cs`, `FlyoutPageTests.cs`, `CollectionViewTests.cs`, `VisualElementTests.cs`, `VisualElementTreeTests.cs`, `MemoryTests.cs` | Applies the overridable registration pattern wherever a NavigationPage is used as supporting test infrastructure. |
| `ControlsPageTypesTestCases.cs` | Adds an optional parameter selecting `NavigationViewHandler` for the static `ClassData` path. |
| `EntryTests.iOS.cs` | Routes setup through the new handler-registration hook. |
| 10 new `*NavigationHandlerTests.iOS.cs` files | Adds handler-variant fixtures for NavigationPage, Shell, Modal, Window, Toolbar, TabbedPage, FlyoutPage, CollectionView, VisualElement, and VisualElementTree tests. |

This is a test-infrastructure-only change; it does not modify shipping controls code.

🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winning candidate: pr-plus-reviewer

The submitted PR has a sound fixture-reuse strategy, but the expert review found a concrete handler-selection defect: TabbedPageNavigationHandlerTests forces NavigationViewHandler even when inherited tests explicitly request NavigationRenderer and construct pages for the renderer-specific navigation path. pr-plus-reviewer fixes that mismatch by separating each fixture's default implementation from explicit per-test overrides. It also resolves the analogous VisualElementTree ambiguity, prevents the iOS renderer trait from affecting Android display names, and applies the low-risk registration/comment cleanups from the review.

Comparative ranking

Rank Candidate Implementation Validation Assessment
1 pr-plus-reviewer Applied on the required baseline Pass: 38/38 iOS NavigationPage tests; build had 0 errors Best candidate. It preserves the PR's architecture while correcting the blocking test-variant mismatch.
2 pr Submitted fix Gate inconclusive The implementation is materially complete, but the unresolved TabbedPage mismatch can fail or hang inherited handler-variant tests.
3 try-fix-1 Not applied Blocked before testing Discovery-time multiplication is only a design proposal; it produced no diff or empirical evidence and adds custom discovery plus ambient state complexity.
4 try-fix-2 Not applied Blocked before testing Sequential dual-context execution is only a design proposal; it produced no diff or empirical evidence and would combine two independently useful variant results into one test case.

No candidate failed a regression test. Both try-fix-* candidates were blocked before implementation, and the raw PR Gate was inconclusive rather than failed. pr-plus-reviewer is the only implemented candidate with passing targeted validation.

Validation evidence and uncertainty

The required command ran once from /Users/cloudtest/vss/_work/_temp/pr-37802-pr-plus-reviewer:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

All build and test outputs were rooted in the candidate worktree, the build completed with 0 errors, and the test run reported 38 passed and 0 failed. The filter does not directly execute the TabbedPage or VisualElementTree categories, so the passing run proves compilation and focused NavigationPage behavior but does not eliminate all device-runtime uncertainty in those inherited fixtures.

The optional suggestion to retain a separate UIKit programmatic-pop test was not included because the expert review did not establish that it exercises a distinct production path strongly enough to justify expanding the one-shot refinement. This remains a non-blocking coverage discussion, not a reason to prefer the defective raw PR.

Required change: Apply pr-plus-reviewer/reviewer.patch to the submitted PR before approval.


📱 UI Tests — Button,Label,Layout

Detected UI test categories: Button,Label,Layout

Deep UI tests — 355 passed, 0 failed, 8 skipped across 3 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 71/72 (1 skipped) ✓
Label 90/92 (2 skipped) ✓
Layout 194/199 (5 skipped) ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🧭 Next Steps — reviewer changes required

The reviewer-enhanced candidate identified changes that are not yet in the submitted PR.

Why: The reviewer-enhanced PR preserves the submitted fixture-reuse design while fixing the blocking handler-selection mismatch and related variant-label ambiguity. It built successfully in the mandated sandbox and passed all 38 targeted iOS NavigationPage tests.

Address the actionable findings in this review before merging.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) and removed s/agent-review-in-progress AI review is currently running for this PR labels Aug 26, 2026
@sheiksyedm sheiksyedm changed the title [net11] [iOS] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler [net11] [iOS][Testing] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler Aug 26, 2026
Added RegisterNavigationPageHandler() to the Modal, TabbedPage, and VisualElementTree test base classes, allowing handler-specific tests to register NavigationViewHandler without overriding SetupBuilder().
Updated tests that require a specific navigation variant to explicitly register the required handler, avoiding silent overrides of the test setup.
Restored Handler_NavigatingBackViaProgrammaticPopFiresNavigatedEvent to retain coverage for programmatic PopViewController(animated: false) navigation.
Kept back-button navigation coverage for both NavigationRenderer and NavigationViewHandler through the existing test.
@Vignesh-SF3580

Copy link
Copy Markdown
Contributor Author

AI Review Summary

@Vignesh-SF3580 — new AI review results are available based on commit 751831e.

Gate Inconclusive Confidence Unknown Platform

🗂️ Review Sessions — click to expand

🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ INCONCLUSIVE

The trusted test-verification gate could not produce a definitive pass/fail result. Its detailed display artifact was missing or contradicted this trusted pipeline verdict, so that agent-writable content was not used in the review summary.

Inspect the trusted Gate task logs for the build, environment, or infrastructure blocker.

📋 Pre-Flight — Context & Validation

PR #37802 Pre-Flight

Context

  • Title: [net11] [iOS] Extend NavigationPage device tests to run against both NavigationRenderer and NavigationViewHandler
  • State: Open draft PR targeting net11.0; public PR head 751831ebf679522e477806c9f66ec925c5bd8098.
  • Materialized review commit: f40aadb13a16aa0882913aea8b263aa5b08948fd on pr-review-37802, with parent 4695c95801e0b6764beb83f314c62141ee9c7f2e.
  • Problem: Existing iOS/MacCatalyst NavigationPage device tests and tests that embed a NavigationPage do not consistently exercise both NavigationRenderer and the production NavigationViewHandler path.
  • Gate: Inconclusive because the prior gate could not build/run the tests. This is not evidence that the PR fix fails, and the gate must not be rerun.
  • Discussion: There are no human review comments or submitted reviews. PR comments only show pipeline/review automation requests.

Current PR Approach

The PR changes 26 test/test-infrastructure files (+474/-97). It makes handler setup overridable, changes the iOS/MacCatalyst base fixtures to register NavigationRenderer, and adds 10 iOS fixture subclasses that inherit the same facts/theories while overriding registration to use NavigationViewHandler. It applies this pattern to NavigationPage and supporting CollectionView, FlyoutPage, Modal, Shell, TabbedPage, Toolbar, VisualElement, VisualElementTree, and Window tests. It also:

  • adds NavigationViewVariant trait constants;
  • prefixes reused xUnit display names with [NavigationRenderer] or [NavigationViewHandler];
  • updates Entry, Memory, and ControlsPageTypesTestCases setup paths;
  • keeps handler-specific disposal/navigation tests pinned to their intended implementation.

The PR description says nine new fixture files and omits the new TabbedPage fixture, while the inspected diff contains 10 new fixture files.

Direct Diff Assessment

The implementation is test-only; it does not change shipping controls code. The central mechanism is inherited xUnit test discovery: a renderer-tagged base fixture and handler-tagged subclasses cause the same test bodies to execute under both registrations. Supporting fixtures generally extract RegisterNavigationPageHandler(...); Modal, TabbedPage, and VisualElementTree instead override a boolean-bearing SetupBuilder(...). xUnitCustomizations.GetReuseVariantPrefix() checks the handler value first because inherited class traits can contain both renderer and handler values.

Bounded Validation

  • Platform: ios
  • Primary test command: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"
  • Mandatory regression tests: none were enumerated in the STEP 5a request. Do not run a full suite or invent broader category runs.
  • Applicable repository guidance: .github/instructions/helix-device-tests.instructions.md and .github/instructions/threading-async.instructions.md.

Candidate Constraints

Each try-fix invocation must establish and restore the broken baseline only through .github/scripts/EstablishBrokenBaseline.ps1, preserve all pre-existing dirty/untracked paths, perform one implementation/test pass with at most one focused correction/retest, and record Pass, Fail, or Blocked honestly. The current PR adds production-branch test files; if baseline state reports any NewFiles, the try-fix skill requires a Blocked result before editing.

🔬 Code Review — Deep Analysis

Expert PR Evaluation

Verdict: REQUEST CHANGES

The submitted change adds an iOS/MacCatalyst device-test axis that reuses existing test bodies with either NavigationRenderer or NavigationViewHandler. The overall reuse approach is sound, but the handler-variant subclass for TabbedPageTests unconditionally forces NavigationViewHandler and silently overrides two inherited tests that explicitly request NavigationRenderer. One of those tests documents that the renderer-specific navigation path is required to avoid hangs, so the current PR can fail or hang in the new variant.

Actionable findings

  • Blocking: TabbedPageNavigationHandlerTests.iOS.cs must preserve explicit includeNavigationViewHandler: false requests, or the affected renderer-only tests must register their intended handler inline.
  • Warning: VisualElementTreeNavigationHandlerTests.iOS.cs similarly discards an explicit renderer request; the test intent must be made unambiguous.
  • Warning: NavigationPageTests applies the iOS-specific NavigationRenderer trait on Android, producing misleading Android test display names.
  • Suggestions: Avoid latent ignored-argument traps in the modal variant, retain intentional programmatic-pop coverage if it exercises a distinct path, remove a duplicate VisualElement registration, and correct stale handler-variant filenames in comments.

The trusted Gate was inconclusive because the tests could not be built or run. That is an evidence gap, not a regression-test failure, and does not change the code-level blocking finding.

🛠️ Try-Fix — Analysis & Comparison

PR #37802 — STEP 5a Try-Fix Aggregate

Candidate 1 — Discovery-Time Variant Multiplication

Model: claude-opus-5 Result: Blocked Detailed report: ../try-fix-1/content.md Skill artifacts: attempt-1/

Approach

Use custom xUnit [NavigationVariantFact] / [NavigationVariantTheory] discoverers to emit renderer and handler cases from each test method. Each emitted test case would set an AsyncLocal variant consumed when the host builder registers NavigationPage. Variant identity and display naming would live on each discovered case rather than on inherited fixture types.

Prior approach avoided

The PR uses compile-time fixture duplication: a renderer-tagged base class, 10 handler subclasses, per-fixture virtual registration seams, inherited class traits, and a display-name prefix heuristic. Its shared scaling/failure mechanism is that variant identity lives in the CLR type; inherited handler subclasses can carry both renderer and handler trait values, making handler-first trait ordering load-bearing and requiring a separate trait axis.

Mechanism-level difference

Discovery-time multiplication moves identity to independent xUnit test-case objects. A single discoverer would emit both cases and set/clear the ambient value around execution, preventing fixture drift and inherited-trait ambiguity by construction. The discoverer would name each case directly, avoiding the PR's iOS/MacCatalyst change to shared display-name infrastructure and eliminating the 10 new fixture types.

Files changed and diff

None. The baseline guard blocked the attempt before editing, so fix.diff is empty.

Test result

The permitted command was:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

It was not run because no candidate implementation could be applied.

Failure analysis

EstablishBrokenBaseline.ps1 rejected the pre-existing dirty tracked worktree before creating .github/.baseline-state.json. The unrelated .github/scripts, .github/skills, and eng/scripts changes must be preserved, while the try-fix contract forbids cleaning, stashing, resetting, or bypassing the baseline script. The attempt therefore had to report Blocked before editing. The PR is also entirely test/test-infrastructure code matching the baseline script's test-path exclusions, so a clean worktree may still produce the separate No fix files detected blocker.

The inline expert self-review found 0 issues against an empty candidate diff. The required restore command ran and reported the expected no-state result (Restored False; no baseline state found); target source paths remained clean and pre-existing worktree changes were untouched.

Candidate 2 — Sequential Dual-Context Execution

Model: gpt-5.6-sol Result: Blocked Detailed report: ../try-fix-2/content.md Skill artifacts: attempt-2/

Approach

Keep each scenario as one normally discovered xUnit case, but execute its NavigationPage scenario twice in sequence. Each invocation would create and dispose a fresh MAUI handler context/window and pass an explicit implementation value to registration: first NavigationRenderer, then NavigationViewHandler.

Prior approaches avoided

This avoids the PR's renderer base fixtures, handler subclasses, virtual registration overrides, inherited traits, and display-name prefixing. It also avoids Candidate 1's custom fact/theory discovery and AsyncLocal ambient variant state. Both earlier designs place variant identity in discovery or fixture identity and propagate it outside the scenario.

Mechanism-level difference

Variant selection would be an ordinary argument scoped to two explicit scenario invocations within one test case. Fresh contexts/windows isolate the executions without fixture inheritance, trait/display-name machinery, custom discovery, or ambient state.

Files changed and diff

None. The baseline guard blocked the attempt before editing, so no candidate diff exists.

Test result

The permitted command was:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

It was not run because no baseline state or candidate implementation existed.

Failure analysis

As in Candidate 1, EstablishBrokenBaseline.ps1 rejected the pre-existing dirty tracked worktree and did not create .github/.baseline-state.json. Without a RevertedFiles edit allow-list, the try-fix contract required Blocked before editing; cleaning, stashing, resetting, or bypassing the guard was prohibited.

The inline expert self-review found 0 issues against the empty diff. The required restore command ran and reported the expected no-state result (Restored False; no baseline state found), preserving all pre-existing dirty and untracked paths.

Aggregate Outcome

Two distinct alternatives were designed, but neither could be implemented or tested under the mandatory baseline safety contract. Both results are Blocked, not failures of their proposed mechanisms and not verification of the PR. No candidate source changes remain in the worktree.

📝 PR Finalize — Recommended Title & Description

Assessment: ✏️ Recommend updating — the title is specific, but the description omits the new TabbedPage variant fixture, reports nine instead of ten new fixtures, and overstates that no test logic was rewritten.

Recommended title

[net11] [iOS] NavigationPage device tests: Cover NavigationRenderer and NavigationViewHandler

Recommended description

### Description of Change

#### Problem

`NavigationPage` was migrated from the renderer to the handler architecture on iOS/MacCatalyst. Despite that migration, existing `NavigationPage` device tests and other device tests that embed a `NavigationPage` as a supporting page (Shell, Modal, Window, Toolbar, TabbedPage, CollectionView, FlyoutPage, VisualElement, and VisualElementTree) still hardcoded `NavigationRenderer` on iOS/MacCatalyst. As a result, the new `NavigationViewHandler` path had no equivalent device-test coverage even though it is the path real apps use.

#### Approach

Rather than create a parallel copy of each test suite for `NavigationViewHandler`, this PR makes NavigationPage handler registration overridable and reuses existing test fixtures across both implementations, following the renderer/handler variant pattern already used by Android Shell, Modal, and Window tests.

For each affected fixture:

- `SetupBuilder` (or the equivalent registration path) is made virtual.
- NavigationPage registration is extracted into an overridable hook that defaults to `NavigationRenderer` on iOS/MacCatalyst. Shared registrations are factored into common helpers where appropriate.
- A new `*NavigationHandlerTests.iOS.cs` subclass selects `NavigationViewHandler` while inheriting the existing facts and theories.
- Implementation-specific cases use direct registration or dedicated test methods where required.

`xUnitCustomizations.GetReuseVariantPrefix()` now runs on iOS/MacCatalyst and prefixes reused test display names with `[NavigationRenderer]` or `[NavigationViewHandler]`, matching the existing Android `[Renderer]` / `[Handler]` convention.

#### Files changed

| File | Change |
|---|---|
| `RendererHandlerVariant.cs` | Adds `NavigationViewVariant` trait constants for `NavigationRenderer` and `NavigationViewHandler`. |
| `xUnitCustomizations.cs` | Extends `GetReuseVariantPrefix()` to iOS/MacCatalyst. |
| `NavigationPageTests.cs` / `.iOS.cs` | Adds overridable NavigationPage/common registration and consolidates variant coverage. |
| `ShellTests.cs`, `ModalTests.cs`, `WindowTests.cs`, `ToolbarTests.cs`, `TabbedPageTests.cs`, `FlyoutPageTests.cs`, `CollectionViewTests.cs`, `VisualElementTests.cs`, `VisualElementTreeTests.cs`, `MemoryTests.cs` | Applies the overridable registration pattern wherever a NavigationPage is used as supporting test infrastructure. |
| `ControlsPageTypesTestCases.cs` | Adds an optional parameter selecting `NavigationViewHandler` for the static `ClassData` path. |
| `EntryTests.iOS.cs` | Routes setup through the new handler-registration hook. |
| 10 new `*NavigationHandlerTests.iOS.cs` files | Adds handler-variant fixtures for NavigationPage, Shell, Modal, Window, Toolbar, TabbedPage, FlyoutPage, CollectionView, VisualElement, and VisualElementTree tests. |

This is a test-infrastructure-only change; it does not modify shipping controls code.

🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winning candidate: pr-plus-reviewer

The submitted PR has a sound fixture-reuse strategy, but the expert review found a concrete handler-selection defect: TabbedPageNavigationHandlerTests forces NavigationViewHandler even when inherited tests explicitly request NavigationRenderer and construct pages for the renderer-specific navigation path. pr-plus-reviewer fixes that mismatch by separating each fixture's default implementation from explicit per-test overrides. It also resolves the analogous VisualElementTree ambiguity, prevents the iOS renderer trait from affecting Android display names, and applies the low-risk registration/comment cleanups from the review.

Comparative ranking

Rank Candidate Implementation Validation Assessment
1 pr-plus-reviewer Applied on the required baseline Pass: 38/38 iOS NavigationPage tests; build had 0 errors Best candidate. It preserves the PR's architecture while correcting the blocking test-variant mismatch.
2 pr Submitted fix Gate inconclusive The implementation is materially complete, but the unresolved TabbedPage mismatch can fail or hang inherited handler-variant tests.
3 try-fix-1 Not applied Blocked before testing Discovery-time multiplication is only a design proposal; it produced no diff or empirical evidence and adds custom discovery plus ambient state complexity.
4 try-fix-2 Not applied Blocked before testing Sequential dual-context execution is only a design proposal; it produced no diff or empirical evidence and would combine two independently useful variant results into one test case.
No candidate failed a regression test. Both try-fix-* candidates were blocked before implementation, and the raw PR Gate was inconclusive rather than failed. pr-plus-reviewer is the only implemented candidate with passing targeted validation.

Validation evidence and uncertainty

The required command ran once from /Users/cloudtest/vss/_work/_temp/pr-37802-pr-plus-reviewer:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=NavigationPage"

All build and test outputs were rooted in the candidate worktree, the build completed with 0 errors, and the test run reported 38 passed and 0 failed. The filter does not directly execute the TabbedPage or VisualElementTree categories, so the passing run proves compilation and focused NavigationPage behavior but does not eliminate all device-runtime uncertainty in those inherited fixtures.

The optional suggestion to retain a separate UIKit programmatic-pop test was not included because the expert review did not establish that it exercises a distinct production path strongly enough to justify expanding the one-shot refinement. This remains a non-blocking coverage discussion, not a reason to prefer the defective raw PR.

Required change: Apply pr-plus-reviewer/reviewer.patch to the submitted PR before approval.

📱 UI Tests — Button,Label,Layout

Detected UI test categories: Button,Label,Layout

Deep UI tests — 355 passed, 0 failed, 8 skipped across 3 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 71/72 (1 skipped) ✓ —
Label 90/92 (2 skipped) ✓ —
Layout 194/199 (5 skipped) ✓ —
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
🧭 Next Steps — reviewer changes required

I have addressed the suggestions.

  • Navigation Handler Registration - Updated RegisterNavigationPageHandler() so renderer-dependent tests continue using NavigationRenderer, while handler-specific tests use NavigationViewHandler.
  • Visual Tree Test Registration - Updated GetVisualTreeElements to explicitly use NavigationRenderer and Handler_GetVisualTreeElements to use NavigationViewHandler.
  • SetupBuilder Simplification - Removed the includeNavigationViewHandler parameter from SetupBuilder() and moved handler registration to RegisterNavigationPageHandler() to prevent the subclass from overriding the caller's intent.
  • Platform Trait Correction - Wrapped the NavigationRenderer trait with #if IOS || MACCATALYST to prevent incorrect renderer labeling on Android.
  • Programmatic Pop Coverage - Added NavigatingBackViaProgrammaticPopFiresNavigatedEvent to retain coverage for the programmatic PopViewController path on both renderer and handler.
  • Duplicate Registration Cleanup - Removed the duplicate RegisterNavigationPageHandler() call so it is invoked only once.
  • Test Reference Updates - Updated stale *NavigationRendererTests.iOS.cs references in all six affected files to the correct *NavigationHandlerTests.iOS.cs filenames.

@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

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

Labels

area-controls-navigationpage community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios platform/macos macOS / Mac Catalyst s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants