-
Notifications
You must be signed in to change notification settings - Fork 2k
[PR-Agent] Fix ApplyQueryAttributes called with empty dictionary on back #33451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| using Microsoft.Maui.Controls; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Maui.Controls.Sample.Issues | ||
| { | ||
| [Issue(IssueTracker.Github, 33415, "ApplyQueryAttributes gets called with empty Dictionary on back", PlatformAffected.All)] | ||
| public class Issue33415 : TestShell | ||
| { | ||
| protected override void Init() | ||
| { | ||
| // Start on a landing page, not the test page | ||
| var landingPage = new ContentPage | ||
| { | ||
| Content = new Button | ||
| { | ||
| AutomationId = "NavigateToMainPageButton", | ||
| Text = "Navigate to Main Page with Parameters", | ||
| Command = new Command(async () => | ||
| { | ||
| await Shell.Current.GoToAsync("MainPage", new Dictionary<string, object> | ||
| { | ||
| { "TestKey", "TestValue" } | ||
| }); | ||
| }) | ||
| } | ||
| }; | ||
| Items.Add(new ShellContent { Title = "Landing", Content = landingPage }); | ||
|
|
||
| // Register the main test page as a route (not starting page) | ||
| Routing.RegisterRoute("MainPage", typeof(Issue33415MainPage)); | ||
| Routing.RegisterRoute("SecondPage", typeof(Issue33415SecondPage)); | ||
| } | ||
| } | ||
|
|
||
| public class Issue33415MainPage : ContentPage, IQueryAttributable | ||
| { | ||
| private readonly Label _statusLabel; | ||
| private readonly Label _callCountLabel; | ||
| private readonly Button _navigateButton; | ||
| private int _applyQueryCallCount = 0; | ||
|
|
||
| public Issue33415MainPage() | ||
| { | ||
| _statusLabel = new Label | ||
| { | ||
| AutomationId = "StatusLabel", | ||
| Text = "Status: Not called", | ||
| Margin = new Thickness(10) | ||
| }; | ||
|
|
||
| _callCountLabel = new Label | ||
| { | ||
| AutomationId = "CallCountLabel", | ||
| Text = "Call count: 0", | ||
| Margin = new Thickness(10) | ||
| }; | ||
|
|
||
| _navigateButton = new Button | ||
| { | ||
| AutomationId = "NavigateButton", | ||
| Text = "Navigate to Second Page", | ||
| Margin = new Thickness(10) | ||
| }; | ||
|
|
||
| _navigateButton.Clicked += OnNavigateClicked; | ||
|
|
||
| Content = new StackLayout | ||
| { | ||
| Padding = new Thickness(10), | ||
| Children = | ||
| { | ||
| new Label | ||
| { | ||
| Text = "Issue 33415 - ApplyQueryAttributes called with empty Dictionary on back", | ||
| FontSize = 16, | ||
| FontAttributes = FontAttributes.Bold, | ||
| Margin = new Thickness(0, 0, 0, 20) | ||
| }, | ||
| _statusLabel, | ||
| _callCountLabel, | ||
| _navigateButton | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| public void ApplyQueryAttributes(IDictionary<string, object> query) | ||
| { | ||
| _applyQueryCallCount++; | ||
| _callCountLabel.Text = $"Call count: {_applyQueryCallCount}"; | ||
|
|
||
| if (query.Count == 0) | ||
| { | ||
| _statusLabel.Text = "Status: ApplyQueryAttributes called with EMPTY dictionary"; | ||
| } | ||
| else | ||
| { | ||
| _statusLabel.Text = $"Status: ApplyQueryAttributes called with {query.Count} parameter(s)"; | ||
|
|
||
| // According to documentation, calling Clear() should prevent | ||
| // ApplyQueryAttributes from being called again on back navigation | ||
| query.Clear(); | ||
| } | ||
| } | ||
|
|
||
| private async void OnNavigateClicked(object sender, System.EventArgs e) | ||
| { | ||
| // Navigate to second page - pass through the same parameter for testing | ||
| await Shell.Current.GoToAsync("SecondPage", new Dictionary<string, object> | ||
| { | ||
| { "TestKey", "TestValue" } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| [QueryProperty(nameof(TestKey), "TestKey")] | ||
| public class Issue33415SecondPage : ContentPage | ||
| { | ||
| private readonly Label _receivedLabel; | ||
| private string _testKey; | ||
|
|
||
| public string TestKey | ||
| { | ||
| get => _testKey; | ||
| set | ||
| { | ||
| _testKey = value; | ||
| _receivedLabel.Text = $"Received: {value ?? "null"}"; | ||
| } | ||
| } | ||
|
|
||
| public Issue33415SecondPage() | ||
| { | ||
| _receivedLabel = new Label | ||
| { | ||
| AutomationId = "ReceivedLabel", | ||
| Text = "Received: null", | ||
| Margin = new Thickness(10) | ||
| }; | ||
|
|
||
| var backButton = new Button | ||
| { | ||
| AutomationId = "BackButton", | ||
| Text = "Go Back", | ||
| Margin = new Thickness(10) | ||
| }; | ||
|
|
||
| backButton.Clicked += async (s, e) => await Shell.Current.GoToAsync(".."); | ||
|
|
||
| Content = new StackLayout | ||
| { | ||
| Padding = new Thickness(10), | ||
| Children = | ||
| { | ||
| new Label | ||
| { | ||
| Text = "Second Page", | ||
| FontSize = 16, | ||
| FontAttributes = FontAttributes.Bold, | ||
| Margin = new Thickness(0, 0, 0, 20) | ||
| }, | ||
| _receivedLabel, | ||
| backButton | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||||||||||||||||||||||||||||||||
| using NUnit.Framework; | ||||||||||||||||||||||||||||||||||||
| using UITest.Appium; | ||||||||||||||||||||||||||||||||||||
| using UITest.Core; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| namespace Microsoft.Maui.TestCases.Tests.Issues | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| public class Issue33415 : _IssuesUITest | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| public override string Issue => "ApplyQueryAttributes gets called with empty Dictionary on back"; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| public Issue33415(TestDevice device) : base(device) { } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||
| [Category(UITestCategories.Shell)] | ||||||||||||||||||||||||||||||||||||
| public void ApplyQueryAttributesShouldNotBeCalledWithEmptyDictionaryOnBack() | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| // Start on landing page - tap to navigate TO MainPage WITH parameters | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("NavigateToMainPageButton"); | ||||||||||||||||||||||||||||||||||||
| App.Tap("NavigateToMainPageButton"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Wait for main page to appear | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("StatusLabel"); | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("CallCountLabel"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Verify ApplyQueryAttributes was called with parameters | ||||||||||||||||||||||||||||||||||||
| var callCountAfterNav = App.FindElement("CallCountLabel").GetText(); | ||||||||||||||||||||||||||||||||||||
| var statusAfterNav = App.FindElement("StatusLabel").GetText(); | ||||||||||||||||||||||||||||||||||||
| Assert.That(callCountAfterNav, Is.EqualTo("Call count: 1"), "ApplyQueryAttributes should be called once with parameters"); | ||||||||||||||||||||||||||||||||||||
| Assert.That(statusAfterNav, Does.Contain("1 parameter"), "Status should indicate parameters were received"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Navigate to second page | ||||||||||||||||||||||||||||||||||||
| App.Tap("NavigateButton"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Wait for second page to appear | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("ReceivedLabel"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Verify second page received the parameter (passed through from MainPage navigation) | ||||||||||||||||||||||||||||||||||||
| var receivedText = App.FindElement("ReceivedLabel").GetText(); | ||||||||||||||||||||||||||||||||||||
| Assert.That(receivedText, Is.EqualTo("Received: TestValue"), "Second page should receive the parameter"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Go back to main page | ||||||||||||||||||||||||||||||||||||
| App.Tap("BackButton"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Wait for main page to reappear | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("StatusLabel"); | ||||||||||||||||||||||||||||||||||||
| App.WaitForElement("CallCountLabel"); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // BUG: According to documentation, after calling query.Clear() in ApplyQueryAttributes, | ||||||||||||||||||||||||||||||||||||
| // the method should NOT be called again when navigating back. | ||||||||||||||||||||||||||||||||||||
| // However, it IS called with an empty dictionary. | ||||||||||||||||||||||||||||||||||||
| var finalCallCount = App.FindElement("CallCountLabel").GetText(); | ||||||||||||||||||||||||||||||||||||
| var finalStatus = App.FindElement("StatusLabel").GetText(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // This assertion will FAIL (demonstrating the bug) | ||||||||||||||||||||||||||||||||||||
| // Expected: Call count should still be 1 (ApplyQueryAttributes should not be called on back) | ||||||||||||||||||||||||||||||||||||
| // Actual: Call count will be 2 (ApplyQueryAttributes IS called with empty dictionary) | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+56
|
||||||||||||||||||||||||||||||||||||
| // BUG: According to documentation, after calling query.Clear() in ApplyQueryAttributes, | |
| // the method should NOT be called again when navigating back. | |
| // However, it IS called with an empty dictionary. | |
| var finalCallCount = App.FindElement("CallCountLabel").GetText(); | |
| var finalStatus = App.FindElement("StatusLabel").GetText(); | |
| // This assertion will FAIL (demonstrating the bug) | |
| // Expected: Call count should still be 1 (ApplyQueryAttributes should not be called on back) | |
| // Actual: Call count will be 2 (ApplyQueryAttributes IS called with empty dictionary) | |
| // According to documentation, after calling query.Clear() in ApplyQueryAttributes, | |
| // the method should NOT be called again when navigating back. | |
| // This test verifies that behavior and ensures ApplyQueryAttributes is not invoked with an empty dictionary. | |
| var finalCallCount = App.FindElement("CallCountLabel").GetText(); | |
| var finalStatus = App.FindElement("StatusLabel").GetText(); | |
| // This assertion verifies that ApplyQueryAttributes is NOT called when navigating back after query.Clear() | |
| // i.e., the call count should still be 1 when returning to the main page. |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional check at lines 61-65 is redundant. If the assertion at line 57 passes (finalCallCount == "Call count: 1"), this code block will never execute. If the assertion fails, the test will already have failed. Consider removing this conditional block as it serves no purpose.
| // If the bug exists, this will show it was called with empty dictionary | |
| if (finalCallCount != "Call count: 1") | |
| { | |
| Assert.That(finalStatus, Is.EqualTo("Status: ApplyQueryAttributes called with EMPTY dictionary"), | |
| "If ApplyQueryAttributes is incorrectly called, it's called with empty dictionary"); | |
| } |
Uh oh!
There was an error while loading. Please reload this page.