Skip to content

Commit

Permalink
Add a link to the Kubernetes code lab. (#1062)
Browse files Browse the repository at this point in the history
* Add a link to the Kubernetes codelab as in informational icon link to the Kubernetes Engine Cloud Console link.
  • Loading branch information
Jim Przybylinski authored Aug 13, 2018
1 parent 28bfd88 commit e21761c
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
x:Class="GoogleCloudExtension.CloudExplorer.CloudExplorerToolWindowControl"
xmlns:ext="clr-namespace:GoogleCloudExtension"
xmlns:consoleLinks="clr-namespace:GoogleCloudExtension.CloudExplorerSources.CloudConsoleLinks"
xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
xmlns:catalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
Background="{DynamicResource {x:Static vsshell:VsBrushes.WindowKey}}"
Foreground="{DynamicResource {x:Static vsshell:VsBrushes.WindowTextKey}}"
mc:Ignorable="d"
Expand Down Expand Up @@ -90,8 +92,17 @@
<Hyperlink Focusable="False"
Command="{Binding NavigateCommand}"
Style="{DynamicResource {x:Static vsshell:VsResourceKeys.ThemedDialogHyperlinkStyleKey}}">
<TextBlock Focusable="False" Text="{Binding Caption}" />
<Run Focusable="False" Text="{Binding Caption}" />
</Hyperlink>

<TextBlock Focusable="False"
Visibility="{Binding InfoLinkInfo, Converter={utils:NullEmptyInvisibleConverter}}">
<Hyperlink Command="{Binding NavigateInfoCommand}" TextDecorations="None">
<imaging:CrispImage Moniker="{x:Static catalog:KnownMonikers.StatusInformation}"
Width="12"
ToolTip="{Binding InfoLinkInfo.Caption}"/>
</Hyperlink>
</TextBlock>
</TextBlock>
</DataTemplate>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// limitations under the License.

using GoogleCloudExtension.CloudExplorer;
using GoogleCloudExtension.Services;
using GoogleCloudExtension.Utils;
using System;
using System.Diagnostics;

namespace GoogleCloudExtension.CloudExplorerSources.CloudConsoleLinks
{
Expand All @@ -26,46 +26,59 @@ public class ConsoleLink : TreeLeaf
{
private readonly LinkInfo _linkFormatInfo;
private readonly ICloudSourceContext _context;
private readonly Func<string, Process> _startProcess;
private readonly Lazy<IBrowserService> _browserService =
GoogleCloudExtensionPackage.Instance.GetMefServiceLazy<IBrowserService>();

/// <summary>
/// The command to execute when the link is pressed.
/// </summary>
public ProtectedCommand NavigateCommand { get; }

/// <summary>
/// The link info for the help link, if any.
/// </summary>
public LinkInfo InfoLinkInfo { get; }

/// <summary>
/// The command to navigate to the info link.
/// </summary>
public ProtectedCommand NavigateInfoCommand { get; }

private IBrowserService BrowserService => _browserService.Value;

/// <summary>
/// Creates a new Console Link tree leaf node.
/// </summary>
/// <param name="context">The <see cref="ICloudSourceContext"/>.</param>
/// <param name="linkFormatInfo">
/// The link info with the caption and the <see cref="string.Format(string,object[])"/> ready url format.
/// </param>
/// <param name="context">The <see cref="ICloudSourceContext"/>.</param>
public ConsoleLink(LinkInfo linkFormatInfo, ICloudSourceContext context) : this(
linkFormatInfo, context, Process.Start)
{ }
/// <param name="infoLinkInfo">The link info for the help section of the console link.</param>
public ConsoleLink(ICloudSourceContext context, LinkInfo linkFormatInfo, LinkInfo infoLinkInfo) : this(context, linkFormatInfo)
{
InfoLinkInfo = infoLinkInfo;
NavigateInfoCommand.CanExecuteCommand = true;
}

/// <summary>
/// Internal constructor for testing.
/// Creates a new Console Link tree leaf node.
/// </summary>
/// <param name="context">The <see cref="ICloudSourceContext"/>.</param>
/// <param name="linkFormatInfo">
/// The link info with the caption and the <see cref="string.Format(string,object[])"/> ready url format.
/// </param>
/// <param name="context">The <see cref="ICloudSourceContext"/>.</param>
/// <param name="startProcess">
/// Dependency injecion of the static function <see cref="Process.Start(string)"/>.
/// </param>
internal ConsoleLink(LinkInfo linkFormatInfo, ICloudSourceContext context, Func<string, Process> startProcess)
public ConsoleLink(ICloudSourceContext context, LinkInfo linkFormatInfo)
{
_startProcess = startProcess;
_context = context;
_linkFormatInfo = linkFormatInfo;
Caption = _linkFormatInfo.Caption;
NavigateCommand = new ProtectedCommand(OnNavigateCommand);
NavigateInfoCommand = new ProtectedCommand(OnNavigateHelpCommand, false);
}

private void OnNavigateCommand()
{
_startProcess(string.Format(_linkFormatInfo.NavigateUrl, _context.CurrentProject?.ProjectId));
}
private void OnNavigateCommand() => BrowserService.OpenBrowser(
string.Format(_linkFormatInfo.NavigateUrl, _context.CurrentProject?.ProjectId));

private void OnNavigateHelpCommand() => BrowserService.OpenBrowser(InfoLinkInfo.NavigateUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace GoogleCloudExtension.CloudExplorerSources.CloudConsoleLinks
public class ConsoleLinkGroup : TreeHierarchy
{
public ConsoleLinkGroup(string caption, ICloudSourceContext context, IEnumerable<LinkInfo> groupLinks) : base(
groupLinks.Select(l => new ConsoleLink(l, context)))
groupLinks.Select(l => new ConsoleLink(context, l)))
{
Caption = caption;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ public class ConsoleLinksRoot : TreeHierarchy, ISourceRootViewModelBase
private const string PubSubPath = "cloudpubsub";
private const string MachineLearningEnginePath = "mlengine";

private const string KubernetesCodelabLink =
"https://codelabs.developers.google.com/codelabs/cloud-kubernetes-aspnetcore/";

internal static readonly LinkInfo s_consoleHomeFormatInfo = new LinkInfo(
HomeUrl, Resources.CloudExplorerConsoleLinkCaption);

private static readonly IReadOnlyList<(string, string)> s_primaryConsoleLinkPaths = new[]
private static readonly IReadOnlyList<(string path, string caption)> s_primaryConsoleLinkPaths = new[]
{
(AppEnginePath, Resources.CloudLinkAppEngineCaption),
(ComputeEnginePath, Resources.CloudLinkComputeEngineCaption),
Expand Down Expand Up @@ -129,6 +132,11 @@ public class ConsoleLinksRoot : TreeHierarchy, ISourceRootViewModelBase
})
};

private static readonly IReadOnlyDictionary<string, LinkInfo> s_helpLinks = new Dictionary<string, LinkInfo>
{
[KubernetesEnginePath] = new LinkInfo(KubernetesCodelabLink, Resources.ConsoleLinksKubernetesInfoTooltip)
};

private readonly Func<string, Process> _startProcess;

private readonly ICloudSourceContext _context;
Expand Down Expand Up @@ -163,9 +171,14 @@ internal ConsoleLinksRoot(ICloudSourceContext context, Func<string, Process> sta
Caption = s_consoleHomeFormatInfo.Caption;
NavigateCommand = new ProtectedCommand(OnNavigateCommand);

foreach (LinkInfo formatLinkInfo in PrimaryConsoleLinkFormats)
foreach ((string path, string caption) tuple in s_primaryConsoleLinkPaths)
{
Children.Add(new ConsoleLink(formatLinkInfo, _context));
LinkInfo linkInfo = PathTupleToLinkInfo(tuple);
ConsoleLink consoleLink = s_helpLinks.ContainsKey(tuple.path) ?
new ConsoleLink(_context, linkInfo, s_helpLinks[tuple.path]) :
new ConsoleLink(_context, linkInfo);

Children.Add(consoleLink);
}

foreach ((string groupCaption, IEnumerable<LinkInfo> linkInfos) in GroupedConsoleLinkFormats)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions GoogleCloudExtension/GoogleCloudExtension/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2507,4 +2507,8 @@ Delete corrupted file?</value>
<data name="UiDontShowWarningAgain" xml:space="preserve">
<value>Don't show this warning again</value>
</data>
<data name="ConsoleLinksKubernetesInfoTooltip" xml:space="preserve">
<value>Kubernetes Engine Code Lab</value>
<comment>The tooltip of the info link of the kubernetes console link.</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
namespace GoogleCloudExtensionUnitTests.CloudExplorerSources.CloudConsoleLinks
{
[TestClass]
public class CloudConsoleLinksSourceTests
public class CloudConsoleLinksSourceTests : ExtensionTestBase
{
[TestMethod]
public void TestConstructor_SetsRoot()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace GoogleCloudExtensionUnitTests.CloudExplorerSources.CloudConsoleLinks
{
[TestClass]
public class ConsoleLinkGroupTests
public class ConsoleLinkGroupTests : ExtensionTestBase
{
private static readonly ICloudSourceContext s_mockedContext = Mock.Of<ICloudSourceContext>();
private const string DefaultCaption = "DefaultCaption";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,96 @@

using GoogleCloudExtension.CloudExplorer;
using GoogleCloudExtension.CloudExplorerSources.CloudConsoleLinks;
using GoogleCloudExtension.Services;
using GoogleCloudExtension.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Diagnostics;
using TestingHelpers;

namespace GoogleCloudExtensionUnitTests.CloudExplorerSources.CloudConsoleLinks
{
[TestClass]
public class ConsoleLinkTests
public class ConsoleLinkTests : ExtensionTestBase
{
private const string DefaultCaption = "Default Caption";
private const string DefaultUrl = "url://default";
private static readonly LinkInfo s_defaultLinkInfo = new LinkInfo(DefaultUrl, DefaultCaption);
private Mock<Func<string, Process>> _startProcessMock;
private Mock<IBrowserService> _browserServiceMock;

[TestInitialize]
public void BeforeEach()
{
_startProcessMock = new Mock<Func<string, Process>>();
_browserServiceMock = new Mock<IBrowserService>();

PackageMock.Setup(p => p.GetMefServiceLazy<IBrowserService>()).Returns(_browserServiceMock.ToLazy());
}

[TestMethod]
public void TestConstructor_SetsCaption()
{
const string testCaption = "Test Caption";
var objectUnderTest = new ConsoleLink(new LinkInfo(DefaultUrl, testCaption), Mock.Of<ICloudSourceContext>());
var objectUnderTest = new ConsoleLink(Mock.Of<ICloudSourceContext>(), new LinkInfo(DefaultUrl, testCaption));

Assert.AreEqual(testCaption, objectUnderTest.Caption);
}

[TestMethod]
public void TestConstructor_CreatesNavigateCommand()
{
var objectUnderTest = new ConsoleLink(s_defaultLinkInfo, Mock.Of<ICloudSourceContext>());
var objectUnderTest = new ConsoleLink(Mock.Of<ICloudSourceContext>(), s_defaultLinkInfo);

Assert.IsTrue(objectUnderTest.NavigateCommand.CanExecuteCommand);
}

[TestMethod]
public void TestConstructor_SetsHelpLinkInfo()
{
var expectedHelpLinkInfo = new LinkInfo("url://test/url", "Test Caption");
var objectUnderTest = new ConsoleLink(
Mock.Of<ICloudSourceContext>(),
new LinkInfo(DefaultUrl, DefaultCaption),
expectedHelpLinkInfo);

Assert.AreEqual(expectedHelpLinkInfo, objectUnderTest.InfoLinkInfo);
}

[TestMethod]
public void TestConstructor_EnablesNavigateHelpCommand()
{
var objectUnderTest = new ConsoleLink(
Mock.Of<ICloudSourceContext>(),
new LinkInfo(DefaultUrl, DefaultCaption),
new LinkInfo(DefaultUrl, DefaultCaption));

Assert.IsTrue(objectUnderTest.NavigateInfoCommand.CanExecute(null));
}

[TestMethod]
public void TestNavigateHelpCommand_NavigatesToUrl()
{
const string testUrl = "url://test/url";

var objectUnderTest = new ConsoleLink(
Mock.Of<ICloudSourceContext>(),
new LinkInfo(DefaultUrl, DefaultCaption),
new LinkInfo(testUrl, "Test Caption"));
objectUnderTest.NavigateInfoCommand.Execute(null);

_browserServiceMock.Verify(b => b.OpenBrowser(testUrl));
}

[TestMethod]
public void TestNavigateCommand_NavigatesToUrlMissingProject()
{
const string testUrlFormat = "url://default?project={0}";
const string expectedUrl = "url://default?project=";
var objectUnderTest = new ConsoleLink(
new LinkInfo(testUrlFormat, DefaultCaption),
Mock.Of<ICloudSourceContext>(),
_startProcessMock.Object);
new LinkInfo(testUrlFormat, DefaultCaption));

objectUnderTest.NavigateCommand.Execute(null);

_startProcessMock.Verify(f => f(expectedUrl), Times.Once);
_browserServiceMock.Verify(b => b.OpenBrowser(expectedUrl), Times.Once);
}

[TestMethod]
Expand All @@ -74,13 +113,12 @@ public void TestNavigateCommand_NavigatesToUrl()
const string testProjectId = "testProjectId";
const string expectedUrl = "url://project/testProjectId/default";
var objectUnderTest = new ConsoleLink(
new LinkInfo(testUrlFormat, DefaultCaption),
Mock.Of<ICloudSourceContext>(c => c.CurrentProject.ProjectId == testProjectId),
_startProcessMock.Object);
new LinkInfo(testUrlFormat, DefaultCaption));

objectUnderTest.NavigateCommand.Execute(null);

_startProcessMock.Verify(f => f(expectedUrl), Times.Once);
_browserServiceMock.Verify(b => b.OpenBrowser(expectedUrl), Times.Once);
}

[TestMethod]
Expand All @@ -93,17 +131,14 @@ public void TestNavigateCommand_NavigatesToUpdatedUrl()
const string secondExpectedUrl = "url://project/secondProjectId/default";
var contextMock = new Mock<ICloudSourceContext>();
contextMock.Setup(c => c.CurrentProject.ProjectId).Returns(firstProjectId);
var objectUnderTest = new ConsoleLink(
new LinkInfo(testUrlFormat, DefaultCaption),
contextMock.Object,
_startProcessMock.Object);
var objectUnderTest = new ConsoleLink(contextMock.Object, new LinkInfo(testUrlFormat, DefaultCaption));

objectUnderTest.NavigateCommand.Execute(null);
contextMock.Setup(c => c.CurrentProject.ProjectId).Returns(secondProjectId);
objectUnderTest.NavigateCommand.Execute(null);

_startProcessMock.Verify(f => f(firstExpectedUrl), Times.Once);
_startProcessMock.Verify(f => f(secondExpectedUrl), Times.Once);
_browserServiceMock.Verify(b => b.OpenBrowser(firstExpectedUrl), Times.Once);
_browserServiceMock.Verify(b => b.OpenBrowser(secondExpectedUrl), Times.Once);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
namespace GoogleCloudExtensionUnitTests.CloudExplorerSources.CloudConsoleLinks
{
[TestClass]
public class ConsoleLinksRootTests
public class ConsoleLinksRootTests : ExtensionTestBase
{
[TestMethod]
public void TestConstructor_SetsCaption()
Expand Down

0 comments on commit e21761c

Please sign in to comment.