|
1 | 1 | using System; |
| 2 | +using System.Collections.ObjectModel; |
| 3 | +using System.ComponentModel; |
| 4 | +using System.Diagnostics; |
2 | 5 | using System.Threading.Tasks; |
3 | 6 | using System.Windows.Input; |
4 | | -using Microsoft.UI.Xaml.Tests.Common; |
5 | | -using Microsoft.VisualStudio.TestTools.UnitTesting; |
6 | 7 | using Microsoft.UI.Xaml; |
7 | 8 | using Microsoft.UI.Xaml.Automation.Peers; |
8 | 9 | using Microsoft.UI.Xaml.Controls; |
| 10 | +using Microsoft.UI.Xaml.Controls.Primitives; |
| 11 | +using Microsoft.UI.Xaml.Data; |
| 12 | +using Microsoft.UI.Xaml.Tests.Common; |
| 13 | +using Microsoft.VisualStudio.TestTools.UnitTesting; |
| 14 | +using Uno.UI.RuntimeTests.Helpers; |
| 15 | +using Windows.Foundation; |
9 | 16 | using static Private.Infrastructure.TestServices; |
10 | 17 |
|
11 | 18 | namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls |
@@ -354,6 +361,156 @@ public async Task When_GroupName_Default_Two_Containers() |
354 | 361 | Assert.IsFalse(radioButtonNull2.IsChecked); |
355 | 362 | } |
356 | 363 |
|
| 364 | + [TestMethod] |
| 365 | + [RunsOnUIThread] |
| 366 | + public async Task When_RadioButtons_Unload_SelectedIndex() |
| 367 | + { |
| 368 | + var vm = new When_RadioButtons_Unload_VM |
| 369 | + { |
| 370 | + Items = ["QweQwe", "AsdAsd", "ZxcZxc"], |
| 371 | + CurrentIndex = 1, |
| 372 | + }; |
| 373 | + |
| 374 | + var sut = new RadioButtons |
| 375 | + { |
| 376 | + ItemsSource = vm.Items, |
| 377 | + }; |
| 378 | +#if DEBUG |
| 379 | + // note: remember that with #22479 resetting data-bound properties on flyout closing, |
| 380 | + // observing with a breakpoint will have a side-effect: bp hit > app lost focus > flyout is closed > bug resetting radio dp |
| 381 | + vm.PropertyChangingEx += (s, e) => |
| 382 | + { |
| 383 | + System.Diagnostics.Debug.WriteLine($"VM::{e.Property}: {e.OldValue} -> {e.NewValue}"); |
| 384 | + |
| 385 | + if (e is { Property: nameof(When_RadioButtons_Unload_VM.CurrentIndex), NewValue: -1 }) { } |
| 386 | + }; |
| 387 | +#endif |
| 388 | + sut.SetBinding(RadioButtons.SelectedIndexProperty, new Binding |
| 389 | + { |
| 390 | + Path = new PropertyPath(nameof(When_RadioButtons_Unload_VM.CurrentIndex)), |
| 391 | + Mode = BindingMode.TwoWay, |
| 392 | + Source = vm, |
| 393 | + }); |
| 394 | + var flyout = new Flyout |
| 395 | + { |
| 396 | + Content = new Grid { Children = { sut } } |
| 397 | + }; |
| 398 | + var dropdown = new DropDownButton |
| 399 | + { |
| 400 | + Content = "sadsadasd", |
| 401 | + Flyout = flyout, |
| 402 | + }; |
| 403 | + |
| 404 | + WindowHelper.WindowContent = dropdown; |
| 405 | + await WindowHelper.WaitForLoaded(dropdown); |
| 406 | + |
| 407 | + try |
| 408 | + { |
| 409 | + // 0. Open the flyout —- initial selection should be @1 |
| 410 | + flyout.ShowAt(dropdown); |
| 411 | + await WindowHelper.WaitForLoaded(sut); |
| 412 | + await WindowHelper.WaitForIdle(); |
| 413 | + Assert.AreEqual(1, sut.SelectedIndex, "0. Initial 'sut.SelectedIndex' should be 1"); |
| 414 | + Assert.AreEqual(1, vm.CurrentIndex, "0. Initial 'vm.CurrentIndex' should be 1"); |
| 415 | + |
| 416 | + // 1. Select the last item @2 programmatically -- ... |
| 417 | + sut.SelectedIndex = 2; |
| 418 | + await WindowHelper.WaitForIdle(); |
| 419 | + Assert.AreEqual(2, vm.CurrentIndex, "1. 'vm.CurrentIndex' should've been changed to 2"); |
| 420 | + |
| 421 | + // 2. Close the flyout —- selection should be preserved |
| 422 | + flyout.Hide(); |
| 423 | + await WindowHelper.WaitForIdle(); |
| 424 | + Assert.AreEqual(2, vm.CurrentIndex, "2. 'vm.CurrentIndex' should be preserved as 2"); |
| 425 | + |
| 426 | + // 3. Re-open the flyout —- selection should remain unchanged |
| 427 | + flyout.ShowAt(dropdown); |
| 428 | + await WindowHelper.WaitForLoaded(sut); |
| 429 | + await WindowHelper.WaitForIdle(); |
| 430 | + Assert.AreEqual(2, sut.SelectedIndex, "3. 'sut.SelectedIndex' should remain unchanged as 2"); |
| 431 | + Assert.AreEqual(2, vm.CurrentIndex, "3. 'vm.CurrentIndex' should remain unchanged as 2"); |
| 432 | + } |
| 433 | + finally |
| 434 | + { |
| 435 | + UITestHelper.CloseAllPopups(); |
| 436 | + } |
| 437 | + } |
| 438 | + |
| 439 | + [TestMethod] |
| 440 | + [RunsOnUIThread] |
| 441 | + public async Task When_RadioButtons_Unload_SelectedItem() |
| 442 | + { |
| 443 | + var vm = new When_RadioButtons_Unload_VM(); |
| 444 | + { |
| 445 | + vm.Items = ["QweQwe", "AsdAsd", "ZxcZxc"]; |
| 446 | + vm.CurrentItem = vm.Items[1]; |
| 447 | + } |
| 448 | + |
| 449 | + var sut = new RadioButtons |
| 450 | + { |
| 451 | + ItemsSource = vm.Items, |
| 452 | + }; |
| 453 | +#if DEBUG |
| 454 | + // note: remember that with #22479 resetting data-bound properties on flyout closing, |
| 455 | + // observing with a breakpoint will have a side-effect: bp hit > app lost focus > flyout is closed > bug resetting radio dp |
| 456 | + vm.PropertyChangingEx += (s, e) => |
| 457 | + { |
| 458 | + System.Diagnostics.Debug.WriteLine($"VM::{e.Property}: {e.OldValue} -> {e.NewValue}"); |
| 459 | + |
| 460 | + if (e is { Property: nameof(When_RadioButtons_Unload_VM.CurrentItem), NewValue: null }) { } |
| 461 | + }; |
| 462 | +#endif |
| 463 | + sut.SetBinding(RadioButtons.SelectedItemProperty, new Binding |
| 464 | + { |
| 465 | + Path = new PropertyPath(nameof(When_RadioButtons_Unload_VM.CurrentItem)), |
| 466 | + Mode = BindingMode.TwoWay, |
| 467 | + Source = vm, |
| 468 | + }); |
| 469 | + var flyout = new Flyout |
| 470 | + { |
| 471 | + Content = new Grid { Children = { sut } } |
| 472 | + }; |
| 473 | + var dropdown = new DropDownButton |
| 474 | + { |
| 475 | + Content = "sadsadasd", |
| 476 | + Flyout = flyout, |
| 477 | + }; |
| 478 | + |
| 479 | + WindowHelper.WindowContent = dropdown; |
| 480 | + await WindowHelper.WaitForLoaded(dropdown); |
| 481 | + |
| 482 | + try |
| 483 | + { |
| 484 | + // 0. Open the flyout —- initial selection should be Items@1 |
| 485 | + flyout.ShowAt(dropdown); |
| 486 | + await WindowHelper.WaitForLoaded(sut); |
| 487 | + await WindowHelper.WaitForIdle(); |
| 488 | + Assert.AreEqual(vm.Items[1], sut.SelectedItem, $"0. Initial 'sut.SelectedItem' should be '{vm.Items[1]}'"); |
| 489 | + Assert.AreEqual(vm.Items[1], vm.CurrentItem, $"0. Initial 'vm.CurrentItem' should be '{vm.Items[1]}'"); |
| 490 | + |
| 491 | + // 1. Select the last item @2 programmatically --- ... |
| 492 | + sut.SelectedItem = vm.Items[2]; |
| 493 | + await WindowHelper.WaitForIdle(); |
| 494 | + Assert.AreEqual(vm.Items[2], vm.CurrentItem, $"1. 'vm.CurrentItem' should've been changed to '{vm.Items[2]}'"); |
| 495 | + |
| 496 | + // 2. Close the flyout —- selection should be preserved |
| 497 | + flyout.Hide(); |
| 498 | + await WindowHelper.WaitForIdle(); |
| 499 | + Assert.AreEqual(vm.Items[2], vm.CurrentItem, $"2. 'vm.CurrentItem' should be preserved as '{vm.Items[2]}'"); |
| 500 | + |
| 501 | + // 3. Re-open the flyout —- selection should remain unchanged |
| 502 | + flyout.ShowAt(dropdown); |
| 503 | + await WindowHelper.WaitForLoaded(sut); |
| 504 | + await WindowHelper.WaitForIdle(); |
| 505 | + Assert.AreEqual(vm.Items[2], sut.SelectedItem, $"3. 'sut.SelectedItem' should remain unchanged as '{vm.Items[2]}'"); |
| 506 | + Assert.AreEqual(vm.Items[2], vm.CurrentItem, $"3. 'vm.CurrentItem' should remain unchanged as '{vm.Items[2]}'"); |
| 507 | + } |
| 508 | + finally |
| 509 | + { |
| 510 | + UITestHelper.CloseAllPopups(); |
| 511 | + } |
| 512 | + } |
| 513 | + |
357 | 514 | [TestMethod] |
358 | 515 | public async Task When_AutomationPeer_Toggle() |
359 | 516 | { |
@@ -412,6 +569,47 @@ await RunOnUIThread(async () => |
412 | 569 | } |
413 | 570 | } |
414 | 571 |
|
| 572 | + public class When_RadioButtons_Unload_VM : INotifyPropertyChanged//, INotifyPropertyChanging |
| 573 | + { |
| 574 | + public event PropertyChangedEventHandler PropertyChanged; |
| 575 | + public event TypedEventHandler<object, (string Property, object OldValue, object NewValue)> PropertyChangingEx; |
| 576 | + |
| 577 | + private int _currentIndex; |
| 578 | + private object _currentItem; |
| 579 | + private ObservableCollection<string> _items; |
| 580 | + |
| 581 | + public int CurrentIndex |
| 582 | + { |
| 583 | + get => _currentIndex; |
| 584 | + set |
| 585 | + { |
| 586 | + PropertyChangingEx?.Invoke(this, (nameof(CurrentIndex), _currentIndex, value)); |
| 587 | + _currentIndex = value; |
| 588 | + PropertyChanged?.Invoke(this, new(nameof(CurrentIndex))); |
| 589 | + } |
| 590 | + } |
| 591 | + public object CurrentItem |
| 592 | + { |
| 593 | + get => _currentItem; |
| 594 | + set |
| 595 | + { |
| 596 | + PropertyChangingEx?.Invoke(this, (nameof(CurrentItem), _currentItem, value)); |
| 597 | + _currentItem = value; |
| 598 | + PropertyChanged?.Invoke(this, new(nameof(CurrentItem))); |
| 599 | + } |
| 600 | + } |
| 601 | + public ObservableCollection<string> Items |
| 602 | + { |
| 603 | + get => _items; |
| 604 | + set |
| 605 | + { |
| 606 | + PropertyChangingEx?.Invoke(this, (nameof(Items), _items, value)); |
| 607 | + _items = value; |
| 608 | + PropertyChanged?.Invoke(this, new(nameof(Items))); |
| 609 | + } |
| 610 | + } |
| 611 | + } |
| 612 | + |
415 | 613 | public class TestCommand : ICommand |
416 | 614 | { |
417 | 615 | public event EventHandler CanExecuteChanged { add { } remove { } } |
|
0 commit comments