Skip to content

Commit

Permalink
Fix null pointer exception in pick project dialog. (#962)
Browse files Browse the repository at this point in the history
* Fix the null pointer exception.
  • Loading branch information
Jim Przybylinski authored May 17, 2018
1 parent 453a41b commit bdb6b47
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.VisualStudio.Sdk.BuildTasks.14.0.14.0.156\build\Microsoft.VisualStudio.Sdk.BuildTasks.14.0.props" Condition="$(VisualStudioVersion) != '15.0' and Exists('..\packages\Microsoft.VisualStudio.Sdk.BuildTasks.14.0.14.0.156\build\Microsoft.VisualStudio.Sdk.BuildTasks.14.0.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
Expand Down Expand Up @@ -224,6 +224,7 @@
</Compile>
<Compile Include="CopyablePrompt\CopyablePromptDialogWindowViewModel.cs" />
<Compile Include="IGoogleCloudExtensionPackage.cs" />
<Compile Include="PickProjectDialog\IPickProjectIdViewModel.cs" />
<Compile Include="StackdriverLogsViewer\ILogsViewerViewModel.cs" />
<Compile Include="TemplateWizards\Dialogs\TemplateChooserDialog\AppTypeSelectorControl.xaml.cs">
<DependentUpon>AppTypeSelectorControl.xaml</DependentUpon>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Google.Apis.CloudResourceManager.v1.Data;
using GoogleCloudExtension.Utils;
using GoogleCloudExtension.Utils.Async;
using System.Collections.Generic;

namespace GoogleCloudExtension.PickProjectDialog
{
/// <summary>
/// Interface of the view model used by the <see cref="PickProjectIdWindowContent"/>
/// </summary>
public interface IPickProjectIdViewModel : IViewModelBase
{
/// <summary>
/// Result of the view model after the dialog window is closed. Remains
/// null until an action buttion is clicked.
/// </summary>
Project Result { get; }

/// <summary>
/// Command to open the manage users dialog.
/// </summary>
ProtectedCommand ChangeUserCommand { get; }

/// <summary>
/// Command to confirm the selection of a project id.
/// </summary>
ProtectedCommand OkCommand { get; }

/// <summary>
/// Command to execute when refreshing the list of projects.
/// </summary>
ProtectedCommand RefreshCommand { get; }

/// <summary>
/// The list of projects available to the current user.
/// </summary>
IEnumerable<Project> Projects { get; }

/// <summary>
/// The project selected from the list of current projects.
/// </summary>
Project SelectedProject { get; set; }

/// <summary>
/// The property that surfaces task completion information for the Load Projects task.
/// </summary>
AsyncProperty LoadTask { get; set; }

bool HasAccount { get; }
string Filter { get; set; }
bool AllowAccountChange { get; }
string HelpText { get; }

bool FilterItem(object item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace GoogleCloudExtension.PickProjectDialog
/// <summary>
/// View model for picking a project id.
/// </summary>
public class PickProjectIdViewModel : ViewModelBase
public class PickProjectIdViewModel : ViewModelBase, IPickProjectIdViewModel
{
private IEnumerable<Project> _projects;
private Project _selectedProject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</ResourceDictionary.MergedDictionaries>

<!-- The collection view to maniuplate the list of projects. -->
<CollectionViewSource x:Key="cvs"
<CollectionViewSource x:Key="{x:Static local:PickProjectIdWindowContent.CvsKey}"
Source="{Binding Projects}"
Filter="OnFilterItemInCollectionView"
IsLiveFilteringRequested="True" />
Expand Down Expand Up @@ -144,7 +144,7 @@
<!-- The data row. -->
<DataGrid Grid.Row="2"
Grid.ColumnSpan="2"
ItemsSource="{Binding Source={StaticResource cvs}}"
ItemsSource="{Binding Source={StaticResource {x:Static local:PickProjectIdWindowContent.CvsKey}}}"
SelectedItem="{Binding SelectedProject}"
SelectionMode="Single"
SelectionUnit="FullRow"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Windows.Controls;
using System.Windows.Data;

namespace GoogleCloudExtension.PickProjectDialog
Expand All @@ -21,25 +22,26 @@ namespace GoogleCloudExtension.PickProjectDialog
/// </summary>
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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@
<Compile Include="GoogleCloudExtensionPackageTests.cs" />
<Compile Include="Options\AnalyticsOptionsTests.cs" />
<Compile Include="Options\AnalyticsOptionsPageViewModelTests.cs" />
<Compile Include="PickProjectDialog\PickProjectIdWindowContentTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PublishDialogSteps\ChoiceStep\ChoiceStepViewModelTests.cs" />
<Compile Include="PublishDialogSteps\FlexStep\FlexStepViewModelTests.cs" />
Expand Down Expand Up @@ -299,7 +300,7 @@
<Compile Include="Utils\ToolWindowCommandUtilsTests.cs" />
<Compile Include="Utils\Validation\StringValidationResultTests.cs" />
<Compile Include="Utils\Validation\ValidatingViewModelBaseTests.cs" />
<Compile Include="TemplateWizards\Dialogs\PickProjectIdViewModelTests.cs" />
<Compile Include="PickProjectDialog\PickProjectIdViewModelTests.cs" />
<Compile Include="VsVersion\ToolsPathProviderBaseTests.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IPickProjectIdViewModel> _viewModelMock;

protected override void BeforeEach()
{
_viewModelMock = new Mock<IPickProjectIdViewModel>();
_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<Project>())).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<Project>().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)));
}
}
}

0 comments on commit bdb6b47

Please sign in to comment.