[Android] [ShellHandler] Extend existing device tests to cover both Renderer and Handler code paths - #36328
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36328Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36328" |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR extends Android device test coverage for Shell/Modal/Window by reusing the existing renderer-based test bodies and adding handler-based subclasses, and it adjusts Android tab menu setup to preserve BottomNavigationView menu item identity for the handler implementation.
Changes:
- Introduces a shared
Varianttrait (Renderer/Handler) and updates Android xUnit display names to prefix[Renderer]/[Handler]to distinguish reused test bodies. - Refactors Shell/Modal/Window device tests to allow reuse via virtual setup/helpers and adds Android handler-based subclasses that register the native handlers.
- Updates
TabbedViewManager.SetupBottomNavigationView()to useBottomNavigationViewUtils.SetupMenu()(incremental updates) instead of recreating menu items viamenu.Clear().
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/TestUtils/src/DeviceTests/xUnitCustomizations.cs | Prefixes Android test display names with a variant label based on a Variant trait. |
| src/Controls/tests/DeviceTests/Elements/Window/WindowTests.cs | Tags renderer variant and makes builder setup overridable for handler subclasses. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.cs | Tags renderer variant, makes setup overridable, and updates tests to use the non-generic window helper for reuse. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs | Refactors Android helpers to operate on IShellContext so tests can run against renderer or handler. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellTabBarTests.cs | Makes shared runner virtual and reuses non-generic handler/window path for subclassing. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellTabBarTests.Android.cs | Updates Android tab lookup to use IShellContext instead of renderer-specific types. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellHandlerSubclasses.Android.cs | Adds Android handler-based subclasses that register native Shell/Tabbed/etc handlers and reuse existing test bodies. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.iOS.cs | Adjusts to new shared callback signature and accesses handler via shell.Handler. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.cs | Refactors shared flyout tests to a Func<Shell, Task> callback to enable Android handler subclass reuse. |
| src/Controls/tests/DeviceTests/Elements/Shell/ShellFlyoutTests.Android.cs | Updates Android flyout test to use IShellContext from shell.Handler. |
| src/Controls/tests/DeviceTests/Elements/Shell/RendererHandlerVariant.cs | Defines shared Variant trait constants used by device tests and Android display name prefixing. |
| src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs | Tags renderer variant and makes builder setup overridable for handler subclasses. |
| src/Controls/tests/DeviceTests/ControlsHandlerTestBase.Android.cs | Makes IsBackButtonVisible virtual so handler variants can override toolbar traversal. |
| src/Controls/src/Core/Platform/Android/TabbedViewManager.cs | Switches bottom navigation menu setup to incremental updates via BottomNavigationViewUtils.SetupMenu(). |
084ce87 to
d49cb2e
Compare
| catch (Exception ex) | ||
| { |
@kubaflo I have resolved the conflicts and reviewed the AI suggestions. Both suggestions appear to be invalid, so no additional changes were required. |
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There are no major or valid concerns raised. |
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@Vignesh-SF3580 — new AI review results are available based on this last commit:
1643845.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate: Test Verification (Failure-Only Mode)
Result:
This is a test-only change (no fix files detected in the diff), so the gate only verifies that the new/changed tests fail against the merge base — proving they reproduce the bug they target.
Platform: ANDROID
Merge base: f3a08b3a
| Test | Type | Outcome |
|---|---|---|
ModalTests |
DeviceTest | 🛠️ BUILD ERROR |
ShellTests (ReusedBottomTabReappliesEnabledState, MoreOverflowItemIsReusedNotRecreated) |
DeviceTest | 🛠️ BUILD ERROR |
Diagnostics
- ModalTests:
Build failed: /home/vsts/work/1/s/.dotnet/sdk/11.0.100-preview.7.26365.101/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.ImportWorkloads.targets(38,5): error NETSDK1147: To build this project, the following work... - ShellTests (ReusedBottomTabReappliesEnabledState, MoreOverflowItemIsReusedNotRecreated):
Build failed: /home/vsts/work/1/s/.dotnet/sdk/11.0.100-preview.7.26365.101/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.ImportWorkloads.targets(38,5): error NETSDK1147: To build this project, the following work...
📋 Pre-Flight — Context & Validation
Issue: N/A - No linked issue found in the public PR metadata
PR: #36328 - [Android] [ShellHandler] Extend existing device tests to cover both Renderer and Handler code paths
Platforms Affected: Android
Files Changed: 2 implementation, 13 test
Key Findings
- PR #36328 adds Android Shell renderer/handler variant device-test coverage and changes Android bottom-navigation setup to reuse native menu items instead of clearing/recreating them.
- The production
BottomNavigationViewUtils.SetupMenu()reuse path appears to address prior menu-item state/icon concerns, including enabled state, checked state, icon tint, null icon clearing, and stale async icon loads. - Pre-flight code review found unresolved test/build blockers: unused
shellContextlocals, inherited native handler flyout tests still using renderer-oriented toolbar lookup, and an overflow reuse test that does not wait on native menu mutation. - Required CI status could not be queried because
ghis unauthenticated in this environment; the provided gate result is inconclusive and was not re-run.
Code Review Summary
Verdict: NEEDS_CHANGES
Confidence: low
Errors: 2 | Warnings: 1 | Suggestions: 0
Key code review findings:
- ✗
ShellTests.Android.cs:72,109,224,316,633contain unusedshellContextlocals that can fail warnings-as-errors builds. - ✗
ShellTests.Android.cs:693callsGetPlatformToolbar((IPlatformViewHandler)shellContext)fromOpenFlyout(), which does not traverse the native AndroidShellHandlertoolbar hierarchy used by handler-variant tests. - ⚠
ShellTests.Android.cs:533waits on managedItems.Count, which changes synchronously and does not prove the nativeBottomNavigationViewprocessed the overflow update.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #36328 | Route TabbedViewManager bottom navigation setup through BottomNavigationViewUtils.SetupMenu() to reuse IMenuItems and preserve state/icon updates. |
BottomNavigationViewUtils.cs, TabbedViewManager.cs, Android Shell device tests |
Original PR fix; gate already inconclusive and not re-run. |
🔬 Code Review — Deep Analysis
Code Review — PR #36328
Independent Assessment
What this changes: Adds Android device-test coverage for Shell renderer/handler variants, refactors Shell tests to use IShellContext, and changes Android bottom-navigation menu setup to reuse IMenuItems while preserving icon/state updates.
Inferred motivation: Exercise the newer Android Shell handler path and fix menu-item identity/state issues exposed by that coverage.
Reconciliation with PR Narrative
Author claims: Adds renderer/handler variant test coverage and fixes TabbedViewManager bottom navigation updates by reusing menu items.
Agreement/disagreement: Matches the code. I found unresolved test/build issues in the added/refactored handler coverage.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
| Reused menu items did not reapply enabled/selection/tint or guard async icon races | MauiBot inline reviews | ✅ Fixed | BottomNavigationViewUtils.cs:129-135, 162-165, 172-184, 197-234; TabbedViewManager.cs:790-797, 822-830 |
Unused shellContext locals cause warnings-as-errors build failures |
MauiBot inline reviews | ❌ Unresolved | Still present at ShellTests.Android.cs:72, 109, 224, 316, 633; repo has TreatWarningsAsErrors=true in Directory.Build.props:6 |
| Native Shell handler flyout tests still use toolbar lookup that lacks native handler traversal | MauiBot inline review | ❌ Unresolved | OpenFlyout still calls GetPlatformToolbar((IPlatformViewHandler)shellContext) at ShellTests.Android.cs:693; native traversal only exists in ShellHandlerSubclasses.Android.cs:64-93 |
ConditionalWeakTable null icon source concern |
MauiBot inline review | 🔄 Obsolete / not reproduced | Current null branch clears icon at BottomNavigationViewUtils.cs:206-210; AddOrUpdate only rejects null keys |
Blast Radius Assessment
- Runs for all instances: Yes, Android Shell/TabbedView bottom-tab setup uses the changed production path.
- Startup impact: No process startup impact; affects Shell/tab initialization and updates.
- Static/shared state: Yes, weak-table state tracks pending icon sources per
IMenuItem.
CI Status
- Required-check result: undetermined
- Classification: tool unavailable —
gh pr checks 36328 --repo dotnet/maui --requiredfailed becauseghis unauthenticated. - Action taken: confidence capped low; no GitHub comments posted.
Findings
❌ Error — Unused locals will fail warnings-as-errors builds
src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs:72, 109, 224, 316, and 633 still declare var shellContext = (IShellContext)shell.Handler; without using it. With repo-wide TreatWarningsAsErrors=true, CS0219 breaks the device-test build. Remove the unused declarations or use them.
❌ Error — Handler flyout tests still use the renderer toolbar lookup
ShellTests.Android.cs:693 still finds the hamburger via GetPlatformToolbar((IPlatformViewHandler)shellContext). For NativeShellHandler, the PR itself adds separate toolbar traversal in ShellHandlerSubclasses.Android.cs:64-93, but only uses it for IsBackButtonVisible. Inherited handler flyout tests that call OpenFlyout can still throw Unable to find Drawer Button.
⚠️ Warning — Overflow reuse test does not prove the native menu updated
ShellTests.Android.cs:533 waits for shell.CurrentItem.Items.Count == 6, which is true immediately after RemoveAt(6). The subsequent menu assertions were already true before the change, so a handler that never processes the overflow update could still pass. Wait on a native-menu change that only occurs after the update.
Failure-Mode Probing
- Null icon source: current code clears the reused item icon before returning; looks safe.
- Stale async icon load: guarded by per-item pending source checks when using
SetMenuItemIcon. - Native handler flyout open: still fails through base
OpenFlyouttoolbar lookup. - Build with warnings-as-errors: unresolved unused locals produce compile failures.
Verdict: NEEDS_CHANGES
Confidence: low
Summary: The production bottom-navigation approach looks mostly addressed, but the PR still contains concrete test/build blockers. CI status could not be verified due unavailable gh auth, further capping confidence.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Add virtual Android Shell test helpers (GetFlyoutToolbar/GetDrawerButton), override native handler toolbar lookup in the handler subclass, remove unused locals, and strengthen overflow wait by mutating a visible title. |
2 files | Environment blocked before candidate compilation; addresses pre-flight test-helper blockers with narrow blast radius. | |
| 2 | try-fix-2 | Centralize native ShellHandler toolbar lookup in ControlsHandlerTestBase.Android.cs:GetPlatformToolbar, remove unused locals, and strengthen overflow wait by visible slot retitle/reuse. |
2 files | Environment blocked before candidate compilation; broader test-helper blast radius but avoids new virtual hooks. | |
| 3 | try-fix-3 | Replace PR's BottomNavigationViewUtils.SetupMenu() call with local TabbedViewManager slot reconciliation and per-item icon-load version guards. |
1 file | Environment blocked before candidate compilation; meaningful production-code alternative but riskier because it duplicates menu reconciliation logic. | |
| PR | PR #36328 | Use shared BottomNavigationViewUtils.SetupMenu() from TabbedViewManager for incremental bottom-navigation item reuse. |
2 implementation files + tests | Existing PR fix; not considered passing because gate was inconclusive. |
Cross-Pollination
| Model/Reviewer | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 1 | Yes | try-fix-1: test-helper virtual hooks and subclass override. |
| maui-expert-reviewer | 2 | Yes | try-fix-2: central shared Android toolbar helper strategy. |
| maui-expert-reviewer | 3 | Yes | try-fix-3: production-code slot reconciliation alternative. |
| maui-expert-reviewer | Exhaustion | No | Remaining variants are refactorings of the same test-helper or bottom-nav reconciliation approaches. |
Test / Environment Summary
All candidate test commands were blocked before executing Android tests or compiling the candidate changes:
- Initial
net11.0-androidbuild failed withNETSDK1242because Mono runtime Android projects are not supported in .NET 11+ without the required CoreCLR targeting configuration. - The allowed
dotnet workload restore src/Controls/tests/DeviceTests/Controls.DeviceTests.csprojattempt succeeded and installed the Android workload. net10.0-androidtargeted builds then failed atsrc/Maui.InTree.targetsbecause required MAUI MSBuild tasks were not built.- The prerequisite
dotnet build Microsoft.Maui.BuildTasks.slnf --no-restoreattempt failed due missing assets, unsupportednet11.0-androidMono runtime targeting, and missing .NET Framework 4.7.2 reference assemblies on Linux.
Per-candidate details are in:
CustomAgentLogsTmp/PRState/36328/PRAgent/try-fix-1/content.mdCustomAgentLogsTmp/PRState/36328/PRAgent/try-fix-2/content.mdCustomAgentLogsTmp/PRState/36328/PRAgent/try-fix-3/content.md
Exhausted: Yes
Selected Fix: No candidate selected — none passed validation. On code merits, try-fix-1 and try-fix-2 directly address the unresolved test/build blockers; try-fix-3 is a meaningful but higher-risk production alternative. The PR's existing shared SetupMenu() production fix remains simpler than try-fix-3, but the PR still needs the test-helper/build issues from pre-flight resolved before it can be considered better validated.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the current description is strong for the raw PR, but the selected winning candidate adds test-helper/build reliability fixes that are not described.
Recommended title
[Android] ShellHandler: Cover handler device-test paths and reuse bottom-tab menu items
Recommended description
### Summary
Adds Android device test coverage for the Handler-based Shell, Modal, and Window implementations alongside the existing Renderer-based tests, allowing both Android implementations to run in CI. Also fixes a bottom navigation menu update issue in TabbedViewManager.cs that was uncovered while adding this coverage.
### Changes
- Added RendererHandlerVariant.cs to define shared [Trait] constants (AndroidShellRenderer and AndroidShellHandler) for identifying Renderer and Handler test variants.
- Updated xUnitCustomizations.cs to read the Variant trait (Android only) and prefix test display names with [Renderer] or [Handler], making it easy to identify the implementation that failed in CI.
- Updated ShellTests, ModalTests, WindowTests, ShellFlyoutTests, ShellTabBarTests, and their Android/iOS partials to make the required setup methods virtual and tag the existing tests with the Renderer trait. This allows the same test implementations to be reused by the Handler subclasses instead of duplicating them.
- Added ShellHandlerSubclasses.Android.cs, which contains ShellHandlerTests_Shell, ModalHandlerTests, and WindowHandlerTests. These subclasses reuse the existing test implementations while registering the Handler implementation on Android and are tagged with the Handler trait.
- Removed unused Android Shell test locals that can fail warnings-as-errors builds.
- Added overridable Android flyout toolbar/drawer-button lookup helpers so inherited Shell handler flyout tests can locate the native ShellHandler toolbar hierarchy.
- Strengthened the bottom-tab overflow reuse test so it waits for an observable native BottomNavigationView menu update instead of the synchronously updated managed Items.Count.
### Source Fix – TabbedViewManager.cs
**Issue:**
SetupBottomNavigationView() called menu.Clear() during tab updates, recreating IMenuItem instances. The Handler-based tests validate menu item identity, which caused the tests to fail.
**Fix:**
Updated the menu setup logic to use BottomNavigationViewUtils.SetupMenu(), which updates existing menu items instead of recreating them.
### Test Fix – PushingNavigationPageModallyWithShellShowsToolbarCorrectly
**Issue:**
The existing GetPlatformToolbar() helper supported ShellRenderer but not ShellHandler. In the Handler implementation, the toolbar is hosted within an outer CoordinatorLayout, so the existing lookup logic did not work.
**Fix:**
Added an IsBackButtonVisible helper and a GetShellHandlerToolbar() helper to locate the toolbar through the nested CoordinatorLayout hierarchy.
### Test Fix – ShellHandler flyout lookup
**Issue:**
Inherited Android flyout tests still opened the flyout through the renderer-oriented toolbar lookup path, which does not traverse the native ShellHandler toolbar hierarchy.
**Fix:**
Added overridable GetFlyoutToolbar() and GetDrawerButton() helpers and overrode the ShellHandler variant to use the native ShellHandler toolbar traversal.
🏁 Report — Final Recommendation
Comparative Report — PR #36328
Candidate ranking
| Rank | Candidate | Regression/gate result | Assessment |
|---|---|---|---|
| 1 | try-fix-1 |
Best code-merit candidate. It preserves the PR's shared production BottomNavigationViewUtils.SetupMenu() approach and narrowly fixes the device-test/build blockers: removes unused locals, routes flyout drawer lookup through overridable helpers with a native ShellHandler override, and strengthens the overflow reuse wait. |
|
| 2 | try-fix-2 |
Also addresses the unused locals and overflow wait, and centralizes native ShellHandler toolbar lookup in ControlsHandlerTestBase.Android.cs. Slightly lower than try-fix-1 because the helper-level change has broader blast radius across Android device tests. |
|
| 3 | pr-plus-reviewer |
Improves the raw PR by applying the expert reviewer's overflow-test reliability feedback, but leaves the pre-flight unused-local build issue and inherited handler flyout toolbar lookup issue unresolved. | |
| 4 | pr |
Production fix is directionally sound and simpler than try-fix-3, but the raw PR still has the expert overflow wait warning plus the pre-flight test/build blockers. |
|
| 5 | try-fix-3 |
Meaningful production-code alternative, but riskier: it duplicates bottom-navigation reconciliation in TabbedViewManager instead of reusing the established shared utility path and does not address the test-helper/build blockers. |
No candidate has a passing regression/gate result. The gate and all try-fix validations were blocked or inconclusive due environment/build prerequisites, so none are treated as failed regression tests. Because no candidate passed validation, the winner is selected on code correctness, blast radius, and how completely it addresses known review findings.
Winner
Selected candidate: try-fix-1
try-fix-1 is the strongest candidate because it keeps the PR's production fix intact while adding the smallest targeted test-side corrections needed for the new Android handler coverage to be reliable. Compared with pr-plus-reviewer, it includes the same native-menu wait strengthening and also resolves the two pre-flight blockers that the expert reviewer did not include in its final inline JSON. Compared with try-fix-2, it avoids changing shared Android test helper behavior for unrelated callers.
Notes on PR metadata
Because the selected winner is not the raw PR fix, the PR description should be updated if this candidate is applied. The current description accurately describes the raw PR's production fix and added handler coverage, but it does not mention the additional try-fix-1 refinements: removing unused shellContext locals, adding overridable flyout toolbar/drawer-button lookup for native ShellHandler, and strengthening the overflow reuse test wait.
🧭 Next Steps — alternative fix proposed (try-fix-1)
Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-1 as the strongest fix.
Why: try-fix-1 wins because it preserves the PR production fix while narrowly addressing the known Android handler test/build blockers and the expert reviewer overflow-wait feedback. No candidate passed validation; all results were blocked or inconclusive, so this ranking is based on code merits and blast radius.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (try-fix-1)
diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellHandlerSubclasses.Android.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellHandlerSubclasses.Android.cs
index 2c91281c94..41db963c8e 100644
--- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellHandlerSubclasses.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellHandlerSubclasses.Android.cs
@@ -61,7 +61,15 @@ namespace Microsoft.Maui.DeviceTests
return false;
}
- MaterialToolbar GetShellHandlerToolbar(IElementHandler handler)
+ protected override MaterialToolbar GetFlyoutToolbar(IShellContext shellContext)
+ {
+ if (shellContext is NativeShellHandler nativeShell)
+ return GetShellHandlerToolbar(nativeShell);
+
+ return base.GetFlyoutToolbar(shellContext);
+ }
+
+ protected MaterialToolbar GetShellHandlerToolbar(IElementHandler handler)
{
// Direct NativeShellHandler: toolbar lives in nested CoordinatorLayout of shell.CurrentPage.
if (handler is NativeShellHandler nativeShell)
diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs
index 86e7a4c4d8..2fd97d52f8 100644
--- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs
@@ -69,7 +69,6 @@ namespace Microsoft.Maui.DeviceTests
await CreateHandlerAndAddToWindow(shell, async () =>
{
- var shellContext = (IShellContext)shell.Handler;
await OnLoadedAsync(page1);
await shell.GoToAsync("//Item2");
await shell.GoToAsync("..");
@@ -106,7 +105,6 @@ namespace Microsoft.Maui.DeviceTests
await CreateHandlerAndAddToWindow(shell, async () =>
{
- var shellContext = (IShellContext)shell.Handler;
await Task.Delay(100);
var platformToolbar = GetPlatformToolbar((IPlatformViewHandler)shell.Handler);
@@ -221,7 +219,6 @@ namespace Microsoft.Maui.DeviceTests
await CreateHandlerAndAddToWindow(shell, async () =>
{
- var shellContext = (IShellContext)shell.Handler;
await OnFrameSetToNotEmpty(footer);
Assert.True(Math.Abs(20 - footer.Frame.Width) < 1);
Assert.True(footer.Frame.Height > 0);
@@ -313,7 +310,6 @@ namespace Microsoft.Maui.DeviceTests
await CreateHandlerAndAddToWindow(shell, async () =>
{
- var shellContext = (IShellContext)shell.Handler;
await Task.Delay(100);
var headerPlatformView = header.ToPlatform();
var appBar = headerPlatformView.GetParentOfType<AppBarLayout>();
@@ -528,9 +524,12 @@ namespace Microsoft.Maui.DeviceTests
// (6 tabs remain, still over the 5-item max) — the "More" IMenuItem
// must be reused, not recreated, since it's not structurally changing.
shell.CurrentItem.Items.RemoveAt(6);
+ shell.CurrentItem.Items[0].Title = "Updated Tab 1";
- // let the change propagate
- await AssertEventually(() => shell.CurrentItem.Items.Count == 6);
+ // Wait for a native menu update, not the already-updated managed collection count.
+ await AssertEventually(() =>
+ bottomView.Menu.Size() == 5 &&
+ bottomView.Menu.GetItem(0).TitleFormatted?.ToString() == "Updated Tab 1");
menu = bottomView.Menu;
Assert.Equal(5, menu.Size());
@@ -630,7 +629,6 @@ namespace Microsoft.Maui.DeviceTests
await CreateHandlerAndAddToWindow(shell, async () =>
{
- var shellContext = (IShellContext)shell.Handler;
await OnLoadedAsync(shell.CurrentPage);
await OnNavigatedToAsync(shell.CurrentPage);
@@ -690,8 +688,7 @@ namespace Microsoft.Maui.DeviceTests
await Task.Delay(10);
var hamburger =
- GetPlatformToolbar((IPlatformViewHandler)shellContext).GetChildrenOfType<AppCompatImageButton>().FirstOrDefault() ??
- throw new InvalidOperationException("Unable to find Drawer Button");
+ GetDrawerButton(shellContext);
timeOut = timeOut ?? TimeSpan.FromSeconds(2);
@@ -708,6 +705,19 @@ namespace Microsoft.Maui.DeviceTests
}
}
+ protected virtual MaterialToolbar GetFlyoutToolbar(IShellContext shellContext)
+ {
+ return GetPlatformToolbar((IPlatformViewHandler)shellContext);
+ }
+
+ protected virtual AppCompatImageButton GetDrawerButton(IShellContext shellContext)
+ {
+ return GetFlyoutToolbar(shellContext)?
+ .GetChildrenOfType<AppCompatImageButton>()
+ .FirstOrDefault()
+ ?? throw new InvalidOperationException("Unable to find Drawer Button");
+ }
+
protected virtual async Task<double> ScrollFlyoutToBottom(IShellContext shellContext)
{
DrawerLayout dl = shellContext.CurrentDrawerLayout;
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!
Summary
Adds Android device test coverage for the Handler-based Shell, Modal, and Window implementations alongside the existing Renderer-based tests, allowing both Android implementations to run in CI. Also fixes a bottom navigation menu update issue in TabbedViewManager.cs that was uncovered while adding this coverage.
Changes
Source Fix – TabbedViewManager.cs
Issue:
SetupBottomNavigationView() called menu.Clear() during tab updates, recreating IMenuItem instances. The Handler-based tests validate menu item identity, which caused the tests to fail.
Fix:
Updated the menu setup logic to use BottomNavigationViewUtils.SetupMenu(), which updates existing menu items instead of recreating them.
Test Fix – PushingNavigationPageModallyWithShellShowsToolbarCorrectly
Issue:
The existing GetPlatformToolbar() helper supported ShellRenderer but not ShellHandler. In the Handler implementation, the toolbar is hosted within an outer CoordinatorLayout, so the existing lookup logic did not work.
Fix:
Added an IsBackButtonVisible helper and a GetShellHandlerToolbar() helper to locate the toolbar through the nested CoordinatorLayout hierarchy.