Skip to content

Commit 0dbc236

Browse files
committed
Icu 17881 UI desktop add rdp launch button in target detail view (#3038)
1 parent e5d7f85 commit 0dbc236

File tree

3 files changed

+139
-11
lines changed

3 files changed

+139
-11
lines changed

ui/desktop/app/controllers/scopes/scope/projects/targets/target.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default class ScopesScopeProjectsTargetsTargetController extends Controll
1515

1616
@service store;
1717
@service confirm;
18+
@service rdp;
1819

1920
// =attributes
2021

@@ -48,4 +49,13 @@ export default class ScopesScopeProjectsTargetsTargetController extends Controll
4849
});
4950
}
5051
}
52+
53+
/**
54+
* Launch method that calls parent quickConnectAndLaunchRdp method
55+
* @param {TargetModel} target
56+
*/
57+
@action
58+
async connectAndLaunchRdp(target) {
59+
await this.targets.quickConnectAndLaunchRdp(target);
60+
}
5161
}

ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,37 @@
1515
</PH.Title>
1616
{{#if (can 'connect target' @model.target)}}
1717
<PH.Actions>
18-
<Hds::Button
19-
data-test-target-detail-connect-button
20-
@text={{if
21-
(gt @model.hosts.length 1)
22-
(t 'resources.session.actions.quick-connect')
23-
(t 'resources.session.actions.connect')
24-
}}
25-
@icon='entry-point'
26-
@iconPosition='trailing'
27-
{{on 'click' (fn this.connect @model.target)}}
28-
/>
18+
{{#if (and @model.target.isRDP this.rdp.isPreferredRdpClientSet)}}
19+
<Hds::Button
20+
data-test-target-detail-connect-button
21+
@text={{if
22+
(gt @model.hosts.length 1)
23+
(t 'resources.session.actions.quick-connect')
24+
(t 'resources.session.actions.connect')
25+
}}
26+
@color='secondary'
27+
{{on 'click' (fn this.connect @model.target)}}
28+
/>
29+
<Hds::Button
30+
data-test-target-detail-open-button
31+
@text={{t 'actions.open'}}
32+
@icon='external-link'
33+
@iconPosition='trailing'
34+
{{on 'click' (fn this.connectAndLaunchRdp @model.target)}}
35+
/>
36+
{{else}}
37+
<Hds::Button
38+
data-test-target-detail-connect-button
39+
@text={{if
40+
(gt @model.hosts.length 1)
41+
(t 'resources.session.actions.quick-connect')
42+
(t 'resources.session.actions.connect')
43+
}}
44+
@icon='entry-point'
45+
@iconPosition='trailing'
46+
{{on 'click' (fn this.connect @model.target)}}
47+
/>
48+
{{/if}}
2949
</PH.Actions>
3050
{{/if}}
3151
</Hds::PageHeader>

ui/desktop/tests/acceptance/projects/targets/target-test.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { setupApplicationTest } from 'desktop/tests/helpers';
1717
import WindowMockIPC from '../../../helpers/window-mock-ipc';
1818
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
1919
import { setRunOptions } from 'ember-a11y-testing/test-support';
20+
import { TYPE_TARGET_RDP } from 'api/models/target';
2021

2122
module('Acceptance | projects | targets | target', function (hooks) {
2223
setupApplicationTest(hooks);
@@ -25,6 +26,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
2526
const TARGET_RESOURCE_LINK = (id) => `[data-test-visit-target="${id}"]`;
2627
const TARGET_TABLE_CONNECT_BUTTON = (id) =>
2728
`[data-test-targets-connect-button="${id}"]`;
29+
const TARGET_OPEN_BUTTON = `[data-test-target-detail-open-button]`;
2830
const TARGET_CONNECT_BUTTON = '[data-test-target-detail-connect-button]';
2931
const TARGET_HOST_SOURCE_CONNECT_BUTTON = (id) =>
3032
`[data-test-target-connect-button=${id}]`;
@@ -570,6 +572,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
570572
assert.strictEqual(currentURL(), urls.targetWithOneHost);
571573
assert.dom('.aliases').exists();
572574
});
575+
573576
test('user can connect to a target without read permissions for host-set', async function (assert) {
574577
setRunOptions({
575578
rules: {
@@ -625,4 +628,99 @@ module('Acceptance | projects | targets | target', function (hooks) {
625628

626629
assert.dom(APP_STATE_TITLE).hasText('Connected');
627630
});
631+
632+
test('shows `Open` and `Connect` button for RDP target with preferred client', async function (assert) {
633+
let rdpService = this.owner.lookup('service:rdp');
634+
rdpService.preferredRdpClient = 'windows-app';
635+
instances.target.update({ type: TYPE_TARGET_RDP });
636+
637+
this.stubCacheDaemonSearch();
638+
639+
await visit(urls.target);
640+
641+
assert.dom(TARGET_OPEN_BUTTON).exists();
642+
assert.dom(TARGET_OPEN_BUTTON).hasText('Open');
643+
assert.dom(TARGET_CONNECT_BUTTON).exists();
644+
assert.dom(TARGET_CONNECT_BUTTON).hasText('Connect');
645+
});
646+
647+
test('shows "Connect" button for RDP target without preferred client', async function (assert) {
648+
let rdpService = this.owner.lookup('service:rdp');
649+
rdpService.preferredRdpClient = null;
650+
instances.target.update({ type: TYPE_TARGET_RDP });
651+
652+
this.stubCacheDaemonSearch();
653+
654+
await visit(urls.target);
655+
656+
assert.dom(TARGET_CONNECT_BUTTON).exists();
657+
assert.dom(TARGET_CONNECT_BUTTON).hasText('Connect');
658+
});
659+
660+
test('clicking `open` button for RDP target triggers launchRdpClient', async function (assert) {
661+
let rdpService = this.owner.lookup('service:rdp');
662+
rdpService.preferredRdpClient = 'windows-app';
663+
instances.target.update({ type: TYPE_TARGET_RDP });
664+
665+
this.ipcStub.withArgs('cliExists').returns(true);
666+
this.ipcStub.withArgs('connect').returns({
667+
session_id: instances.session.id,
668+
address: 'a_123',
669+
port: 'p_123',
670+
protocol: 'rdp',
671+
});
672+
this.stubCacheDaemonSearch();
673+
this.ipcStub.withArgs('launchRdpClient').resolves();
674+
675+
const confirmService = this.owner.lookup('service:confirm');
676+
confirmService.enabled = true;
677+
678+
await visit(urls.target);
679+
680+
await click(TARGET_OPEN_BUTTON);
681+
682+
assert.ok(this.ipcStub.calledWith('launchRdpClient', instances.session.id));
683+
});
684+
685+
test('shows `Connect` button for rdp target without preferred client', async function (assert) {
686+
let rdpService = this.owner.lookup('service:rdp');
687+
rdpService.preferredRdpClient = null;
688+
instances.target.update({ type: TYPE_TARGET_RDP });
689+
690+
this.stubCacheDaemonSearch();
691+
this.ipcStub.withArgs('cliExists').returns(true);
692+
this.ipcStub.withArgs('connect').returns({
693+
session_id: instances.session.id,
694+
address: 'a_123',
695+
port: 'p_123',
696+
protocol: 'rdp',
697+
});
698+
await visit(urls.target);
699+
700+
assert.dom(TARGET_CONNECT_BUTTON).exists();
701+
assert.dom(TARGET_CONNECT_BUTTON).hasText('Connect');
702+
assert.dom(TARGET_OPEN_BUTTON).doesNotExist();
703+
704+
await click(TARGET_CONNECT_BUTTON);
705+
706+
assert.ok(this.ipcStub.calledWith('connect'));
707+
assert.notOk(this.ipcStub.calledWith('launchRdpClient'));
708+
});
709+
710+
test('shows confirm modal when quickConnectAndLaunchRdp fails', async function (assert) {
711+
let rdpService = this.owner.lookup('service:rdp');
712+
rdpService.preferredRdpClient = 'windows-app';
713+
instances.target.update({ type: TYPE_TARGET_RDP });
714+
this.stubCacheDaemonSearch();
715+
716+
const confirmService = this.owner.lookup('service:confirm');
717+
confirmService.enabled = true;
718+
719+
await visit(urls.target);
720+
721+
await click('[data-test-target-detail-open-button]');
722+
723+
// The modal should be visible because cliExists was not stubbed, and the connection failed
724+
assert.dom(HDS_DIALOG_MODAL).isVisible();
725+
});
628726
});

0 commit comments

Comments
 (0)