CmdPal: New Remote Desktop built-in extension (#43090)

This PR introduces a new built-in extension for Remote Desktop users.

It allows you to view past RDP connections, save predefined connections,
and connect to any of them. Or start a new RDP connection.


https://github.com/user-attachments/assets/6a5041a6-5741-4df0-a305-da7166f962e1

### GitHub issue maintenance stuff

Closes #38305

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Jiří Polášek <me@jiripolasek.com>
This commit is contained in:
Michael Jolley
2025-11-29 13:07:19 -06:00
committed by GitHub
parent 0de60445ea
commit 06afe09973
29 changed files with 1232 additions and 2 deletions

View File

@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
using System.Reflection;
using System.Text;
using Microsoft.CmdPal.Ext.RemoteDesktop.Commands;
using Microsoft.CmdPal.Ext.RemoteDesktop.Helper;
using Microsoft.CmdPal.Ext.RemoteDesktop.Properties;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests;
[TestClass]
public class FallbackRemoteDesktopItemTests
{
private static readonly CompositeFormat OpenHostCompositeFormat = CompositeFormat.Parse(Resources.remotedesktop_open_host);
[TestMethod]
public void UpdateQuery_WhenMatchingConnectionExists_UsesConnectionName()
{
var connectionName = "my-rdp-server";
// Arrange
var setup = CreateFallback(connectionName);
var fallback = setup.Fallback;
// Act
fallback.UpdateQuery("my-rdp-server");
// Assert
Assert.AreEqual(connectionName, fallback.Title);
var expectedSubtitle = string.Format(CultureInfo.CurrentCulture, OpenHostCompositeFormat, connectionName);
Assert.AreEqual(expectedSubtitle, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_connect, command.Name);
Assert.AreEqual(connectionName, GetCommandHost(command));
}
[TestMethod]
public void UpdateQuery_WhenQueryIsValidHostWithoutExistingConnection_UsesQuery()
{
// Arrange
var setup = CreateFallback();
var fallback = setup.Fallback;
const string hostname = "test.corp";
// Act
fallback.UpdateQuery(hostname);
// Assert
var expectedTitle = string.Format(CultureInfo.CurrentCulture, OpenHostCompositeFormat, hostname);
Assert.AreEqual(expectedTitle, fallback.Title);
Assert.AreEqual(Resources.remotedesktop_title, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_connect, command.Name);
Assert.AreEqual(hostname, GetCommandHost(command));
}
[TestMethod]
public void UpdateQuery_WhenQueryIsWhitespace_ResetsCommand()
{
// Arrange
var setup = CreateFallback("rdp-server-two");
var fallback = setup.Fallback;
// Act
fallback.UpdateQuery(" ");
// Assert
Assert.AreEqual(Resources.remotedesktop_command_open, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_open, command.Name);
Assert.AreEqual(string.Empty, GetCommandHost(command));
}
[TestMethod]
public void UpdateQuery_WhenQueryIsInvalidHost_ClearsCommand()
{
// Arrange
var setup = CreateFallback("rdp-server-three");
var fallback = setup.Fallback;
// Act
fallback.UpdateQuery("not a valid host");
// Assert
Assert.AreEqual(Resources.remotedesktop_command_open, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_open, command.Name);
Assert.AreEqual(string.Empty, GetCommandHost(command));
}
private static string GetCommandHost(OpenRemoteDesktopCommand command)
{
var field = typeof(OpenRemoteDesktopCommand).GetField("_rdpHost", BindingFlags.NonPublic | BindingFlags.Instance);
if (field is null)
{
return string.Empty;
}
return field.GetValue(command) as string ?? string.Empty;
}
private static (FallbackRemoteDesktopItem Fallback, IRdpConnectionManager Manager) CreateFallback(params string[] connectionNames)
{
var settingsManager = new MockSettingsManager(connectionNames);
var connectionsManager = new MockRDPConnectionsManager(settingsManager);
var fallback = new FallbackRemoteDesktopItem(connectionsManager);
return (fallback, connectionsManager);
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<RootNamespace>Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests</RootNamespace>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\tests\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ext\Microsoft.CmdPal.Ext.RemoteDesktop\Microsoft.CmdPal.Ext.RemoteDesktop.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.CmdPal.Ext.RemoteDesktop.Commands;
using Microsoft.CmdPal.Ext.RemoteDesktop.Helper;
using Microsoft.CmdPal.Ext.RemoteDesktop.Settings;
namespace Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests;
internal sealed class MockRDPConnectionsManager : IRdpConnectionManager
{
private readonly List<ConnectionListItem> _connections = new();
public IReadOnlyCollection<ConnectionListItem> Connections => _connections.AsReadOnly();
public MockRDPConnectionsManager(ISettingsInterface settingsManager)
{
_connections.AddRange(settingsManager.PredefinedConnections.Select(ConnectionHelpers.MapToResult));
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.CmdPal.Ext.RemoteDesktop.Settings;
using ToolkitSettings = Microsoft.CommandPalette.Extensions.Toolkit.Settings;
namespace Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests;
internal sealed class MockSettingsManager : ISettingsInterface
{
private readonly List<string> _connections;
public IReadOnlyCollection<string> PredefinedConnections => _connections;
public ToolkitSettings Settings { get; } = new();
public MockSettingsManager(params string[] predefinedConnections)
{
_connections = new(predefinedConnections);
}
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using Microsoft.CmdPal.Ext.RemoteDesktop.Commands;
using Microsoft.CmdPal.Ext.RemoteDesktop.Helper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests;
[TestClass]
public class RDPConnectionsManagerTests
{
[TestMethod]
public void Constructor_AddsOpenCommandItem()
{
// Act
var manager = new RDPConnectionsManager(new MockSettingsManager(["test.local"]));
// Assert
Assert.IsTrue(manager.Connections.Any(item => string.IsNullOrEmpty(item.ConnectionName)));
}
[TestMethod]
public void FindConnection_ReturnsExactMatch()
{
// Arrange
var connectionName = "rdp-test";
var connection = new ConnectionListItem(connectionName);
// Act
var result = ConnectionHelpers.FindConnection(connectionName, new[] { connection });
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(connectionName, result.ConnectionName);
}
[TestMethod]
public void FindConnection_ReturnsNullForWhitespaceQuery()
{
// Arrange
var connection = new ConnectionListItem("rdp-test");
// Act
var result = ConnectionHelpers.FindConnection(" ", new[] { connection });
// Assert
Assert.IsNull(result);
}
}

View File

@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using Microsoft.CmdPal.Ext.RemoteDesktop.Commands;
using Microsoft.CmdPal.Ext.RemoteDesktop.Pages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests;
[TestClass]
public class RemoteDesktopCommandProviderTests
{
[TestMethod]
public void ProviderHasCorrectId()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Assert
Assert.AreEqual("com.microsoft.cmdpal.builtin.remotedesktop", provider.Id);
}
[TestMethod]
public void ProviderHasDisplayName()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Assert
Assert.IsNotNull(provider.DisplayName);
Assert.IsTrue(provider.DisplayName.Length > 0);
}
[TestMethod]
public void ProviderHasIcon()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Assert
Assert.IsNotNull(provider.Icon);
}
[TestMethod]
public void TopLevelCommandsNotEmpty()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Act
var commands = provider.TopLevelCommands();
// Assert
Assert.IsNotNull(commands);
Assert.IsTrue(commands.Length > 0);
}
[TestMethod]
public void FallbackCommandsNotEmpty()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Act
var commands = provider.FallbackCommands();
// Assert
Assert.IsNotNull(commands);
Assert.IsTrue(commands.Length > 0);
}
[TestMethod]
public void TopLevelCommandsContainListPageCommand()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Act
var commands = provider.TopLevelCommands();
// Assert
Assert.AreEqual(1, commands.Length);
Assert.IsInstanceOfType(commands.Single().Command, typeof(RemoteDesktopListPage));
}
[TestMethod]
public void FallbackCommandsContainFallbackItem()
{
// Setup
var provider = new RemoteDesktopCommandProvider();
// Act
var commands = provider.FallbackCommands();
// Assert
Assert.AreEqual(1, commands.Length);
Assert.IsInstanceOfType(commands.Single(), typeof(FallbackRemoteDesktopItem));
}
}