feat: 🎸 preferred clients section on settings page (#3034)
* feat: 🎸 preferred clients section on settings page * chore: 🤖 update mock ipc with new handlers * refactor: 💡 moved windows rdp protocol to const * refactor: 💡 moved rdp calls to app route * test: 💍 fixed failings tests * refactor: 💡 addressed comments * fix: 🐛 fixed failing tests * refactor: 💡 add mock rdp service calls to tests * refactor: 💡 addressed comments * test: 💍 add additional tests * refactor: 💡 fixed test failure
This commit is contained in:
1 parent
9234dbae70
commit
e55ea0b629
19 files changed
+487
-31
No files matched your search
@@ -0,0 +1,78 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<SettingsCard
|
||||
@header={{t 'settings.preferred-clients.title'}}
|
||||
@icon='external-link'
|
||||
@description={{t 'settings.preferred-clients.description'}}
|
||||
>
|
||||
<:body>
|
||||
<Hds::Table
|
||||
class='full-width'
|
||||
@model={{this.protocolClients}}
|
||||
@columns={{array
|
||||
(hash
|
||||
label=(t 'settings.preferred-clients.table.header.protocol')
|
||||
width='50%'
|
||||
)
|
||||
(hash
|
||||
label=(t 'settings.preferred-clients.table.header.client') width='50%'
|
||||
)
|
||||
}}
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>{{t
|
||||
(concat
|
||||
'settings.preferred-clients.table.data.protocols.'
|
||||
B.data.protocolType
|
||||
)
|
||||
}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if this.showRecommendedRdpClient}}
|
||||
<Hds::Text::Body data-test-recommended-rdp-client>
|
||||
{{t
|
||||
'settings.preferred-clients.table.data.clients.none-detected'
|
||||
rdpClientLink=this.rdp.recommendedRdpClient.link
|
||||
rdpClientName=(t
|
||||
(concat
|
||||
'settings.preferred-clients.table.data.clients.'
|
||||
this.rdp.recommendedRdpClient.name
|
||||
)
|
||||
)
|
||||
htmlSafe=true
|
||||
}}
|
||||
</Hds::Text::Body>
|
||||
{{else}}
|
||||
<Hds::Form::Select::Field
|
||||
@width='100%'
|
||||
{{on 'change' B.data.updateClient}}
|
||||
data-test-select-preferred-rdp-client
|
||||
as |F|
|
||||
>
|
||||
<F.Options>
|
||||
{{#each B.data.clients as |client|}}
|
||||
<option
|
||||
value={{client}}
|
||||
selected={{eq B.data.preferredClient client}}
|
||||
>
|
||||
{{t
|
||||
(concat
|
||||
'settings.preferred-clients.table.data.clients.'
|
||||
client
|
||||
)
|
||||
}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Field>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
</:body>
|
||||
</SettingsCard>
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
const PROTOCOL_WINDOWS_RDP = 'windows-rdp';
|
||||
|
||||
export default class SettingsCardPreferredClientsComponent extends Component {
|
||||
// =services
|
||||
@service rdp;
|
||||
|
||||
// =getters
|
||||
|
||||
/**
|
||||
* Returns the list of protocols and their available clients
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
get protocolClients() {
|
||||
return [
|
||||
{
|
||||
protocolType: PROTOCOL_WINDOWS_RDP,
|
||||
clients: this.rdp.rdpClients,
|
||||
preferredClient: this.rdp.preferredRdpClient,
|
||||
updateClient: this.updatePreferredRDPClient,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get showRecommendedRdpClient() {
|
||||
return (
|
||||
this.rdp.rdpClients.length === 1 && this.rdp.rdpClients[0] === 'none'
|
||||
);
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Updates the preferred RDP client
|
||||
* @param value
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
@action
|
||||
async updatePreferredRDPClient({ target: { value } }) {
|
||||
await this.rdp.setPreferredRdpClient(value);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export default class ApplicationRoute extends Route {
|
||||
@service clusterUrl;
|
||||
@service ipc;
|
||||
@service intl;
|
||||
@service rdp;
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -52,6 +53,9 @@ export default class ApplicationRoute extends Route {
|
||||
|
||||
await this.session.loadAuthenticatedAccount();
|
||||
}
|
||||
|
||||
// initialize RDP service with rdp client data
|
||||
await this.rdp.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,23 @@ import Service from '@ember/service';
|
||||
import { service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export const RDP_CLIENT_NONE = 'none';
|
||||
export const RDP_CLIENT_WINDOWS_APP = 'windows-app';
|
||||
export const RDP_CLIENT_MSTSC = 'mstsc';
|
||||
export const RDP_CLIENT_WINDOWS_APP_LINK =
|
||||
'https://apps.apple.com/us/app/windows-app/id1295203466';
|
||||
export const RDP_CLIENT_MSTSC_LINK =
|
||||
'https://learn.microsoft.com/windows-server/remote/remote-desktop-services/remotepc/uninstall-remote-desktop-connection';
|
||||
|
||||
const { __electronLog } = globalThis;
|
||||
const macRecommendedRdpClient = {
|
||||
name: RDP_CLIENT_WINDOWS_APP,
|
||||
link: RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
};
|
||||
const windowsRecommendedRdpClient = {
|
||||
name: RDP_CLIENT_MSTSC,
|
||||
link: RDP_CLIENT_MSTSC_LINK,
|
||||
};
|
||||
|
||||
export default class RdpService extends Service {
|
||||
// =services
|
||||
@@ -19,17 +35,21 @@ export default class RdpService extends Service {
|
||||
/**
|
||||
* The preferred RDP client set by the user.
|
||||
* @type {string|null}
|
||||
* @private
|
||||
*/
|
||||
@tracked preferredRdpClient = null;
|
||||
|
||||
/**
|
||||
* The list of available RDP clients fetched from the main process.
|
||||
* @type {Array<String>}
|
||||
* @private
|
||||
*/
|
||||
@tracked rdpClients = [];
|
||||
|
||||
/**
|
||||
* The recommended RDP client based on platform when only 'none' is available.
|
||||
* @type {Object|null}
|
||||
*/
|
||||
@tracked recommendedRdpClient = null;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
@@ -40,12 +60,21 @@ export default class RdpService extends Service {
|
||||
*/
|
||||
get isPreferredRdpClientSet() {
|
||||
return (
|
||||
this.preferredRdpClient !== null && this.preferredRdpClient !== 'none'
|
||||
this.preferredRdpClient !== null &&
|
||||
this.preferredRdpClient !== RDP_CLIENT_NONE
|
||||
);
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
async initialize() {
|
||||
await Promise.all([
|
||||
this.getRdpClients(),
|
||||
this.getPreferredRdpClient(),
|
||||
this.getRecommendedRdpClient(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of available RDP clients from the main process.
|
||||
*/
|
||||
@@ -60,11 +89,35 @@ export default class RdpService extends Service {
|
||||
} catch (error) {
|
||||
__electronLog?.error('Failed to fetch RDP clients', error.message);
|
||||
// default to 'none' option if it fails
|
||||
this.rdpClients = ['none'];
|
||||
this.rdpClients = [RDP_CLIENT_NONE];
|
||||
return this.rdpClients;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recommended RDP client based on OS platform.
|
||||
*/
|
||||
async getRecommendedRdpClient() {
|
||||
try {
|
||||
// return cached recommended client if already set
|
||||
if (this.recommendedRdpClient !== null) {
|
||||
return this.recommendedRdpClient;
|
||||
}
|
||||
const { isWindows, isMac } = await this.ipc.invoke('checkOS');
|
||||
if (isWindows) {
|
||||
this.recommendedRdpClient = windowsRecommendedRdpClient;
|
||||
} else if (isMac) {
|
||||
this.recommendedRdpClient = macRecommendedRdpClient;
|
||||
}
|
||||
} catch (error) {
|
||||
__electronLog?.error(
|
||||
'Failed to set recommended RDP client',
|
||||
error.message,
|
||||
);
|
||||
this.recommendedRdpClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the preferred RDP client from the main process.
|
||||
* @returns {string} The preferred RDP client
|
||||
@@ -83,7 +136,7 @@ export default class RdpService extends Service {
|
||||
error.message,
|
||||
);
|
||||
// default to 'none' if it fails
|
||||
this.preferredRdpClient = 'none';
|
||||
this.preferredRdpClient = RDP_CLIENT_NONE;
|
||||
return this.preferredRdpClient;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +154,7 @@ export default class RdpService extends Service {
|
||||
} catch (error) {
|
||||
__electronLog?.error('Failed to set preferred RDP client', error.message);
|
||||
// set to 'none' if it fails
|
||||
this.preferredRdpClient = 'none';
|
||||
this.preferredRdpClient = RDP_CLIENT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
<SettingsCard::Server @model={{@model}} />
|
||||
<SettingsCard::Application @model={{@model}} @toggle={{this.toggleTheme}} />
|
||||
<SettingsCard::ClientAgent @model={{@model}} />
|
||||
<SettingsCard::Logs @model={{@model}} />
|
||||
<SettingsCard::Logs @model={{@model}} />
|
||||
<SettingsCard::PreferredClients />
|
||||
@@ -164,13 +164,17 @@ const createWindow = async (partition, closeWindowCB) => {
|
||||
// Opens external links in the host default browser.
|
||||
// We allow developer.hashicorp.com domain to open on an external window
|
||||
// and releases.hashicorp.com domain to download the desktop app or
|
||||
// link to the release page for the desktop app.
|
||||
// link to the release page for the desktop app or
|
||||
// apps.apple.com domain to open the app store page to download the Windows App or
|
||||
// learn.microsoft.com domain to open the documentation for installing Remote Desktop Connection.
|
||||
browserWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (
|
||||
isLocalhost(url) ||
|
||||
url.startsWith('https://developer.hashicorp.com/') ||
|
||||
url.startsWith('https://releases.hashicorp.com/boundary-desktop/') ||
|
||||
url.startsWith('https://support.hashicorp.com/hc/en-us')
|
||||
url.startsWith('https://support.hashicorp.com/hc/en-us') ||
|
||||
url.startsWith('https://learn.microsoft.com/') ||
|
||||
url.startsWith('https://apps.apple.com/')
|
||||
) {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@ const which = require('which');
|
||||
const { isMac, isWindows } = require('../helpers/platform.js');
|
||||
const store = require('./electron-store-manager');
|
||||
|
||||
// RDP Clients
|
||||
const RDP_CLIENT_NONE = 'none';
|
||||
const RDP_CLIENT_WINDOWS_APP = 'windows-app';
|
||||
const RDP_CLIENT_MSTSC = 'mstsc';
|
||||
|
||||
// RDP Client Configuration
|
||||
const RDP_CLIENTS = [
|
||||
{
|
||||
value: 'mstsc',
|
||||
value: RDP_CLIENT_MSTSC,
|
||||
isAvailable: async () => {
|
||||
if (!isWindows()) return false;
|
||||
try {
|
||||
@@ -24,7 +29,7 @@ const RDP_CLIENTS = [
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 'windows-app',
|
||||
value: RDP_CLIENT_WINDOWS_APP,
|
||||
isAvailable: () => {
|
||||
if (!isMac()) return false;
|
||||
try {
|
||||
@@ -50,7 +55,7 @@ const RDP_CLIENTS = [
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
value: RDP_CLIENT_NONE,
|
||||
isAvailable: () => true,
|
||||
},
|
||||
];
|
||||
@@ -78,8 +83,10 @@ class RdpClientManager {
|
||||
*/
|
||||
async getBestDefaultRdpClient() {
|
||||
const availableClients = await this.getAvailableRdpClients();
|
||||
const bestClient = availableClients.find((client) => client !== 'none');
|
||||
return bestClient ?? 'none';
|
||||
const bestClient = availableClients.find(
|
||||
(client) => client !== RDP_CLIENT_NONE,
|
||||
);
|
||||
return bestClient ?? RDP_CLIENT_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +110,7 @@ class RdpClientManager {
|
||||
*/
|
||||
setPreferredRdpClient(preferredClient) {
|
||||
if (!preferredClient) {
|
||||
store.set('preferredRdpClient', 'none');
|
||||
store.set('preferredRdpClient', RDP_CLIENT_NONE);
|
||||
} else {
|
||||
store.set('preferredRdpClient', preferredClient);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ module('Acceptance | Application', function (hooks) {
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -94,6 +94,10 @@ module('Acceptance | clusterUrl', function (hooks) {
|
||||
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
|
||||
urls.projects = `${urls.scopes.global}/projects`;
|
||||
urls.targets = `${urls.projects}/targets`;
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from 'api/models/session';
|
||||
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Acceptance | projects | sessions | index', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -147,6 +148,10 @@ module('Acceptance | projects | sessions | index', function (hooks) {
|
||||
|
||||
this.ipcStub.withArgs('isCacheDaemonRunning').returns(true);
|
||||
this.stubCacheDaemonSearch('sessions', 'sessions', 'targets');
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import { STATUS_SESSION_ACTIVE } from 'api/models/session';
|
||||
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -136,6 +137,10 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
this.ipcStub.withArgs('isCacheDaemonRunning').returns(false);
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -19,6 +19,12 @@ import {
|
||||
import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import {
|
||||
RDP_CLIENT_MSTSC_LINK,
|
||||
RDP_CLIENT_MSTSC,
|
||||
RDP_CLIENT_NONE,
|
||||
RDP_CLIENT_WINDOWS_APP,
|
||||
} from 'desktop/services/rdp';
|
||||
|
||||
module('Acceptance | projects | settings | index', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -58,6 +64,9 @@ module('Acceptance | projects | settings | index', function (hooks) {
|
||||
const SIGNOUT_BTN = '[data-test-settings-signout-btn]';
|
||||
const MODAL_CLOSE_SESSIONS = '[data-test-close-sessions-modal]';
|
||||
const MODAL_CONFIRM_BTN = '.hds-modal__footer .hds-button--color-primary';
|
||||
const RDP_PREFERRED_CLIENT = '[data-test-select-preferred-rdp-client]';
|
||||
const RDP_RECOMMENDED_CLIENT = '[data-test-recommended-rdp-client]';
|
||||
const RDP_RECOMMENDED_CLIENT_LINK = '[data-test-recommended-rdp-client] a';
|
||||
|
||||
const setDefaultClusterUrl = (test) => {
|
||||
const windowOrigin = window.location.origin;
|
||||
@@ -98,6 +107,13 @@ module('Acceptance | projects | settings | index', function (hooks) {
|
||||
this.ipcStub
|
||||
.withArgs('cacheDaemonStatus')
|
||||
.returns({ version: 'Boundary CLI v0.1.0' });
|
||||
|
||||
// mock RDP client data
|
||||
this.ipcStub
|
||||
.withArgs('getRdpClients')
|
||||
.returns([RDP_CLIENT_MSTSC, RDP_CLIENT_NONE]);
|
||||
this.ipcStub.withArgs('getPreferredRdpClient').returns(RDP_CLIENT_MSTSC);
|
||||
this.ipcStub.withArgs('checkOS').returns({ isWindows: true, isMac: false });
|
||||
});
|
||||
|
||||
test('can navigate to the settings page', async function (assert) {
|
||||
@@ -226,4 +242,53 @@ module('Acceptance | projects | settings | index', function (hooks) {
|
||||
assert.ok(stopAllSessions.calledOnce);
|
||||
assert.notOk(currentSession().isAuthenticated);
|
||||
});
|
||||
|
||||
test('preferred RDP client is selected correctly for Windows', async function (assert) {
|
||||
await visit(urls.settings);
|
||||
|
||||
assert.dom(RDP_PREFERRED_CLIENT).isVisible().hasValue(RDP_CLIENT_MSTSC);
|
||||
});
|
||||
|
||||
test('preferred RDP client is selected correctly for Mac', async function (assert) {
|
||||
// update IPC stub fo mac
|
||||
this.ipcStub.withArgs('checkOS').returns({ isWindows: false, isMac: true });
|
||||
this.ipcStub
|
||||
.withArgs('getRdpClients')
|
||||
.returns([RDP_CLIENT_WINDOWS_APP, RDP_CLIENT_NONE]);
|
||||
this.ipcStub
|
||||
.withArgs('getPreferredRdpClient')
|
||||
.returns(RDP_CLIENT_WINDOWS_APP);
|
||||
await visit(urls.settings);
|
||||
|
||||
assert
|
||||
.dom(RDP_PREFERRED_CLIENT)
|
||||
.isVisible()
|
||||
.hasValue(RDP_CLIENT_WINDOWS_APP);
|
||||
});
|
||||
|
||||
test('recommended RDP client is shown when no RDP clients are detected', async function (assert) {
|
||||
// update IPC stub for no RDP clients
|
||||
this.ipcStub.withArgs('getRdpClients').returns([RDP_CLIENT_NONE]);
|
||||
this.ipcStub.withArgs('getPreferredRdpClient').returns(RDP_CLIENT_NONE);
|
||||
await visit(urls.settings);
|
||||
|
||||
assert.dom(RDP_RECOMMENDED_CLIENT).isVisible();
|
||||
assert
|
||||
.dom(RDP_RECOMMENDED_CLIENT_LINK)
|
||||
.hasAttribute('href', RDP_CLIENT_MSTSC_LINK)
|
||||
.hasText('Remote Desktop Connection (mstsc)');
|
||||
});
|
||||
|
||||
test('preferred RDP client is updated correctly', async function (assert) {
|
||||
const rdpService = this.owner.lookup('service:rdp');
|
||||
this.ipcStub.withArgs('setPreferredRdpClient').resolves();
|
||||
await visit(urls.settings);
|
||||
|
||||
await select(RDP_PREFERRED_CLIENT, RDP_CLIENT_NONE);
|
||||
|
||||
assert.ok(
|
||||
this.ipcStub.calledWith('setPreferredRdpClient', RDP_CLIENT_NONE),
|
||||
);
|
||||
assert.strictEqual(rdpService.preferredRdpClient, RDP_CLIENT_NONE);
|
||||
});
|
||||
});
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
STATUS_SESSION_TERMINATED,
|
||||
} from 'api/models/session';
|
||||
import { TYPE_TARGET_RDP } from 'api/models/target';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Acceptance | projects | targets | index', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -162,6 +163,10 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
|
||||
this.ipcStub.withArgs('isCacheDaemonRunning').returns(true);
|
||||
this.stubCacheDaemonSearch('sessions', 'targets', 'aliases', 'sessions');
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import { TYPE_TARGET_RDP } from 'api/models/target';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Acceptance | projects | targets | target', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -153,6 +154,10 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
this.ipcStub.withArgs('isCacheDaemonRunning').returns(true);
|
||||
this.stubCacheDaemonSearch('sessions', 'targets', 'aliases', 'sessions');
|
||||
|
||||
// mock RDP service calls
|
||||
this.rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(this.rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
test('user can connect to a target with an address', async function (assert) {
|
||||
@@ -630,8 +635,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
});
|
||||
|
||||
test('shows `Open` and `Connect` button for RDP target with preferred client', async function (assert) {
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
rdpService.preferredRdpClient = 'windows-app';
|
||||
this.rdpService.preferredRdpClient = 'windows-app';
|
||||
instances.target.update({ type: TYPE_TARGET_RDP });
|
||||
|
||||
this.stubCacheDaemonSearch();
|
||||
@@ -645,8 +649,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
});
|
||||
|
||||
test('shows "Connect" button for RDP target without preferred client', async function (assert) {
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
rdpService.preferredRdpClient = null;
|
||||
this.rdpService.preferredRdpClient = null;
|
||||
instances.target.update({ type: TYPE_TARGET_RDP });
|
||||
|
||||
this.stubCacheDaemonSearch();
|
||||
@@ -658,8 +661,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
});
|
||||
|
||||
test('clicking `open` button for RDP target triggers launchRdpClient', async function (assert) {
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
rdpService.preferredRdpClient = 'windows-app';
|
||||
this.rdpService.preferredRdpClient = 'windows-app';
|
||||
instances.target.update({ type: TYPE_TARGET_RDP });
|
||||
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
@@ -683,8 +685,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
});
|
||||
|
||||
test('shows `Connect` button for rdp target without preferred client', async function (assert) {
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
rdpService.preferredRdpClient = null;
|
||||
this.rdpService.preferredRdpClient = null;
|
||||
instances.target.update({ type: TYPE_TARGET_RDP });
|
||||
|
||||
this.stubCacheDaemonSearch();
|
||||
@@ -708,8 +709,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
});
|
||||
|
||||
test('shows confirm modal when quickConnectAndLaunchRdp fails', async function (assert) {
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
rdpService.preferredRdpClient = 'windows-app';
|
||||
this.rdpService.preferredRdpClient = 'windows-app';
|
||||
instances.target.update({ type: TYPE_TARGET_RDP });
|
||||
this.stubCacheDaemonSearch();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import WindowMockIPC from '../helpers/window-mock-ipc';
|
||||
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Acceptance | scopes', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -138,6 +139,10 @@ module('Acceptance | scopes', function (hooks) {
|
||||
|
||||
this.ipcStub.withArgs('isCacheDaemonRunning').returns(true);
|
||||
this.stubCacheDaemonSearch('sessions', 'targets', 'aliases', 'sessions');
|
||||
|
||||
// mock RDP service calls
|
||||
let rdpService = this.owner.lookup('service:rdp');
|
||||
sinon.stub(rdpService, 'initialize').resolves();
|
||||
});
|
||||
|
||||
test('visiting index', async function (assert) {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, select } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
import Service from '@ember/service';
|
||||
import {
|
||||
RDP_CLIENT_WINDOWS_APP,
|
||||
RDP_CLIENT_NONE,
|
||||
RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
} from 'desktop/services/rdp';
|
||||
|
||||
module(
|
||||
'Integration | Component | settings-card/preferred-clients',
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks, 'en-us');
|
||||
|
||||
test('it renders protocol and preferred client correctly', async function (assert) {
|
||||
let updatedPreferredRDPClient;
|
||||
this.owner.register(
|
||||
'service:rdp',
|
||||
class extends Service {
|
||||
rdpClients = [RDP_CLIENT_WINDOWS_APP, RDP_CLIENT_NONE];
|
||||
preferredRdpClient = RDP_CLIENT_WINDOWS_APP;
|
||||
setPreferredRdpClient = (value) => {
|
||||
updatedPreferredRDPClient = value;
|
||||
};
|
||||
},
|
||||
);
|
||||
await render(hbs`<SettingsCard::PreferredClients />`);
|
||||
|
||||
assert
|
||||
.dom('.hds-table tbody tr td:first-child')
|
||||
.hasText('Windows RDP', 'Protocol is rendered');
|
||||
assert
|
||||
.dom('select option:checked')
|
||||
.hasText('Windows App', 'Preferred client is selected');
|
||||
|
||||
await select('select', RDP_CLIENT_NONE);
|
||||
|
||||
assert
|
||||
.dom('select option:checked')
|
||||
.hasText('None', 'Preferred client is updated');
|
||||
|
||||
assert.strictEqual(
|
||||
updatedPreferredRDPClient,
|
||||
RDP_CLIENT_NONE,
|
||||
'setPreferredRdpClient is called with correct value',
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders recommended client link only when none is detected', async function (assert) {
|
||||
this.owner.register(
|
||||
'service:rdp',
|
||||
class extends Service {
|
||||
rdpClients = [RDP_CLIENT_NONE];
|
||||
recommendedRdpClient = {
|
||||
name: RDP_CLIENT_WINDOWS_APP,
|
||||
link: RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
};
|
||||
},
|
||||
);
|
||||
await render(hbs`<SettingsCard::PreferredClients />`);
|
||||
|
||||
assert
|
||||
.dom('.hds-table tbody tr td:first-child')
|
||||
.hasText('Windows RDP', 'Protocol is rendered');
|
||||
|
||||
assert
|
||||
.dom('.hds-table tbody tr td:last-child')
|
||||
.includesText(
|
||||
'None detected. We recommend Windows App',
|
||||
'None detected message is rendered',
|
||||
);
|
||||
|
||||
assert
|
||||
.dom('.hds-table tbody tr td:last-child a')
|
||||
.hasAttribute(
|
||||
'href',
|
||||
RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
'windows app download link is rendered',
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -6,6 +6,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'desktop/tests/helpers';
|
||||
import sinon from 'sinon';
|
||||
import {
|
||||
RDP_CLIENT_NONE,
|
||||
RDP_CLIENT_WINDOWS_APP,
|
||||
RDP_CLIENT_MSTSC,
|
||||
RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
RDP_CLIENT_MSTSC_LINK,
|
||||
} from 'desktop/services/rdp';
|
||||
|
||||
module('Unit | Service | rdp', function (hooks) {
|
||||
setupTest(hooks);
|
||||
@@ -17,12 +24,19 @@ module('Unit | Service | rdp', function (hooks) {
|
||||
ipcService = this.owner.lookup('service:ipc');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test('getRdpClients sets to fallback value on error', async function (assert) {
|
||||
sinon.stub(ipcService, 'invoke').withArgs('getRdpClients').rejects();
|
||||
const ipcStub = sinon.stub(ipcService, 'invoke');
|
||||
ipcStub.withArgs('getRdpClients').rejects();
|
||||
ipcStub.withArgs('checkOS').resolves({ isMac: true });
|
||||
await service.getRdpClients();
|
||||
|
||||
assert.deepEqual(
|
||||
service.rdpClients,
|
||||
['none'],
|
||||
[RDP_CLIENT_NONE],
|
||||
'rdpClients fallback is set correctly',
|
||||
);
|
||||
});
|
||||
@@ -33,9 +47,10 @@ module('Unit | Service | rdp', function (hooks) {
|
||||
.withArgs('getPreferredRdpClient')
|
||||
.rejects();
|
||||
await service.getPreferredRdpClient();
|
||||
|
||||
assert.strictEqual(
|
||||
service.preferredRdpClient,
|
||||
'none',
|
||||
RDP_CLIENT_NONE,
|
||||
'preferredRdpClient fallback is set correctly',
|
||||
);
|
||||
});
|
||||
@@ -43,13 +58,44 @@ module('Unit | Service | rdp', function (hooks) {
|
||||
test('setPreferredRdpClient sets to fallback value on error', async function (assert) {
|
||||
sinon
|
||||
.stub(ipcService, 'invoke')
|
||||
.withArgs('setPreferredRdpClient', 'mstsc')
|
||||
.withArgs('setPreferredRdpClient', RDP_CLIENT_MSTSC)
|
||||
.rejects();
|
||||
await service.setPreferredRdpClient('mstsc');
|
||||
await service.setPreferredRdpClient(RDP_CLIENT_MSTSC);
|
||||
|
||||
assert.strictEqual(
|
||||
service.preferredRdpClient,
|
||||
'none',
|
||||
RDP_CLIENT_NONE,
|
||||
'preferredRdpClient fallback is set correctly',
|
||||
);
|
||||
});
|
||||
|
||||
test('sets recommendedRdpClient correctly based on OS', async function (assert) {
|
||||
sinon
|
||||
.stub(ipcService, 'invoke')
|
||||
.withArgs('checkOS')
|
||||
.resolves({ isWindows: true });
|
||||
await service.getRecommendedRdpClient();
|
||||
|
||||
assert.deepEqual(
|
||||
service.recommendedRdpClient,
|
||||
{
|
||||
name: RDP_CLIENT_MSTSC,
|
||||
link: RDP_CLIENT_MSTSC_LINK,
|
||||
},
|
||||
'recommendedRdpClient is set correctly for windows',
|
||||
);
|
||||
|
||||
ipcService.invoke.withArgs('checkOS').resolves({ isMac: true });
|
||||
service.recommendedRdpClient = null;
|
||||
await service.getRecommendedRdpClient();
|
||||
|
||||
assert.deepEqual(
|
||||
service.recommendedRdpClient,
|
||||
{
|
||||
name: RDP_CLIENT_WINDOWS_APP,
|
||||
link: RDP_CLIENT_WINDOWS_APP_LINK,
|
||||
},
|
||||
'recommendedRdpClient is set correctly for mac',
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user