diff --git a/e2e-tests/desktop/tests/targets.spec.js b/e2e-tests/desktop/tests/targets.spec.js index a5c98ad00..b50cd6942 100644 --- a/e2e-tests/desktop/tests/targets.spec.js +++ b/e2e-tests/desktop/tests/targets.spec.js @@ -166,13 +166,17 @@ test.describe('Targets tests', () => { await authedPage .getByRole('link', { name: targetWithTwoHosts.name }) .click(); - await authedPage.getByRole('button', { name: 'Connect' }).click(); - await expect( - authedPage.getByRole('heading', { name: 'Choose a Host' }), - ).toBeVisible(); - - await authedPage.getByRole('button', { name: host }).click(); + // eslint-disable-next-line playwright/no-conditional-in-test + if (host === 'Quick Connect') { + await authedPage.getByRole('button', { name: host }).click(); + } else { + await authedPage + .getByRole('row') + .filter({ hasText: host }) + .getByRole('button', { name: 'Connect' }) + .click(); + } await expect( authedPage.getByRole('heading', { name: 'Sessions' }), diff --git a/ui/desktop/app/components/target/host-modal/index.hbs b/ui/desktop/app/components/target/host-modal/index.hbs deleted file mode 100644 index 7f0dacdaf..000000000 --- a/ui/desktop/app/components/target/host-modal/index.hbs +++ /dev/null @@ -1,75 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -}} - -{{#if @showModal}} - - - {{t 'resources.session.actions.host'}} - - - - {{#each @hosts as |host|}} - - - - - - - {{host.displayName}} - - {{#if host.isStatic}} - - {{host.address}} - - {{/if}} - {{#if host.isPlugin}} - {{#if host.isUnknown}} - - {{else}} - - {{/if}} - {{/if}} - {{#if host.description}} - - {{host.description}} - - {{/if}} - - - - - - {{/each}} - - - - - - -{{/if}} \ No newline at end of file diff --git a/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js b/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js index 2fb9b2e97..aeaf6728c 100644 --- a/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js +++ b/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js @@ -25,11 +25,6 @@ export default class ScopesScopeProjectsTargetsTargetController extends Controll // =methods - @action - toggleModal(value) { - this.isConnecting = value; - } - /** * Connect method that calls parent connect method and handles * connection errors unique to this route @@ -48,45 +43,9 @@ export default class ScopesScopeProjectsTargetsTargetController extends Controll .then(() => this.connect(target, host)) .catch(() => { // Reset the flag as this was user initiated and we're not - // in a transition or have a host modal open + // in a transition this.isConnectionError = false; }); } } - - /** - * Establish a session to current target. - * @param {TargetModel} target - * @param {HostModel} host - * hostConnect is only called when making a connection with a host and ensures that the host modal is automatically closed in the case of a connection error. - */ - @action - async hostConnect(target, host) { - await this.connect(target, host); - - if (this.isConnectionError) { - this.toggleModal(false); - this.isConnectionError = false; - } - } - - /** - * Determine if we show host modal or quick connect based on target attributes. - * @param {TargetModel} model - * @param {function} toggleModal - */ - @action - async preConnect(model) { - /** - * if hosts length is 1 or less we will try to - * connect, even if there is no address on the target and - * rely on the CLI to give the user the proper error or if - * there are 2 or more hosts we show the modal for host selection - */ - if (model.hosts.length <= 1) { - await this.connect(model.target); - } else { - this.toggleModal(true); - } - } } diff --git a/ui/desktop/app/styles/app.scss b/ui/desktop/app/styles/app.scss index 8a4e6b7fb..e4b569985 100644 --- a/ui/desktop/app/styles/app.scss +++ b/ui/desktop/app/styles/app.scss @@ -398,43 +398,6 @@ margin-top: 0.75rem; } -// Host Modal -.host-list-container { - border: 1px solid var(--token-color-border-primary); - padding: 0.25rem; - border-radius: 6px; -} - -.host-container { - button { - background: unset; - border: unset; - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - padding: 0.5rem; - - .host-info-container { - display: flex; - gap: 0.75rem; - - .host-info { - text-align: left; - } - } - - &:hover { - background-color: var(--token-color-page-faint); - cursor: pointer; - } - } - - .host-arrow { - color: var(--token-color-foreground-faint); - } -} - // Unsupported controller modal .unsupported-modal-body { display: grid; diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs index 65f7ebec9..72f73c2e6 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs @@ -22,34 +22,73 @@ {{#if (can 'connect target' @model.target)}} {{/if}} - - - - - + @valign='middle' + > + <:body as |B|> + + + + {{B.data.displayName}} + + + {{B.data.description}} + + + + + {{B.data.address}} + + + + + + + + + + + {{else}} + + + + + {{/if}} {{#if @model.target.aliases}} diff --git a/ui/desktop/tests/acceptance/projects/targets/index-test.js b/ui/desktop/tests/acceptance/projects/targets/index-test.js index 6c4ca1f6a..df68f8078 100644 --- a/ui/desktop/tests/acceptance/projects/targets/index-test.js +++ b/ui/desktop/tests/acceptance/projects/targets/index-test.js @@ -32,7 +32,6 @@ module('Acceptance | projects | targets | index', function (hooks) { const TARGET_DETAILS_ROUTE_NAME = 'scopes.scope.projects.targets.target.index'; const HDS_DIALOG_MODAL = '.hds-modal'; - const CHOOSE_HOST_MODAL = '[data-test-host-modal]'; const HDS_DIALOG_MODAL_BUTTONS = '.hds-modal__footer button'; const HDS_DIALOG_RETRY_BUTTON = '.hds-modal__footer .hds-button--color-primary'; @@ -275,7 +274,6 @@ module('Acceptance | projects | targets | index', function (hooks) { await click(`[data-test-targets-connect-button="${instances.target.id}"]`); assert.dom(HDS_DIALOG_MODAL).isVisible(); - assert.dom(CHOOSE_HOST_MODAL).doesNotExist(); assert.strictEqual(currentRouteName(), TARGET_DETAILS_ROUTE_NAME); }); diff --git a/ui/desktop/tests/acceptance/projects/targets/target-test.js b/ui/desktop/tests/acceptance/projects/targets/target-test.js index 6a03a78b5..21b991617 100644 --- a/ui/desktop/tests/acceptance/projects/targets/target-test.js +++ b/ui/desktop/tests/acceptance/projects/targets/target-test.js @@ -16,9 +16,12 @@ module('Acceptance | projects | targets | target', function (hooks) { setupMirage(hooks); setupStubs(hooks); + const TARGET_RESOURCE_LINK = (id) => `[data-test-visit-target="${id}"]`; + const TARGET_TABLE_CONNECT_BUTTON = (id) => + `[data-test-targets-connect-button="${id}"]`; const TARGET_CONNECT_BUTTON = '[data-test-target-detail-connect-button]'; - const TARGET_QUICK_CONNECT_BUTTON = '[data-test-host-quick-connect]'; - const TARGET_HOST_CONNECT_BUTTON = '[data-test-host-connect]'; + const TARGET_HOST_SOURCE_CONNECT_BUTTON = (id) => + `[data-test-target-connect-button=${id}]`; const APP_STATE_TITLE = '.hds-application-state__title'; const HDS_DIALOG_MODAL = '.hds-modal'; const HDS_DIALOG_MODAL_BUTTONS = '.hds-modal__footer button'; @@ -228,8 +231,29 @@ module('Acceptance | projects | targets | target', function (hooks) { assert.dom(APP_STATE_TITLE).hasText('Connected'); }); - test('user can connect to a target with two hosts using host modal', async function (assert) { - assert.expect(3); + test('user can see host source table when visiting a target via resource link', async function (assert) { + const targetId = instances.targetWithTwoHosts.id; + + await visit(urls.targets); + await click(TARGET_RESOURCE_LINK(targetId)); + + assert.dom('tbody tr').exists({ count: 2 }); + }); + + test('user can see host source table when visiting a target via connect button', async function (assert) { + const targetId = instances.targetWithTwoHosts.id; + + await visit(urls.targets); + await click(TARGET_TABLE_CONNECT_BUTTON(targetId)); + + assert.dom('tbody tr').exists({ count: 2 }); + }); + + test('user can connect to a target with two hosts using host source table', async function (assert) { + const hostId = + instances.targetWithTwoHosts.hostSets.models[0].hosts.models[0].id; + + assert.expect(2); this.ipcStub.withArgs('cliExists').returns(true); this.ipcStub.withArgs('connect').returns({ session_id: instances.session.id, @@ -241,17 +265,35 @@ module('Acceptance | projects | targets | target', function (hooks) { await click(`[href="${urls.targetWithTwoHosts}"]`); - assert.dom(APP_STATE_TITLE).hasText('Connect for more info'); + assert.dom('tbody tr').exists({ count: 2 }); - await click(TARGET_CONNECT_BUTTON); - - assert.dom(TARGET_HOST_CONNECT_BUTTON).exists({ count: 2 }); - - await click(TARGET_QUICK_CONNECT_BUTTON); + await click(TARGET_HOST_SOURCE_CONNECT_BUTTON(hostId)); assert.dom(APP_STATE_TITLE).hasText('Connected'); }); + test.each( + 'user sees correct button text for a', + { + 'target with address': { target: 'target', expectedText: 'Connect' }, + 'target with one host': { + target: 'targetWithOneHost', + expectedText: 'Connect', + }, + 'target with two hosts': { + target: 'targetWithTwoHosts', + expectedText: 'Quick Connect', + }, + }, + async function (assert, input) { + await visit(urls.targets); + + await click(`[href="${urls[input.target]}"]`); + + assert.dom(TARGET_CONNECT_BUTTON).hasText(input.expectedText); + }, + ); + test('user can visit target details screen without read permissions for host-set', async function (assert) { assert.expect(1); this.server.get('/host-sets/:id', () => new Response(403)); diff --git a/ui/desktop/tests/integration/components/target/host-modal-test.js b/ui/desktop/tests/integration/components/target/host-modal-test.js deleted file mode 100644 index 473e9ac50..000000000 --- a/ui/desktop/tests/integration/components/target/host-modal-test.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'desktop/tests/helpers'; -import { render, click } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { setupIntl } from 'ember-intl/test-support'; - -module('Integration | Component | target/host-modal', function (hooks) { - setupRenderingTest(hooks); - setupIntl(hooks, 'en-us'); - - let store; - let target; - let hostA; - let hostB; - let connect; - let toggleModal; - - hooks.beforeEach(function () { - connect = () => { - this.set('called', true); - }; - toggleModal = () => { - this.set('called', true); - }; - store = this.owner.lookup('service:store'); - target = store.createRecord('target', { - name: 'User', - }); - hostA = store.createRecord('host', { - name: 'Host A', - compositeType: 'static', - address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', - }); - hostB = store.createRecord('host', { - name: 'Host B', - compositeType: 'static', - description: 'Description of host B', - address: '188e:68a9:b342:c05c:2595:499c:759f:2f46', - }); - }); - - test('it renders', async function (assert) { - assert.expect(1); - this.set('connect', connect); - this.set('toggleModal', toggleModal); - this.set('hosts', [hostA, hostB]); - this.set('target', target); - - await render(hbs``); - - assert.dom('.hds-modal__tagline').hasText(target.displayName); - }); - - test('it closes model when dismissed', async function (assert) { - assert.expect(2); - const toggleModal = () => { - this.set('showModal', false); - }; - this.set('connect', connect); - this.set('toggleModal', toggleModal); - this.set('showModal', true); - this.set('hosts', [hostA, hostB]); - this.set('target', target); - - await render(hbs``); - - assert.dom('.hds-modal__tagline').hasText(target.displayName); - - await click('.hds-dismiss-button'); - - assert.dom('.hds-modal__tagline').doesNotExist(); - }); - - test('it calls connect when Quick Connect button clicked', async function (assert) { - assert.expect(1); - this.set('connect', connect); - this.set('toggleModal', toggleModal); - this.set('hosts', [hostA, hostB]); - this.set('target', target); - - await render(hbs``); - - await click('[data-test-host-quick-connect]'); - - assert.true(this.called); - }); - - test('it calls connect when host item is clicked', async function (assert) { - assert.expect(1); - this.set('connect', connect); - this.set('toggleModal', toggleModal); - this.set('hosts', [hostA, hostB]); - this.set('target', target); - - await render(hbs``); - - await click('[data-test-host-connect]'); - - assert.true(this.called); - }); -});