diff --git a/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj b/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj index 0f6951840..038885237 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj +++ b/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj @@ -1,4 +1,4 @@ - + @@ -224,6 +224,7 @@ + AppTypeSelectorControl.xaml diff --git a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/IPickProjectIdViewModel.cs b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/IPickProjectIdViewModel.cs new file mode 100644 index 000000000..d837e3aa8 --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/IPickProjectIdViewModel.cs @@ -0,0 +1,56 @@ +using Google.Apis.CloudResourceManager.v1.Data; +using GoogleCloudExtension.Utils; +using GoogleCloudExtension.Utils.Async; +using System.Collections.Generic; + +namespace GoogleCloudExtension.PickProjectDialog +{ + /// + /// Interface of the view model used by the + /// + public interface IPickProjectIdViewModel : IViewModelBase + { + /// + /// Result of the view model after the dialog window is closed. Remains + /// null until an action buttion is clicked. + /// + Project Result { get; } + + /// + /// Command to open the manage users dialog. + /// + ProtectedCommand ChangeUserCommand { get; } + + /// + /// Command to confirm the selection of a project id. + /// + ProtectedCommand OkCommand { get; } + + /// + /// Command to execute when refreshing the list of projects. + /// + ProtectedCommand RefreshCommand { get; } + + /// + /// The list of projects available to the current user. + /// + IEnumerable Projects { get; } + + /// + /// The project selected from the list of current projects. + /// + Project SelectedProject { get; set; } + + /// + /// The property that surfaces task completion information for the Load Projects task. + /// + AsyncProperty LoadTask { get; set; } + + bool HasAccount { get; } + string Filter { get; set; } + bool AllowAccountChange { get; } + string HelpText { get; } + + bool FilterItem(object item); + } +} \ No newline at end of file diff --git a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdViewModel.cs b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdViewModel.cs index c09323e99..268fbb766 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdViewModel.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdViewModel.cs @@ -27,7 +27,7 @@ namespace GoogleCloudExtension.PickProjectDialog /// /// View model for picking a project id. /// - public class PickProjectIdViewModel : ViewModelBase + public class PickProjectIdViewModel : ViewModelBase, IPickProjectIdViewModel { private IEnumerable _projects; private Project _selectedProject; diff --git a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindow.cs b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindow.cs index 566b91b02..3f624bf0a 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindow.cs +++ b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindow.cs @@ -28,7 +28,7 @@ private PickProjectIdWindow(string helpContext, bool allowAccountChange) : base(GoogleCloudExtension.Resources.PublishDialogSelectGcpProjectTitle) { ViewModel = new PickProjectIdViewModel(this, helpContext, allowAccountChange); - Content = new PickProjectIdWindowContent { DataContext = ViewModel }; + Content = new PickProjectIdWindowContent(ViewModel); } /// diff --git a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindowContent.xaml b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindowContent.xaml index 82bc08448..b7b0f9ebe 100644 --- a/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindowContent.xaml +++ b/GoogleCloudExtension/GoogleCloudExtension/PickProjectDialog/PickProjectIdWindowContent.xaml @@ -29,7 +29,7 @@ - @@ -144,7 +144,7 @@ public partial class PickProjectIdWindowContent { - public PickProjectIdWindowContent() + // ReSharper disable once MemberCanBePrivate.Global + public const string CvsKey = "cvs"; + private IPickProjectIdViewModel ViewModel { get; } + public PickProjectIdWindowContent(IPickProjectIdViewModel viewModel) { InitializeComponent(); + DataContext = ViewModel = viewModel; + // Ensure the focus is in the filter textbox. _filter.Focus(); } - private void OnFilterItemInCollectionView(object sender, System.Windows.Data.FilterEventArgs e) - { - var viewModel = DataContext as PickProjectIdViewModel; - e.Accepted = viewModel?.FilterItem(e.Item) ?? false; - } + private void OnFilterItemInCollectionView(object sender, FilterEventArgs e) => + e.Accepted = ViewModel?.FilterItem(e.Item) ?? false; - private void OnFilterTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) + private void OnFilterTextChanged(object sender, TextChangedEventArgs e) { - var cvs = Resources["cvs"] as CollectionViewSource; - var view = cvs.View; - view.Refresh(); + var collectionViewSource = Resources[CvsKey] as CollectionViewSource; + collectionViewSource?.View?.Refresh(); } } } diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj index e3b0c4d8d..b4c75f576 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GoogleCloudExtensionUnitTests.csproj @@ -255,6 +255,7 @@ + @@ -299,7 +300,7 @@ - + diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/PickProjectIdViewModelTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdViewModelTests.cs similarity index 99% rename from GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/PickProjectIdViewModelTests.cs rename to GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdViewModelTests.cs index 6d3a441f2..9dfc8c5ec 100644 --- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/TemplateWizards/Dialogs/PickProjectIdViewModelTests.cs +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdViewModelTests.cs @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -using Google.Apis.CloudResourceManager.v1.Data; -using GoogleCloudExtension.Accounts; -using GoogleCloudExtension.PickProjectDialog; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; +using Google.Apis.CloudResourceManager.v1.Data; +using GoogleCloudExtension.Accounts; +using GoogleCloudExtension.PickProjectDialog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; -namespace GoogleCloudExtensionUnitTests.TemplateWizards.Dialogs +namespace GoogleCloudExtensionUnitTests.PickProjectDialog { [TestClass] public class PickProjectIdViewModelTests : ExtensionTestBase diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdWindowContentTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdWindowContentTests.cs new file mode 100644 index 000000000..e32e07f8c --- /dev/null +++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/PickProjectDialog/PickProjectIdWindowContentTests.cs @@ -0,0 +1,82 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Google.Apis.CloudResourceManager.v1.Data; +using GoogleCloudExtension.PickProjectDialog; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System.ComponentModel; +using System.Linq; +using System.Windows.Data; +using System.Windows.Threading; + +namespace GoogleCloudExtensionUnitTests.PickProjectDialog +{ + [TestClass] + public class PickProjectIdWindowContentTests : ExtensionTestBase + { + private PickProjectIdWindowContent _objectUnderTest; + private Mock _viewModelMock; + + protected override void BeforeEach() + { + _viewModelMock = new Mock(); + _objectUnderTest = new PickProjectIdWindowContent(_viewModelMock.Object); + // Initalize data bindings. See https://stackoverflow.com/questions/5396805/force-binding-in-wpf + _objectUnderTest.Dispatcher.Invoke(() => { }, DispatcherPriority.SystemIdle); + } + + [TestMethod] + public void TestInitalConditions() + { + Assert.AreEqual(_objectUnderTest.DataContext, _viewModelMock.Object); + Assert.IsTrue(_objectUnderTest._filter.IsFocused); + } + + [TestMethod] + public void TestFilterUpdated() + { + var visibleProject = new Project { Name = "Visible", ProjectId = "2" }; + _viewModelMock.Setup(vm => vm.FilterItem(It.IsAny())).Returns(false); + _viewModelMock.Setup(vm => vm.FilterItem(visibleProject)).Returns(true); + + _viewModelMock.SetupGet(vm => vm.Projects).Returns( + new[] + { + new Project {Name = "Filtered Out", ProjectId = "1"}, + visibleProject + }); + _viewModelMock.Raise( + vm => vm.PropertyChanged += null, + new PropertyChangedEventArgs(nameof(IPickProjectIdViewModel.Projects))); + + var cvs = (CollectionViewSource)_objectUnderTest.Resources[PickProjectIdWindowContent.CvsKey]; + CollectionAssert.AreEqual(new[] { visibleProject }, cvs.View.Cast().ToList()); + } + + [TestMethod] + public void TestFilterUpdateWithNullViewDoesNotThrow() + { + _viewModelMock.SetupGet(vm => vm.Projects).Returns(() => null); + _viewModelMock.Raise( + vm => vm.PropertyChanged += null, + new PropertyChangedEventArgs(nameof(IPickProjectIdViewModel.Projects))); + + _viewModelMock.SetupGet(vm => vm.Filter).Returns("Visible"); + _viewModelMock.Raise( + vm => vm.PropertyChanged += null, + new PropertyChangedEventArgs(nameof(IPickProjectIdViewModel.Filter))); + } + } +}