From 8c05e57ac1fdc27ea74040e2dfc35192ac6d067b Mon Sep 17 00:00:00 2001 From: Valeriia Ruban Date: Fri, 9 Feb 2024 10:23:13 -0800 Subject: [PATCH] Cc 7147 link to hcp modal (#20474) * add link hcp modal component * integrate modal with SideNav and link to hcp banner --------- Co-authored-by: Chris Hut --- .changelog/20474.txt | 3 + .../app/components/hcp-nav-item/index.js | 4 +- .../components/link-to-hcp-banner/index.js | 4 +- .../components/link-to-hcp-modal/index.hbs | 100 +++++++++ .../app/components/link-to-hcp-modal/index.js | 64 ++++++ .../components/link-to-hcp-modal/index.scss | 26 +++ .../consul-ui/app/controllers/application.js | 1 + .../app/helpers/hcp-authentication-link.js | 43 ++++ .../consul-ui/app/services/hcp-link-modal.js | 23 +++ .../consul-ui/app/styles/components.scss | 1 + .../consul-ui/app/templates/application.hbs | 83 ++++---- .../tests/acceptance/link-to-hcp-test.js | 37 ++++ .../components/link-to-hcp-modal-test.js | 192 ++++++++++++++++++ .../helpers/hcp-authentication-link-test.js | 80 ++++++++ 14 files changed, 619 insertions(+), 42 deletions(-) create mode 100644 .changelog/20474.txt create mode 100644 ui/packages/consul-ui/app/components/link-to-hcp-modal/index.hbs create mode 100644 ui/packages/consul-ui/app/components/link-to-hcp-modal/index.js create mode 100644 ui/packages/consul-ui/app/components/link-to-hcp-modal/index.scss create mode 100644 ui/packages/consul-ui/app/helpers/hcp-authentication-link.js create mode 100644 ui/packages/consul-ui/app/services/hcp-link-modal.js create mode 100644 ui/packages/consul-ui/tests/integration/components/link-to-hcp-modal-test.js create mode 100644 ui/packages/consul-ui/tests/integration/helpers/hcp-authentication-link-test.js diff --git a/.changelog/20474.txt b/.changelog/20474.txt new file mode 100644 index 0000000000..8c0d514b86 --- /dev/null +++ b/.changelog/20474.txt @@ -0,0 +1,3 @@ +```release-note:breaking-change +ui: Adds a "Link to HCP Consul Central" modal with integration to side-nav and link to HCP banner. There will be an option to disable the Link to HCP banner from the UI in a follow-up release. +``` \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/hcp-nav-item/index.js b/ui/packages/consul-ui/app/components/hcp-nav-item/index.js index 005d32f4b9..1de939d356 100644 --- a/ui/packages/consul-ui/app/components/hcp-nav-item/index.js +++ b/ui/packages/consul-ui/app/components/hcp-nav-item/index.js @@ -14,6 +14,7 @@ import { action } from '@ember/object'; export default class HcpLinkItemComponent extends Component { @service env; @service('hcp-link-status') hcpLinkStatus; + @service('hcp-link-modal') hcpLinkModal; get alreadyLinked() { return this.args.linkData?.isLinked; @@ -51,6 +52,7 @@ export default class HcpLinkItemComponent extends Component { @action onLinkToConsulCentral() { - // TODO: https://hashicorp.atlassian.net/browse/CC-7147 open the modal + this.hcpLinkModal.setResourceId(this.args.linkData?.resourceId); + this.hcpLinkModal.show(); } } diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js index 9416cfbafb..3f96ac261f 100644 --- a/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js +++ b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js @@ -9,6 +9,7 @@ import { inject as service } from '@ember/service'; export default class LinkToHcpBannerComponent extends Component { @service('hcp-link-status') hcpLinkStatus; + @service('hcp-link-modal') hcpLinkModal; @service('env') env; get notLinked() { @@ -21,6 +22,7 @@ export default class LinkToHcpBannerComponent extends Component { } @action onClusterLink() { - // TODO: CC-7147: Open simplified modal + this.hcpLinkModal.setResourceId(this.args.linkData?.resourceId); + this.hcpLinkModal.show(); } } diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.hbs b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.hbs new file mode 100644 index 0000000000..bd29b2423f --- /dev/null +++ b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.hbs @@ -0,0 +1,100 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +}} + + + + Link to HCP Consul Central + + + + Select cluster access mode before linking + Control the level of access that HCP Consul Central has to your linked cluster. + Learn more + + + + Read/write + HCP Consul Central can perform write operations on your cluster (i.e. cluster peering). + + + + Read-only + HCP Consul Central can only read information from your cluster. Read-only requires an ACL token + with the “builtin/global-read-only” policy in the next step. + + + + {{#if (and this.isReadOnlyAccessLevelSelected (can "create tokens"))}} + + {{/if}} + + + + + + + + + \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.js b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.js new file mode 100644 index 0000000000..a7be3b3ddf --- /dev/null +++ b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export const ACCESS_LEVEL = { + GLOBALREADONLY: 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_ONLY', + GLOBALREADWRITE: 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE', +}; + +export default class LinkToHcpModalComponent extends Component { + @service('repository/token') tokenRepo; + @service('repository/policy') policyRepo; + @service('hcp-link-modal') hcpLinkModal; + @service('router') router; + + @tracked + token = ''; + @tracked + accessLevel = ACCESS_LEVEL.GLOBALREADWRITE; + @tracked + isGeneratingToken = false; + AccessLevel = ACCESS_LEVEL; + + get isReadOnlyAccessLevelSelected() { + return this.accessLevel === this.AccessLevel.GLOBALREADONLY; + } + + get isTokenGenerated() { + return this.token && this.token.length > 0; + } + + deactivateModal = () => { + this.hcpLinkModal.hide(); + }; + + onGenerateTokenClicked = (policy) => { + this.isGeneratingToken = true; + let token = this.tokenRepo.create({ + Datacenter: this.args.dc, + Partition: this.args.partition, + Namespace: this.args.nspace, + Policies: [policy.data], + }); + this.tokenRepo.persist(token, event).then((token) => { + this.token = token.SecretID; + this.isGeneratingToken = false; + }); + }; + + @action + onCancel() { + this.deactivateModal(); + } + @action + onAccessModeChanged({ target }) { + this.accessLevel = target.value; + } +} diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.scss b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.scss new file mode 100644 index 0000000000..f28aa8ecde --- /dev/null +++ b/ui/packages/consul-ui/app/components/link-to-hcp-modal/index.scss @@ -0,0 +1,26 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +.link-to-hcp-modal { + &__generate-token { + display: flex; + flex-direction: column; + gap: 16px; + margin-top: 8px; + + &__copy-card { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: start; + gap: 10px; + padding: 16px 24px; + + &__token { + margin-top: 8px; + } + } + } +} diff --git a/ui/packages/consul-ui/app/controllers/application.js b/ui/packages/consul-ui/app/controllers/application.js index 9180761dcd..fe67622a5c 100644 --- a/ui/packages/consul-ui/app/controllers/application.js +++ b/ui/packages/consul-ui/app/controllers/application.js @@ -13,6 +13,7 @@ export default class ApplicationController extends Controller { @service('router') router; @service('store') store; @service('feedback') feedback; + @service('hcp-link-modal') hcpLinkModal; // TODO: We currently do this in the controller instead of the router // as the nspace and dc variables aren't available directly on the Route diff --git a/ui/packages/consul-ui/app/helpers/hcp-authentication-link.js b/ui/packages/consul-ui/app/helpers/hcp-authentication-link.js new file mode 100644 index 0000000000..08bc205e74 --- /dev/null +++ b/ui/packages/consul-ui/app/helpers/hcp-authentication-link.js @@ -0,0 +1,43 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Helper from '@ember/component/helper'; +import { inject as service } from '@ember/service'; + +/** + * A resourceId Looks like: + * organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api + * organization/${organizationId}/project/${projectId}/hashicorp.consul.global-network-manager.cluster/${clusterName} + * + * A HCP URL looks like: + * https://portal.cloud.hashicorp.com/services/consul/clusters/self-managed/link-existing?cluster_name=test-from-api&cluster_version=1.18.0&cluster_access_mode=CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE&redirect_url=localhost:8500/services + */ +export const HCP_PREFIX = + 'https://portal.cloud.hashicorp.com/services/consul/clusters/self-managed/link-existing'; +export default class hcpAuthenticationLink extends Helper { + @service('env') env; + compute([resourceId, accessMode], hash) { + let url = new URL(HCP_PREFIX); + const clusterVersion = this.env.var('CONSUL_VERSION'); + + // if resourceId is empty, we still might want the user to get to the HCP sign-in page + if (resourceId) { + // Array looks like: ["organization", organizationId, "project", projectId, "hashicorp.consul.global-network-manager.cluster", "Cluster Id"] + const [, , , , , clusterName] = resourceId.split('/'); + if (clusterName) { + url.searchParams.append('cluster_name', clusterName); + } + } + + if (clusterVersion) { + url.searchParams.append('cluster_version', clusterVersion); + } + if (accessMode) { + url.searchParams.append('cluster_access_mode', accessMode); + } + + return url.toString(); + } +} diff --git a/ui/packages/consul-ui/app/services/hcp-link-modal.js b/ui/packages/consul-ui/app/services/hcp-link-modal.js new file mode 100644 index 0000000000..3938fa4655 --- /dev/null +++ b/ui/packages/consul-ui/app/services/hcp-link-modal.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Service from '@ember/service'; +import { tracked } from '@glimmer/tracking'; + +export default class HcpLinkModalService extends Service { + @tracked isModalVisible = false; + @tracked resourceId = null; + + show(hcpLinkData) { + this.isModalVisible = true; + } + + hide() { + this.isModalVisible = false; + } + setResourceId(resourceId) { + this.resourceId = resourceId; + } +} diff --git a/ui/packages/consul-ui/app/styles/components.scss b/ui/packages/consul-ui/app/styles/components.scss index f4baa2ca74..104092c9a1 100644 --- a/ui/packages/consul-ui/app/styles/components.scss +++ b/ui/packages/consul-ui/app/styles/components.scss @@ -74,6 +74,7 @@ @import 'consul-ui/components/tab-nav'; @import 'consul-ui/components/search-bar'; @import 'consul-ui/components/copyable-code'; +@import 'consul-ui/components/link-to-hcp-modal'; @import 'consul-ui/components/consul/loader'; @import 'consul-ui/components/consul/tomography/graph'; diff --git a/ui/packages/consul-ui/app/templates/application.hbs b/ui/packages/consul-ui/app/templates/application.hbs index b0c9054ce6..3ad3ca2d5c 100644 --- a/ui/packages/consul-ui/app/templates/application.hbs +++ b/ui/packages/consul-ui/app/templates/application.hbs @@ -93,47 +93,50 @@ as |dc dcs| }} {{#if (and (gt dc.Name.length 0) dcs)}} - - {{! figure out our current DC and convert it to a model }} - - {{#if dc.data}} - - - {{#if error}} - {{! If we got an error from anything, show an error page }} - - {{else}} - {{! Otherwise show the rest of the app}} - - {{outlet}} - - - {{! loading component for when we need it}} - - {{/if}} - - + {{#if this.hcpLinkModal.isModalVisible}} + {{/if}} - + + {{! figure out our current DC and convert it to a model }} + + {{#if dc.data}} + + + {{#if error}} + {{! If we got an error from anything, show an error page }} + + {{else}} + {{! Otherwise show the rest of the app}} + + {{outlet}} + + + {{! loading component for when we need it}} + + {{/if}} + + + {{/if}} + {{/if}} {{/let}} diff --git a/ui/packages/consul-ui/tests/acceptance/link-to-hcp-test.js b/ui/packages/consul-ui/tests/acceptance/link-to-hcp-test.js index 3253304610..9a71e4c600 100644 --- a/ui/packages/consul-ui/tests/acceptance/link-to-hcp-test.js +++ b/ui/packages/consul-ui/tests/acceptance/link-to-hcp-test.js @@ -6,15 +6,29 @@ import { module, test } from 'qunit'; import { click, visit } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; +import HcpLinkModalService from 'consul-ui/services/hcp-link-modal'; const bannerSelector = '[data-test-link-to-hcp-banner]'; const linkToHcpSelector = '[data-test-link-to-hcp]'; +const linkToHcpBannerButtonSelector = '[data-test-link-to-hcp-banner-button]'; +const linkToHcpModalSelector = '[data-test-link-to-hcp-modal]'; +const linkToHcpModalCancelButtonSelector = '[data-test-link-to-hcp-modal-cancel-button]'; module('Acceptance | link to hcp', function (hooks) { setupApplicationTest(hooks); + const correctResourceId = + 'organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api'; hooks.beforeEach(function () { // clear local storage so we don't have any settings window.localStorage.clear(); + this.owner.register( + 'service:hcp-link-modal', + class extends HcpLinkModalService { + setResourceId(resourceId) { + super.setResourceId(correctResourceId); + } + } + ); }); test('the banner and nav item are initially displayed on services page', async function (assert) { @@ -35,4 +49,27 @@ module('Acceptance | link to hcp', function (hooks) { // link to HCP nav item still there assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default'); }); + + test('the link to hcp modal window appears when trigger from side-nav item and from banner', async function (assert) { + // default route is services page so we're good here + await visit('/'); + // Expect the banner to be visible by default + assert.dom(bannerSelector).isVisible('Banner is visible by default'); + // expect linkToHCP nav item to be visible as well + assert.dom(linkToHcpSelector).isVisible('Link to HCP nav item is visible by default'); + // Click on the link to HCP banner button + await click(`${bannerSelector} ${linkToHcpBannerButtonSelector}`); + + // link to HCP modal appears + assert.dom(linkToHcpModalSelector).isVisible('Link to HCP modal is visible'); + // Click on the cancel button + await click(`${linkToHcpModalSelector} ${linkToHcpModalCancelButtonSelector}`); + assert.dom(linkToHcpModalSelector).doesNotExist('Link to HCP modal is gone after cancel'); + + // Click on the link to HCP nav item + await click(`${linkToHcpSelector} button`); + + // link to HCP modal appears + assert.dom(linkToHcpModalSelector).isVisible('Link to HCP modal is visible'); + }); }); diff --git a/ui/packages/consul-ui/tests/integration/components/link-to-hcp-modal-test.js b/ui/packages/consul-ui/tests/integration/components/link-to-hcp-modal-test.js new file mode 100644 index 0000000000..1aee8c7cbe --- /dev/null +++ b/ui/packages/consul-ui/tests/integration/components/link-to-hcp-modal-test.js @@ -0,0 +1,192 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { click, render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import Service, { inject as service } from '@ember/service'; +import DataSourceComponent from 'consul-ui/components/data-source/index'; +import sinon from 'sinon'; +import { BlockingEventSource as RealEventSource } from 'consul-ui/utils/dom/event-source'; +import { ACCESS_LEVEL } from 'consul-ui/components/link-to-hcp-modal'; + +const modalSelector = '[data-test-link-to-hcp-modal]'; +const modalOptionReadOnlySelector = '#accessMode-readonly'; +const modalGenerateTokenCardSelector = '[data-test-link-to-hcp-modal-generate-token-card]'; +const modalGenerateTokenCardValueSelector = + '[data-test-link-to-hcp-modal-generate-token-card-value]'; +const modalGenerateTokenCardCopyButtonSelector = + '[data-test-link-to-hcp-modal-generate-token-card-copy-button]'; +const modalGenerateTokenButtonSelector = '[data-test-link-to-hcp-modal-generate-token-button]'; +const modalGenerateTokenMissedPolicyAlertSelector = + '[data-test-link-to-hcp-modal-missed-policy-alert]'; +const modalNextButtonSelector = '[data-test-link-to-hcp-modal-next-button]'; +const modalCancelButtonSelector = '[data-test-link-to-hcp-modal-cancel-button]'; +const resourceId = + 'organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api'; + +module('Integration | Component | link-to-hcp-modal', function (hooks) { + let originalClipboardWriteText; + let hideModal = sinon.stub(); + const close = sinon.stub(); + const source = new RealEventSource(); + + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + const fakeService = class extends Service { + close = close; + open() { + source.getCurrentEvent = function () { + return { data: { Name: 'global-read-only', ID: '00000000-0000-0000-0000-000000000002' } }; + }; + return source; + } + }; + this.owner.register('service:data-source/fake-service', fakeService); + this.owner.register( + 'component:data-source', + class extends DataSourceComponent { + @service('data-source/fake-service') dataSource; + } + ); + this.owner.register( + 'service:abilities', + class Stub extends Service { + can(permission) { + if (permission === 'create tokens') { + return true; + } + } + } + ); + this.owner.register( + 'service:hcp-link-modal', + class Stub extends Service { + resourceId = resourceId; + hide = hideModal; + } + ); + + originalClipboardWriteText = navigator.clipboard.writeText; + navigator.clipboard.writeText = sinon.stub(); + }); + + hooks.afterEach(function () { + navigator.clipboard.writeText = originalClipboardWriteText; + }); + + test('it renders modal', async function (assert) { + await render(hbs``); + + assert.dom(modalSelector).exists({ count: 1 }); + // select read-only + await click(`${modalSelector} ${modalOptionReadOnlySelector}`); + + // when read-only selected, it shows the generate token button + assert.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`).isVisible(); + + // with the correct policy, it doesn't show the missed policy alert + assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).doesNotExist(); + }); + + test('it updates next link on option selected', async function (assert) { + await render(hbs``); + + let hrefValue = this.element + .querySelector(`${modalSelector} ${modalNextButtonSelector}`) + .getAttribute('href'); + assert.ok( + hrefValue.includes(ACCESS_LEVEL.GLOBALREADWRITE), + 'next link includes read/write access level' + ); + + // select read-only + await click(`${modalSelector} ${modalOptionReadOnlySelector}`); + + hrefValue = this.element + .querySelector(`${modalSelector} ${modalNextButtonSelector}`) + .getAttribute('href'); + assert.ok( + hrefValue.includes(ACCESS_LEVEL.GLOBALREADONLY), + 'next link includes read-only access level' + ); + }); + + test('it creates token and copy it to clipboard', async function (assert) { + await render(hbs``); + // select read-only + await click(`${modalSelector} ${modalOptionReadOnlySelector}`); + assert + .dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`) + .hasText('Generate a read-only ACL token'); + + // with the correct policy, it doesn't show the missed policy alert + assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).doesNotExist(); + + // trigger generate token + await click(`${modalSelector} ${modalGenerateTokenButtonSelector}`); + + assert.dom(`${modalSelector} ${modalGenerateTokenCardSelector}`).isVisible(); + assert.dom(`${modalSelector} ${modalGenerateTokenCardValueSelector}`).exists(); + const tokenValue = this.element.querySelector( + `${modalSelector} ${modalGenerateTokenCardValueSelector}` + ).textContent; + // click on copy button + await click(`${modalSelector} ${modalGenerateTokenCardCopyButtonSelector}`); + assert.ok( + navigator.clipboard.writeText.called, + 'clipboard write function is called when copy button is clicked' + ); + assert.ok( + navigator.clipboard.writeText.calledWith(tokenValue.trim()), + 'clipboard contains expected value' + ); + }); + + test('it calls hcpLinkModal.hide when closing modal', async function (assert) { + await render(hbs``); + + await click(`${modalSelector} ${modalCancelButtonSelector}`); + + assert.ok(hideModal.called, 'hide method is called when cancel button is clicked'); + }); + + test('it shows an alert when policy was not loaded and it is not possible to generate a token', async function (assert) { + // creating a fake service that will return an empty policy + const fakeService = class extends Service { + close = close; + open() { + source.getCurrentEvent = function () { + return {}; + }; + return source; + } + }; + this.owner.register('service:data-source/fake-service', fakeService); + + await render(hbs``); + + assert.dom(modalSelector).exists({ count: 1 }); + // select read-only + await click(`${modalSelector} ${modalOptionReadOnlySelector}`); + + // when read-only selected and no policy, it doesn't show the generate token button + assert.dom(`${modalSelector} ${modalGenerateTokenButtonSelector}`).doesNotExist(); + // Missed policy alert is visible + assert.dom(`${modalSelector} ${modalGenerateTokenMissedPolicyAlertSelector}`).isVisible(); + }); +}); diff --git a/ui/packages/consul-ui/tests/integration/helpers/hcp-authentication-link-test.js b/ui/packages/consul-ui/tests/integration/helpers/hcp-authentication-link-test.js new file mode 100644 index 0000000000..f089b20934 --- /dev/null +++ b/ui/packages/consul-ui/tests/integration/helpers/hcp-authentication-link-test.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import { setupRenderingTest } from 'ember-qunit'; +import { HCP_PREFIX } from 'consul-ui/helpers/hcp-authentication-link'; +import { EnvStub } from 'consul-ui/services/env'; + +// organization/b4432207-bb9c-438e-a160-b98923efa979/project/4b09958c-fa91-43ab-8029-eb28d8cee9d4/hashicorp.consul.global-network-manager.cluster/test-from-api +const clusterName = 'hello'; +const clusterVersion = '1.18.0'; +const accessMode = 'CONSUL_ACCESS_LEVEL_GLOBAL_READ_WRITE'; +const projectId = '4b09958c-fa91-43ab-8029-eb28d8cee9d4'; +const realResourceId = `organization/b4432207-bb9c-438e-a160-b98923efa979/project/${projectId}/hashicorp.consul.global-network-manager.cluster/${clusterName}`; +module('Integration | Helper | hcp-authentication-link', function (hooks) { + setupRenderingTest(hooks); + hooks.beforeEach(function () { + this.owner.register( + 'service:env', + class Stub extends EnvStub { + stubEnv = { + CONSUL_VERSION: clusterVersion, + }; + } + ); + }); + test('it makes a URL out of a real resourceId', async function (assert) { + this.resourceId = realResourceId; + + await render(hbs`{{hcp-authentication-link resourceId}}`); + + assert.equal( + this.element.textContent.trim(), + `${HCP_PREFIX}?cluster_name=${clusterName}&cluster_version=${clusterVersion}` + ); + }); + + test('it returns correct link with invalid resourceId', async function (assert) { + this.resourceId = 'invalid'; + + await render(hbs`{{hcp-authentication-link resourceId}}`); + assert.equal( + this.element.textContent.trim(), + `${HCP_PREFIX}?cluster_version=${clusterVersion}` + ); + + // not enough items in id + this.resourceId = + '`organization/b4432207-bb9c-438e-a160-b98923efa979/project/${projectId}/hashicorp.consul.global-network-manager.cluster`'; + await render(hbs`{{hcp-authentication-link resourceId}}`); + assert.equal( + this.element.textContent.trim(), + `${HCP_PREFIX}?cluster_version=${clusterVersion}` + ); + + // value is null + this.resourceId = null; + await render(hbs`{{hcp-authentication-link resourceId}}`); + assert.equal( + this.element.textContent.trim(), + `${HCP_PREFIX}?cluster_version=${clusterVersion}` + ); + }); + + test('it makes a URL out of a real resourceId and accessLevel, if passed', async function (assert) { + this.resourceId = realResourceId; + this.accessMode = accessMode; + + await render(hbs`{{hcp-authentication-link resourceId accessMode}}`); + + assert.equal( + this.element.textContent.trim(), + `${HCP_PREFIX}?cluster_name=${clusterName}&cluster_version=${clusterVersion}&cluster_access_mode=${accessMode}` + ); + }); +});