From d2cf82b85c88262058ab76730a5e9c8e4b259694 Mon Sep 17 00:00:00 2001 From: Priya Patel Date: Wed, 12 Nov 2025 12:33:25 -0500 Subject: [PATCH] e2e Rdp client tests (#3055) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 💍 e2e test for rdp client launch * test: 💍 added rdp test helper * refactor: 💡 misc * refactor: 💡 addressed comments * refactor: 💡 removed similar test --- e2e-tests/desktop/tests/targets.spec.js | 40 ++++++++++++++++- e2e-tests/helpers/rdp.js | 60 +++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 e2e-tests/helpers/rdp.js diff --git a/e2e-tests/desktop/tests/targets.spec.js b/e2e-tests/desktop/tests/targets.spec.js index 6362fc5bf..c3d8e260e 100644 --- a/e2e-tests/desktop/tests/targets.spec.js +++ b/e2e-tests/desktop/tests/targets.spec.js @@ -6,6 +6,12 @@ import { expect, test } from '../fixtures/baseTest.js'; import * as boundaryHttp from '../../helpers/boundary-http.js'; import { textToMatch } from '../fixtures/tesseractTest.js'; +import { + isRdpClientInstalled, + isRdpRunning, + killRdpProcesses, + isOSForRdpSupported, +} from '../../helpers/rdp.js'; const hostName = 'Host name for test'; let org; @@ -117,7 +123,6 @@ test.beforeEach( }); // Create an RDP target and add host source and credential sources - // TODO: A test for RDP target connection will be added later when the Proxy is in place. rdpTarget = await boundaryHttp.createTarget(request, { scopeId: project.id, type: 'rdp', @@ -285,4 +290,37 @@ test.describe('Targets tests', () => { authedPage.getByRole('link', { name: targetWithHost.name }), ).toBeVisible(); }); + + test('Launches RDP client when connecting to an RDP target', async ({ + authedPage, + }) => { + const isRdpClientInstalledCheck = await isRdpClientInstalled(); + const isOSForRdpSupportedCheck = await isOSForRdpSupported(); + + test.skip( + !isRdpClientInstalledCheck || !isOSForRdpSupportedCheck, + 'RDP client is not installed/supported on this system', + ); + + const beforeLaunchRunning = await isRdpRunning(); + expect(beforeLaunchRunning).toBe(false); + + await authedPage.getByRole('link', { name: rdpTarget.name }).click(); + await authedPage.getByRole('button', { name: 'Open' }).click(); + + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + + const afterLaunchRunning = await isRdpRunning(); + expect(afterLaunchRunning).toBe(true); + + await authedPage.getByRole('button', { name: 'End Session' }).click(); + await expect(authedPage.getByText('Canceled successfully.')).toBeVisible(); + + killRdpProcesses(); + + const afterKillRunning = await isRdpRunning(); + expect(afterKillRunning).toBe(false); + }); }); diff --git a/e2e-tests/helpers/rdp.js b/e2e-tests/helpers/rdp.js new file mode 100644 index 000000000..defac2550 --- /dev/null +++ b/e2e-tests/helpers/rdp.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { execSync } from 'node:child_process'; + +export async function isOSForRdpSupported() { + return process.platform === 'win32' || process.platform === 'darwin'; +} + +export async function isRdpRunning() { + try { + let result; + if (process.platform === 'win32') { + result = execSync('tasklist /FI "IMAGENAME eq mstsc.exe" /FO CSV /NH', { + encoding: 'utf-8', + }); + return result && result.includes('mstsc.exe'); + } else if (process.platform === 'darwin') { + result = execSync('pgrep -x "Windows App"', { + encoding: 'utf-8', + }); + return result && result.trim().length > 0; + } + return false; + } catch { + return false; + } +} + +export async function isRdpClientInstalled() { + try { + if (process.platform === 'win32') { + execSync('where mstsc', { stdio: 'ignore' }); + return true; + } else if (process.platform === 'darwin') { + const result = execSync( + 'mdfind "kMDItemCFBundleIdentifier == \'com.microsoft.rdc.macos\'"', + { encoding: 'utf-8' }, + ); + return result && result.trim().length > 0; + } + return false; + } catch { + return false; + } +} + +export function killRdpProcesses() { + try { + if (process.platform === 'win32') { + execSync('taskkill /F /IM mstsc.exe', { stdio: 'ignore' }); + } else if (process.platform === 'darwin') { + execSync('pkill -x "Windows App"', { stdio: 'ignore' }); + } + } catch { + // no op + } +}