Skip to content

Commit 7e284c8

Browse files
authored
Icu 17881 UI desktop add rdp launch button in target detail view (#3038)
1 parent 199c56f commit 7e284c8

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
@@ -19,6 +19,7 @@ import { authenticateSession } from 'ember-simple-auth/test-support';
1919
import WindowMockIPC from '../../../helpers/window-mock-ipc';
2020
import setupStubs from 'api/test-support/handlers/cache-daemon-search';
2121
import { setRunOptions } from 'ember-a11y-testing/test-support';
22+
import { TYPE_TARGET_RDP } from 'api/models/target';
2223

2324
module('Acceptance | projects | targets | target', function (hooks) {
2425
setupApplicationTest(hooks);
@@ -28,6 +29,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
2829
const TARGET_RESOURCE_LINK = (id) => `[data-test-visit-target="${id}"]`;
2930
const TARGET_TABLE_CONNECT_BUTTON = (id) =>
3031
`[data-test-targets-connect-button="${id}"]`;
32+
const TARGET_OPEN_BUTTON = `[data-test-target-detail-open-button]`;
3133
const TARGET_CONNECT_BUTTON = '[data-test-target-detail-connect-button]';
3234
const TARGET_HOST_SOURCE_CONNECT_BUTTON = (id) =>
3335
`[data-test-target-connect-button=${id}]`;
@@ -576,6 +578,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
576578
assert.strictEqual(currentURL(), urls.targetWithOneHost);
577579
assert.dom('.aliases').exists();
578580
});
581+
579582
test('user can connect to a target without read permissions for host-set', async function (assert) {
580583
setRunOptions({
581584
rules: {
@@ -631,4 +634,99 @@ module('Acceptance | projects | targets | target', function (hooks) {
631634

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

0 commit comments

Comments
 (0)