diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpNavigationBar.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpNavigationBar.cs new file mode 100644 index 0000000000000000000000000000000000000000..9d3b443f83c096c8a60c0062f157c15c1199a3ba --- /dev/null +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpNavigationBar.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.CSharp +{ + [Collection(nameof(SharedIntegrationHostFixture))] + public class CSharpNavigationBar : AbstractEditorTest + { + private const string TestSource = @" +class C +{ + public void M(int i) { } + private C $$this[int index] { get { return null; } set { } } + public static bool operator ==(C c1, C c2) { return true; } + public static bool operator !=(C c1, C c2) { return false; } +} + +struct S +{ + int Foo() { } + void Bar() { } +}"; + + protected override string LanguageName => LanguageNames.CSharp; + + public CSharpNavigationBar(VisualStudioInstanceFactory instanceFactory) + : base(instanceFactory, nameof(CSharpNavigationBar)) + { + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyNavBar() + { + SetUpEditor(TestSource); + VisualStudio.Editor.PlaceCaret("this", charsOffset: 1); + VisualStudio.Editor.ExpandMemberNavBar(); + var expectedItems = new[] + { + "M(int i)", + "operator !=(C c1, C c2)", + "operator ==(C c1, C c2)", + "this[int index]" + }; + + Assert.Equal(expectedItems, VisualStudio.Editor.GetMemberNavBarItems()); + VisualStudio.Editor.SelectMemberNavBarItem("operator !=(C c1, C c2)"); + + VisualStudio.Editor.Verify.CurrentLineText("public static bool operator $$!=(C c1, C c2) { return false; }", assertCaretPosition: true, trimWhitespace: true); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyNavBar2() + { + SetUpEditor(TestSource); + + VerifyLeftSelected("C"); + VerifyRightSelected("this[int index]"); + + VisualStudio.Editor.ExpandTypeNavBar(); + var expectedItems = new[] + { + "C", + "S", + }; + + VisualStudio.Editor.SelectTypeNavBarItem("S"); + + VerifyLeftSelected("S"); + VerifyRightSelected("Foo()"); + VisualStudio.Editor.Verify.CurrentLineText("$$struct S", assertCaretPosition: true, trimWhitespace: true); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyNavBar3() + { + SetUpEditor(@" +struct S$$ +{ + int Foo() { } + void Bar() { } +}"); + VisualStudio.Editor.ExpandMemberNavBar(); + var expectedItems = new[] + { + "Bar()", + "Foo()", + }; + Assert.Equal(expectedItems, VisualStudio.Editor.GetMemberNavBarItems()); + VisualStudio.Editor.SelectMemberNavBarItem("Bar()"); + VisualStudio.Editor.Verify.CurrentLineText("void $$Bar() { }", assertCaretPosition: true, trimWhitespace: true); + + VisualStudio.ExecuteCommand("Edit.LineUp"); + VerifyRightSelected("Foo()"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void TestSplitWindow() + { + VisualStudio.Editor.SetText(@" +class C +{ + public void M(int i) { } + private C this[int index] { get { return null; } set { } } +} + +struct S +{ + int Foo() { } + void Bar() { } +}"); + VisualStudio.ExecuteCommand("Window.Split"); + VisualStudio.Editor.PlaceCaret("this", charsOffset: 1); + VerifyLeftSelected("C"); + VerifyRightSelected("this[int index]"); + VisualStudio.ExecuteCommand("Window.NextSplitPane"); + VisualStudio.Editor.PlaceCaret("Foo", charsOffset: 1); + VerifyLeftSelected("S"); + VerifyRightSelected("Foo()"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyOption() + { + VisualStudio.Workspace.SetFeatureOption("NavigationBarOptions", "ShowNavigationBar", "C#", "False"); + Assert.False(VisualStudio.Editor.IsNavBarEnabled()); + + VisualStudio.Workspace.SetFeatureOption("NavigationBarOptions", "ShowNavigationBar", "C#", "True"); + Assert.True(VisualStudio.Editor.IsNavBarEnabled()); + } + + private void VerifyLeftSelected(string expected) + { + Assert.Equal(expected, VisualStudio.Editor.GetTypeNavBarSelection()); + } + + private void VerifyRightSelected(string expected) + { + Assert.Equal(expected, VisualStudio.Editor.GetMemberNavBarSelection()); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicNavigationBar.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicNavigationBar.cs new file mode 100644 index 0000000000000000000000000000000000000000..a34c785ea2725ac318b724d59e4350cdc6f5afc1 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicNavigationBar.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.Basic +{ + [Collection(nameof(SharedIntegrationHostFixture))] + public class BasicNavigationBar : AbstractEditorTest + { + private const string TestSource = @" +Class C + Public WithEvents Domain As AppDomain + Public Sub Foo() + End Sub +End Class + +Structure S + Public Property A As Integer + Public Property B As Integer +End Structure"; + + protected override string LanguageName => LanguageNames.VisualBasic; + + public BasicNavigationBar(VisualStudioInstanceFactory instanceFactory) + : base(instanceFactory, nameof(BasicNavigationBar)) + { + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyNavBar() + { + VisualStudio.Editor.SetText(TestSource); + + VisualStudio.Editor.PlaceCaret("Foo", charsOffset: 1); + + VerifyLeftSelected("C"); + VerifyRightSelected("Foo"); + + VisualStudio.Editor.ExpandTypeNavBar(); + var expectedItems = new[] + { + "C", + "Domain", + "S" + }; + + Assert.Equal(expectedItems, VisualStudio.Editor.GetTypeNavBarItems()); + + VisualStudio.Editor.SelectTypeNavBarItem("S"); + + VisualStudio.Editor.Verify.CaretPosition(112); + VisualStudio.Editor.Verify.CurrentLineText("Structure S$$", assertCaretPosition: true); + + VisualStudio.ExecuteCommand("Edit.LineDown"); + VerifyRightSelected("A"); + + VisualStudio.Editor.ExpandMemberNavBar(); + expectedItems = new[] + { + "A", + "B", + }; + + Assert.Equal(expectedItems, VisualStudio.Editor.GetMemberNavBarItems()); + VisualStudio.Editor.SelectMemberNavBarItem("B"); + VisualStudio.Editor.Verify.CaretPosition(169); + VisualStudio.Editor.Verify.CurrentLineText("Public Property $$B As Integer", assertCaretPosition: true, trimWhitespace: true); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void CodeSpit() + { + VisualStudio.Editor.SetText(TestSource); + + VisualStudio.Editor.PlaceCaret("C", charsOffset: 1); + VerifyLeftSelected("C"); + VisualStudio.Editor.ExpandMemberNavBar(); + Assert.Equal(new[] { "New", "Finalize", "Foo" }, VisualStudio.Editor.GetMemberNavBarItems()); + VisualStudio.Editor.SelectMemberNavBarItem("New"); + VisualStudio.Editor.Verify.TextContains(@" + Public Sub New() + + End Sub"); + VisualStudio.Editor.Verify.CaretPosition(78); // Caret is between New() and End Sub() in virtual whitespace + VisualStudio.Editor.Verify.CurrentLineText("$$", assertCaretPosition: true); + } + + [Fact, Trait(Traits.Feature, Traits.Features.NavigationBar)] + public void VerifyOption() + { + VisualStudio.Workspace.SetFeatureOption("NavigationBarOptions", "ShowNavigationBar", "Visual Basic", "False"); + Assert.False(VisualStudio.Editor.IsNavBarEnabled()); + + VisualStudio.Workspace.SetFeatureOption("NavigationBarOptions", "ShowNavigationBar", "Visual Basic", "True"); + Assert.True(VisualStudio.Editor.IsNavBarEnabled()); + } + + private void VerifyLeftSelected(string expected) + { + Assert.Equal(expected, VisualStudio.Editor.GetTypeNavBarSelection()); + } + + private void VerifyRightSelected(string expected) + { + Assert.Equal(expected, VisualStudio.Editor.GetMemberNavBarSelection()); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj index eb2684993a4e6c14eb9830ee1e7ed6bca725cc53..2d4d19c6c0b5a5f53043b19a5f91f5f0291fde8c 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -39,6 +39,7 @@ + @@ -56,6 +57,7 @@ + diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index bd271c0ca5ed27af6405d956ff8511940956d85c..a4f759e952af5c31665b1f1fed49e0211a782f7a 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess { - internal class Editor_InProc : TextViewWindow_InProc + internal partial class Editor_InProc : TextViewWindow_InProc { private static readonly Guid IWpfTextViewId = new Guid("8C40265E-9FDB-4F54-A0FD-EBB72B7D0476"); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc_NavigationBar.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc_NavigationBar.cs new file mode 100644 index 0000000000000000000000000000000000000000..0f691156602e220505fc0f5eff66af77f1524789 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc_NavigationBar.cs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess +{ + internal partial class Editor_InProc + { + public string GetSelectedNavBarItem(int comboBoxIndex) + => ExecuteOnActiveView(v => GetNavigationBarComboBoxes(v)[comboBoxIndex].SelectedItem?.ToString()); + + public string[] GetNavBarItems(int comboBoxIndex) + => ExecuteOnActiveView(v => + GetNavigationBarComboBoxes(v)[comboBoxIndex] + .Items + .OfType() + .Select(i => i?.ToString() ?? "") + .ToArray()); + + public int GetNavbarItemIndex(int index, string itemText) + { + int FindItem(ComboBox comboBox) + { + for (int i = 0; i < comboBox.Items.Count; i++) + { + if (comboBox.Items[i].ToString() == itemText) + { + return i; + } + } + + return -1; + } + + return ExecuteOnActiveView(v => FindItem(GetNavigationBarComboBoxes(v)[index])); + } + + public void ExpandNavigationBar(int index) + { + ExecuteOnActiveView(v => + { + var combobox = GetNavigationBarComboBoxes(v)[index]; + combobox.Focus(); + combobox.IsDropDownOpen = true; + }); + } + + public void SelectNavBarItem(int comboboxIndex, string selection) + { + var itemIndex = GetNavbarItemIndex(comboboxIndex, selection); + if (itemIndex < 0) + { + throw new ArgumentException($"Could not find {selection} in combobox"); + } + + ExpandNavigationBar(comboboxIndex); + System.Windows.Forms.SendKeys.SendWait("{HOME}"); + for (int i = 0; i < itemIndex; i++) + { + System.Windows.Forms.SendKeys.SendWait("{DOWN}"); + } + System.Windows.Forms.SendKeys.SendWait("{ENTER}"); + } + + public bool IsNavBarEnabled() + => ExecuteOnActiveView(v => GetNavbar(v) != null); + + private List GetNavigationBarComboBoxes(IWpfTextView textView) + { + var margin = GetNavbar(textView); + List combos = margin.GetFieldValue>("_combos"); + return combos; + } + + private static UIElement GetNavbar(IWpfTextView textView) + { + var control = textView.VisualElement; + while (control != null) + { + if (control.GetType().Name == "WpfMultiViewHost") + { + break; + } + + control = VisualTreeHelper.GetParent(control) as FrameworkElement; + } + + var topMarginControl = control.GetPropertyValue("TopMarginControl"); + var vsDropDownBarAdapterMargin = topMarginControl.Content as UIElement; + return vsDropDownBarAdapterMargin; + } + } + + internal static class ReflectionExtensions + { + public static PropertyType GetPropertyValue(this object instance, string propertyName) + { + return (PropertyType)GetPropertyValue(instance, propertyName); + } + + public static object GetPropertyValue(this object instance, string propertyName) + { + Type type = instance.GetType(); + PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (propertyInfo == null) + { + throw new ArgumentException("Property " + propertyName + " was not found on type " + type.ToString()); + } + object result = propertyInfo.GetValue(instance, null); + return result; + } + + public static object GetFieldValue(this object instance, string fieldName) + { + Type type = instance.GetType(); + FieldInfo fieldInfo = null; + while (type != null) + { + fieldInfo = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (fieldInfo != null) + { + break; + } + type = type.BaseType; + } + + if (fieldInfo == null) + { + throw new FieldAccessException("Field " + fieldName + " was not found on type " + type.ToString()); + } + object result = fieldInfo.GetValue(instance); + return result; // you can place a breakpoint here (for debugging purposes) + } + + public static FieldType GetFieldValue(this object instance, string fieldName) + { + return (FieldType)GetFieldValue(instance, fieldName); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index 0b103eba5bf26fac9aaa679a66395b7815b7aef1..75029c6f38cd3bfc17a9f68ff954f0a40517b45e 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -173,6 +173,85 @@ public void WaitForActiveView(string viewName) public string[] GetErrorTags() => _editorInProc.GetErrorTags(); + + public void ExpandProjectNavBar() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.ExpandNavigationBar(0); + } + + public void ExpandTypeNavBar() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.ExpandNavigationBar(1); + } + + public void ExpandMemberNavBar() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.ExpandNavigationBar(2); + } + + public string[] GetProjectNavBarItems() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetNavBarItems(0); + } + + public string[] GetTypeNavBarItems() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetNavBarItems(1); + } + + public string[] GetMemberNavBarItems() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetNavBarItems(2); + } + + public string GetProjectNavBarSelection() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetSelectedNavBarItem(0); + } + + public string GetTypeNavBarSelection() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetSelectedNavBarItem(1); + } + + public string GetMemberNavBarSelection() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.GetSelectedNavBarItem(2); + } + + public void SelectProjectNavbarItem(string item) + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.SelectNavBarItem(0, item); + } + + public void SelectTypeNavBarItem(string item) + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.SelectNavBarItem(1, item); + } + + public void SelectMemberNavBarItem(string item) + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + _editorInProc.SelectNavBarItem(2, item); + } + + public bool IsNavBarEnabled() + { + _instance.Workspace.WaitForAsyncOperations(FeatureAttribute.NavigationBar); + return _editorInProc.IsNavBarEnabled(); + } + public TextSpan[] GetKeywordHighlightTags() => Deserialize(_editorInProc.GetHighlightTags()); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj index 6ac713c90983e2436a4b958a762f011b0ec8c2f6..5a13403724c5ac00165445b2c27eabb9c929dd4f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj @@ -34,6 +34,7 @@ +