diff --git a/dev/NavigationView/NavigationView.h b/dev/NavigationView/NavigationView.h index 630aa3e87d..213b6e4efa 100644 --- a/dev/NavigationView/NavigationView.h +++ b/dev/NavigationView/NavigationView.h @@ -83,6 +83,8 @@ class NavigationView : // Used in AutomationPeer winrt::ItemsRepeater LeftNavRepeater(); winrt::NavigationViewItem GetSelectedContainer(); + winrt::ItemsRepeater GetParentItemsRepeaterForContainer(const winrt::NavigationViewItemBase& nvib); + winrt::IndexPath GetIndexPathForContainer(const winrt::NavigationViewItemBase& nvib); // Hierarchical related functions void Expand(const winrt::NavigationViewItem& item); @@ -107,7 +109,6 @@ class NavigationView : int GetIndexFromItem(const winrt::ItemsRepeater& ir, const winrt::IInspectable& data); static winrt::IInspectable GetItemFromIndex(const winrt::ItemsRepeater& ir, int index); winrt::IndexPath GetIndexPathOfItem(const winrt::IInspectable& data); - winrt::IndexPath GetIndexPathForContainer(const winrt::NavigationViewItemBase& nvib); winrt::UIElement GetContainerForIndex(int index); winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::IndexPath& ip); winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip); @@ -118,7 +119,6 @@ class NavigationView : winrt::IndexPath SearchEntireTreeForIndexPath(const winrt::NavigationViewItem& parentContainer, const winrt::IInspectable& data, const winrt::IndexPath& ip); winrt::ItemsRepeater GetChildRepeaterForIndexPath(const winrt::IndexPath& ip); - winrt::ItemsRepeater GetParentItemsRepeaterForContainer(const winrt::NavigationViewItemBase& nvib); winrt::NavigationViewItem GetParentNavigationViewItemForContainer(const winrt::NavigationViewItemBase& nvib); bool IsContainerTheSelectedItemInTheSelectionModel(const winrt::NavigationViewItemBase& nvib); bool IsContainerInOverflow(const winrt::NavigationViewItemBase& nvib); diff --git a/dev/NavigationView/NavigationViewItemAutomationPeer.cpp b/dev/NavigationView/NavigationViewItemAutomationPeer.cpp index 57b26787fc..fb2add6650 100644 --- a/dev/NavigationView/NavigationViewItemAutomationPeer.cpp +++ b/dev/NavigationView/NavigationViewItemAutomationPeer.cpp @@ -8,6 +8,7 @@ #include "NavigationView.h" #include "NavigationViewItemBase.h" #include "SharedHelpers.h" +#include "NavigationViewHelper.h" #include "NavigationViewItemAutomationPeer.properties.cpp" @@ -88,12 +89,7 @@ int32_t NavigationViewItemAutomationPeer::GetPositionInSetCore() if (IsOnTopNavigation()) { - if (auto navigationView = GetParentNavigationView()) - { - auto topDataProvider = winrt::get_self(navigationView)->GetTopDataProvider(); - positionInSet = GetPositionOrSetCountInTopNavHelper(IsOnTopNavigationOverflow() - ? topDataProvider.GetOverflowItems() : topDataProvider.GetPrimaryItems(), AutomationOutput::Position); - } + positionInSet = GetPositionOrSetCountInTopNavHelper(AutomationOutput::Position); } else { @@ -114,13 +110,7 @@ int32_t NavigationViewItemAutomationPeer::GetSizeOfSetCore() if (IsOnTopNavigation()) { - if (auto navview = GetParentNavigationView()) - { - auto topNavDataProvider = winrt::get_self(navview)->GetTopDataProvider(); - sizeOfSet = GetPositionOrSetCountInTopNavHelper(IsOnTopNavigationOverflow() - ? topNavDataProvider.GetOverflowItems() : topNavDataProvider.GetPrimaryItems(), AutomationOutput::Size); - - } + sizeOfSet = GetPositionOrSetCountInTopNavHelper(AutomationOutput::Size); } else { @@ -132,13 +122,26 @@ int32_t NavigationViewItemAutomationPeer::GetSizeOfSetCore() int32_t NavigationViewItemAutomationPeer::GetLevelCore() { - int32_t level = 0; - if (winrt::NavigationViewItemBase navigationViewItem = Owner().try_as()) + if (winrt::NavigationViewItemBase nvib = Owner().try_as()) { - return winrt::get_self(navigationViewItem)->Depth(); + auto const nvibImpl = winrt::get_self(nvib); + if (nvibImpl->IsTopLevelItem()) + { + return 1; + } + else + { + if (auto const navView = GetParentNavigationView()) + { + if (auto const indexPath = winrt::get_self(navView)->GetIndexPathForContainer(nvib)) + { + return indexPath.GetSize(); + } + } + } } - return level; + return 0; } void NavigationViewItemAutomationPeer::Invoke() @@ -279,6 +282,19 @@ NavigationViewRepeaterPosition NavigationViewItemAutomationPeer::GetNavigationVi return NavigationViewRepeaterPosition::LeftNav; } +winrt::ItemsRepeater NavigationViewItemAutomationPeer::GetParentRepeater() +{ + if (auto const navview = GetParentNavigationView()) + { + if (winrt::NavigationViewItemBase navigationViewItem = Owner().try_as()) + { + return winrt::get_self(navview)->GetParentItemsRepeaterForContainer(navigationViewItem); + } + } + return nullptr; +} + + // Get either the position or the size of the set for this particular item in the case of left nav. // We go through all the items and then we determine if the listviewitem from the left listview can be a navigation view item header // or a navigation view item. If it's the former, we just reset the count. If it's the latter, we increment the counter. @@ -287,54 +303,51 @@ int32_t NavigationViewItemAutomationPeer::GetPositionOrSetCountInLeftNavHelper(A { int returnValue = 0; - if (auto const navview = GetParentNavigationView()) + if (auto const repeater = GetParentRepeater()) { - if (auto const repeater = winrt::get_self(navview)->LeftNavRepeater()) + if (auto const parent = Navigate(winrt::AutomationNavigationDirection::Parent).try_as()) { - if (auto const parent = Navigate(winrt::AutomationNavigationDirection::Parent).try_as()) + if (auto const children = parent.GetChildren()) { - if (auto const children = parent.GetChildren()) - { - int index = 0; - bool itemFound = false; + int index = 0; + bool itemFound = false; - for (auto const& child : children) + for (auto const& child : children) + { + if (auto dependencyObject = repeater.TryGetElement(index)) { - if (auto dependencyObject = repeater.TryGetElement(index)) + if (dependencyObject.try_as()) { - if (dependencyObject.try_as()) + if (automationOutput == AutomationOutput::Size && itemFound) { - if (automationOutput == AutomationOutput::Size && itemFound) - { - break; - } - else - { - returnValue = 0; - } + break; } - else if (auto navviewItem = dependencyObject.try_as()) + else { - if (navviewItem.Visibility() == winrt::Visibility::Visible) - { - returnValue++; + returnValue = 0; + } + } + else if (auto navviewItem = dependencyObject.try_as()) + { + if (navviewItem.Visibility() == winrt::Visibility::Visible) + { + returnValue++; - if (winrt::FrameworkElementAutomationPeer::FromElement(navviewItem) == static_cast(*this)) + if (winrt::FrameworkElementAutomationPeer::FromElement(navviewItem) == static_cast(*this)) + { + if (automationOutput == AutomationOutput::Position) + { + break; + } + else { - if (automationOutput == AutomationOutput::Position) - { - break; - } - else - { - itemFound = true; - } + itemFound = true; } } } } - index++; } + index++; } } } @@ -347,49 +360,52 @@ int32_t NavigationViewItemAutomationPeer::GetPositionOrSetCountInLeftNavHelper(A // Basically, we do the same here as GetPositionOrSetCountInLeftNavHelper without dealing with the listview directly, because // TopDataProvider provcides two methods: GetOverflowItems() and GetPrimaryItems(), so we can break the loop (in case of position) by // comparing the value of the FrameworkElementAutomationPeer we can get from the item we're iterating through to this object. -int32_t NavigationViewItemAutomationPeer::GetPositionOrSetCountInTopNavHelper(winrt::IVector navigationViewElements, AutomationOutput automationOutput) +int32_t NavigationViewItemAutomationPeer::GetPositionOrSetCountInTopNavHelper(AutomationOutput automationOutput) { int32_t returnValue = 0; + bool itemFound = false; - if (auto const navview = GetParentNavigationView()) + if (auto const parentRepeater = GetParentRepeater()) { - bool itemFound = false; - - for (auto const& child : navigationViewElements) + if (auto const itemsSourceView = parentRepeater.ItemsSourceView()) { - if (auto const childAsNavViewItem = navview.ContainerFromMenuItem(child)) + auto numberOfElements = itemsSourceView.Count(); + + for (int32_t i = 0; i < numberOfElements; i++) { - if (child.try_as()) + if (auto child = parentRepeater.TryGetElement(i)) { - if (automationOutput == AutomationOutput::Size && itemFound) - { - break; - } - else + if (child.try_as()) { - returnValue = 0; + if (automationOutput == AutomationOutput::Size && itemFound) + { + break; + } + else + { + returnValue = 0; + } } - } - else if (auto const navviewitem = childAsNavViewItem.try_as()) - { - if (navviewitem.Visibility() == winrt::Visibility::Visible) + else if (auto const navviewitem = child.try_as()) { - returnValue++; - - if (winrt::FrameworkElementAutomationPeer::FromElement(navviewitem) == static_cast(*this)) + if (navviewitem.Visibility() == winrt::Visibility::Visible) { - if (automationOutput == AutomationOutput::Position) - { - break; - } - else + returnValue++; + + if (winrt::FrameworkElementAutomationPeer::FromElement(navviewitem) == static_cast(*this)) { - itemFound = true; + if (automationOutput == AutomationOutput::Position) + { + break; + } + else + { + itemFound = true; + } } } } } - } } } diff --git a/dev/NavigationView/NavigationViewItemAutomationPeer.h b/dev/NavigationView/NavigationViewItemAutomationPeer.h index 34ebe260d0..387e6e48ed 100644 --- a/dev/NavigationView/NavigationViewItemAutomationPeer.h +++ b/dev/NavigationView/NavigationViewItemAutomationPeer.h @@ -52,6 +52,7 @@ class NavigationViewItemAutomationPeer : }; winrt::NavigationView GetParentNavigationView(); + winrt::ItemsRepeater GetParentRepeater(); bool IsOnTopNavigation(); bool IsOnTopNavigationOverflow(); bool IsSettingsItem(); @@ -59,6 +60,6 @@ class NavigationViewItemAutomationPeer : int32_t GetNavigationViewItemCountInPrimaryList(); int32_t GetNavigationViewItemCountInTopNav(); int32_t GetPositionOrSetCountInLeftNavHelper(AutomationOutput automationOutput); - int32_t GetPositionOrSetCountInTopNavHelper(winrt::IVector navigationViewElements, AutomationOutput automationOutput); + int32_t GetPositionOrSetCountInTopNavHelper(AutomationOutput automationOutput); void ChangeSelection(bool isSelected); }; diff --git a/dev/NavigationView/NavigationView_InteractionTests/CommonTests.cs b/dev/NavigationView/NavigationView_InteractionTests/CommonTests.cs new file mode 100644 index 0000000000..62d70492ac --- /dev/null +++ b/dev/NavigationView/NavigationView_InteractionTests/CommonTests.cs @@ -0,0 +1,1631 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Infra; +using Windows.UI.Xaml.Tests.MUXControls.InteractionTests.Common; + +#if USING_TAEF +using WEX.TestExecution; +using WEX.TestExecution.Markup; +using WEX.Logging.Interop; +#else +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; +#endif + +using Microsoft.Windows.Apps.Test.Automation; +using Microsoft.Windows.Apps.Test.Foundation; +using Microsoft.Windows.Apps.Test.Foundation.Controls; +using Microsoft.Windows.Apps.Test.Foundation.Waiters; + +namespace Windows.UI.Xaml.Tests.MUXControls.InteractionTests.NavigationViewTests +{ + [TestClass] + public class CommonTests : NavigationViewTestsBase + { + [ClassInitialize] + [TestProperty("RunAs", "User")] + [TestProperty("Classification", "Integration")] + [TestProperty("TestPass:IncludeOnlyOn", "Desktop")] + [TestProperty("MUXControlsTestEnabledForPhone", "True")] + public static void ClassInitialize(TestContext testContext) + { + TestEnvironment.Initialize(testContext); + } + + [TestMethod] + public void DisplayModeTest() + { + var testScenarios = RegressionTestScenario.BuildLeftNavRegressionTestScenarios(); + foreach (var testScenario in testScenarios) + { + using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", testScenario.TestPageName })) + { + TextBlock displayModeTextBox = new TextBlock(FindElement.ByName("DisplayModeTextBox")); + + // Tests with PaneDisplayMode='Auto', which enables adaptive layout. + + Log.Comment("Test the adaptive layout with the default Compact and Expanded mode Thresholds"); + SetNavViewWidth(ControlWidth.Narrow); + Wait.ForIdle(); + Verify.AreEqual(minimal, displayModeTextBox.DocumentText); + + SetNavViewWidth(ControlWidth.Medium); + Wait.ForIdle(); + Verify.AreEqual(compact, displayModeTextBox.DocumentText); + + SetNavViewWidth(ControlWidth.Wide); + Wait.ForIdle(); + Verify.AreEqual(expanded, displayModeTextBox.DocumentText); + + Log.Comment("Test adaptive layout when the compact mode threshold is larger than the expanded mode threshold"); + SetThreshold(Threshold.High, ComboBoxName.CompactModeComboBox); + SetThreshold(Threshold.Low, ComboBoxName.ExpandedModeComboBox); + + SetNavViewWidth(ControlWidth.Narrow); + Wait.ForIdle(); + Verify.AreEqual(minimal, displayModeTextBox.DocumentText); + + SetNavViewWidth(ControlWidth.Medium); + Wait.ForIdle(); + Verify.AreEqual(expanded, displayModeTextBox.DocumentText); + + SetNavViewWidth(ControlWidth.Wide); + Wait.ForIdle(); + Verify.AreEqual(expanded, displayModeTextBox.DocumentText); + + Log.Comment("Test adaptive layout when the compact mode threshold is equal to the expanded mode threshold"); + SetThreshold(Threshold.Low, ComboBoxName.CompactModeComboBox); + SetThreshold(Threshold.Low, ComboBoxName.ExpandedModeComboBox); + + SetNavViewWidth(ControlWidth.Narrow); + Wait.ForIdle(); + Verify.AreEqual(minimal, displayModeTextBox.DocumentText); + + SetNavViewWidth(ControlWidth.Medium); + Wait.ForIdle(); + Verify.AreEqual(expanded, displayModeTextBox.DocumentText); + } + } + } + + [TestMethod] + public void IsSettingsVisibleTest() + { + var testScenarios = RegressionTestScenario.BuildAllRegressionTestScenarios(); + foreach (var testScenario in testScenarios) + { + using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", testScenario.TestPageName })) + { + String settings = testScenario.IsLeftNavTest ? "Settings" : "SettingsTopNavPaneItem"; + Log.Comment("Verify that settings item is enabled by default"); + VerifyElement.Found(settings, FindBy.Name); + + CheckBox settingsCheckbox = new CheckBox(FindElement.ByName("SettingsItemVisibilityCheckbox")); + + Log.Comment("Verify that settings item is not visible when IsSettingsVisible == false"); + settingsCheckbox.Uncheck(); + ElementCache.Clear(); + Wait.ForIdle(); + VerifyElement.NotFound(settings, FindBy.Name); + + Log.Comment("Verify that settings item is visible when IsSettingsVisible == true"); + settingsCheckbox.Check(); + Wait.ForIdle(); + VerifyElement.Found(settings, FindBy.Name); + } + } + } + + [TestMethod] + public void IsPaneToggleButtonVisibleTest() + { + var testScenarios = RegressionTestScenario.BuildLeftNavRegressionTestScenarios(); + foreach (var testScenario in testScenarios) + { + using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", testScenario.TestPageName })) + { + Log.Comment("Verify that toggle button item is enabled by default"); + VerifyElement.Found("TogglePaneButton", FindBy.Id); + + CheckBox toggleCheckbox = new CheckBox(FindElement.ByName("PaneToggleButtonVisiblityCheckbox")); + + Log.Comment("Verify that toggle button is not visible when IsPaneToggleButtonVisible == false"); + toggleCheckbox.Uncheck(); + Wait.ForIdle(); + VerifyElement.NotFound("TogglePaneButton", FindBy.Id); + + Log.Comment("Verify that settings item is visible when IsSettingsVisible == true"); + toggleCheckbox.Check(); + Wait.ForIdle(); + VerifyElement.Found("SettingsNavPaneItem", FindBy.Id); + } + } + } + + [TestMethod] + public void AlwaysShowHeaderTest() + { + var testScenarios = RegressionTestScenario.BuildLeftNavRegressionTestScenarios(); + foreach (var testScenario in testScenarios) + { + using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", testScenario.TestPageName })) + { + Log.Comment("Verify that header is visible by default"); + VerifyElement.Found("Home as header", FindBy.Name); + + CheckBox headerVisibilityCheckbox = new CheckBox(FindElement.ByName("HeaderVisiblityCheckbox")); + + Log.Comment("Verify that header is not visible in display mode expanded when AlwaysShowHeader == false"); + headerVisibilityCheckbox.Uncheck(); + Wait.ForIdle(); + VerifyElement.NotFound("Home as header", FindBy.Name); + + Log.Comment("Verify that header is visible in display mode minimal when AlwaysShowHeader == false"); + SetNavViewWidth(ControlWidth.Narrow); + Wait.ForIdle(); + VerifyElement.Found("Home as header", FindBy.Name); + + Log.Comment("Verify that header is not visible in display mode compact when AlwaysShowHeader == false"); + SetNavViewWidth(ControlWidth.Medium); + Wait.ForIdle(); + VerifyElement.NotFound("Home as header", FindBy.Name); + + Log.Comment("Verify that header is visible in display mode compact when AlwaysShowHeader == true"); + headerVisibilityCheckbox.Check(); + Wait.ForIdle(); + VerifyElement.Found("Home as header", FindBy.Name); + + // PaneDisplayMode and Top option were added on RS5, so just run the next tests if we are not using RS4 Style + if (!testScenario.IsUsingRS4Style) + { + var panelDisplayModeComboBox = new ComboBox(FindElement.ByName("PaneDisplayModeCombobox")); + Log.Comment("Set PaneDisplayMode to Top"); + panelDisplayModeComboBox.SelectItemByName("Top"); + Wait.ForIdle(); + + Log.Comment("Verify that header is visible in Top display mode when AlwaysShowHeader == true"); + VerifyElement.Found("Home as header", FindBy.Name); + + Log.Comment("Verify that header is not visible in Top display mode when AlwaysShowHeader == false"); + headerVisibilityCheckbox.Uncheck(); + Wait.ForIdle(); + VerifyElement.NotFound("Home as header", FindBy.Name); + } + } + } + } + + [TestMethod] + public void AddRemoveItemTest() + { + var testScenarios = RegressionTestScenario.BuildLeftNavRegressionTestScenarios(); + foreach (var testScenario in testScenarios) + { + using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", testScenario.TestPageName })) + { + var addButton = FindElement.ById