UI: Hide Client Count Dashboard for PKI Only Clusters (#10513) (#10831)

* hide client counting dashboard for PKI only clusters

* add test and hide client count card

* convert to ts,
add pki only license banner, add comments, and support for multiple dismissed types

* add banner description

* add changelog entry

* update tests + add test coverage for multiple banners + info banner

* update tests

* add doc link

* Apply suggestions from code review



* naming updates

* update feature key format

* update casing

* add enum, revert naming, move helper to util

* test hidden nav link and dashboard card

---------

Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com>
Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
This commit is contained in:
Vault Automation 2025-11-17 10:11:14 -05:00 committed by GitHub
parent 4b54a577fc
commit a92bffe5ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 302 additions and 156 deletions

2
changelog/_10513.txt Normal file
View file

@ -0,0 +1,2 @@
```release-note:improvement
ui: Adds license banner indicating when cluster is operating in PKI-only mode and hides client counting dashboards for PKI-only clusters.

View file

@ -13,7 +13,7 @@ export type Args = {
replication: unknown;
secretsEngines: unknown;
vaultConfiguration: unknown;
version: { isEnterprise: boolean };
version: { isEnterprise: boolean; hasPKIOnly: boolean };
};
export default class OverviewComponent extends Component<Args> {
@ -33,6 +33,9 @@ export default class OverviewComponent extends Component<Args> {
// don't show client count if this isn't an enterprise cluster
if (!version.isEnterprise) return false;
// don't show client count if this is a PKI-only Secrets cluster
if (version.hasPKIOnly) return false;
// HVD clusters
if (namespace.inHvdAdminNamespace) return true;

View file

@ -7,8 +7,8 @@
<Hds::Alert
@type="inline"
@color="critical"
@onDismiss={{fn this.dismissBanner "expired"}}
data-test-license-banner-expired
@onDismiss={{fn this.dismissBanner this.banners.EXPIRED}}
data-test-license-banner={{this.banners.EXPIRED}}
as |A|
>
<A.Title>License expired</A.Title>
@ -17,18 +17,20 @@
{{date-format @expiry "MMM d, yyyy"}}. Add a new license to your configuration and restart Vault.
</A.Description>
<A.Description class="has-top-margin-xs">
<DocLink @path="/vault/tutorials/enterprise/hashicorp-enterprise-license">
Read documentation
<Icon @name="learn-link" />
</DocLink>
<Hds::Link::Standalone
@icon="learn-link"
@iconPosition="trailing"
@href={{doc-link "/vault/tutorials/enterprise/hashicorp-enterprise-license"}}
@text="Read documentation"
/>
</A.Description>
</Hds::Alert>
{{else if (and (lte this.licenseExpiringInDays 30) (not this.warningDismissed))}}
<Hds::Alert
@type="inline"
@color="warning"
@onDismiss={{fn this.dismissBanner "warning"}}
data-test-license-banner-warning
@onDismiss={{fn this.dismissBanner this.banners.WARNING}}
data-test-license-banner={{this.banners.WARNING}}
as |A|
>
<A.Title>Vault license expiring</A.Title>
@ -46,10 +48,32 @@
}}
</A.Description>
<A.Description class="has-top-margin-xs">
<DocLink @path="/vault/tutorials/enterprise/hashicorp-enterprise-license">
Read documentation
<Icon @name="learn-link" />
</DocLink>
<Hds::Link::Standalone
@icon="learn-link"
@iconPosition="trailing"
@href={{doc-link "/vault/tutorials/enterprise/hashicorp-enterprise-license"}}
@text="Read documentation"
/>
</A.Description>
</Hds::Alert>
{{/if}}
{{#if (and this.isPKIOnly (not this.infoDismissed))}}
<Hds::Alert
@type="inline"
@color="highlight"
@onDismiss={{fn this.dismissBanner this.banners.PKI}}
data-test-license-banner={{this.banners.PKI}}
as |A|
>
<A.Title>Cluster operating in PKI-only mode</A.Title>
<A.Description>
This cluster is operating under PKI-only mode. Other than the built-in Vault PKI engine, all secrets engines are
disabled. The
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/api-docs/system/billing/certificates"}}>
number of certificates
</Hds::Link::Inline>
issued is the relevant license utilization metric.
</A.Description>
</Hds::Alert>
{{/if}}

View file

@ -1,77 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';
import isAfter from 'date-fns/isAfter';
import differenceInDays from 'date-fns/differenceInDays';
import localStorage from 'vault/lib/local-storage';
import timestamp from 'core/utils/timestamp';
/**
* @module LicenseBanners
* LicenseBanners components are used to display Vault-specific license expiry messages
*
* @example
* ```js
* <LicenseBanners @expiry={expiryDate} />
* ```
* @param {string} expiry - RFC3339 date timestamp
*/
export default class LicenseBanners extends Component {
@service version;
@tracked warningDismissed;
@tracked expiredDismissed;
constructor() {
super(...arguments);
// reset and show a previously dismissed license banner if:
// the version has been updated or the license has been updated (indicated by a change in the expiry date).
const bannerType = localStorage.getItem(this.dismissedBannerKey); // returns either warning or expired
this.updateDismissType(bannerType);
}
get currentVersion() {
return this.version.version;
}
get dismissedBannerKey() {
return `dismiss-license-banner-${this.currentVersion}-${this.args.expiry}`;
}
get licenseExpired() {
if (!this.args.expiry) return false;
return isAfter(timestamp.now(), new Date(this.args.expiry));
}
get licenseExpiringInDays() {
// Anything more than 30 does not render a warning
if (!this.args.expiry) return 99;
return differenceInDays(new Date(this.args.expiry), timestamp.now());
}
@action
dismissBanner(dismissAction) {
// if a client's version changed their old localStorage key will still exists.
localStorage.cleanupStorage('dismiss-license-banner', this.dismissedBannerKey);
// updates localStorage and then updates the template by calling updateDismissType
localStorage.setItem(this.dismissedBannerKey, dismissAction);
this.updateDismissType(dismissAction);
}
updateDismissType(dismissType) {
// updates tracked properties to update template
if (dismissType === 'warning') {
this.warningDismissed = true;
} else if (dismissType === 'expired') {
this.expiredDismissed = true;
}
}
}

View file

@ -0,0 +1,118 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';
import isAfter from 'date-fns/isAfter';
import differenceInDays from 'date-fns/differenceInDays';
import localStorage from 'vault/lib/local-storage';
import timestamp from 'core/utils/timestamp';
import type VersionService from 'vault/services/version';
enum Banners {
EXPIRED = 'expired',
WARNING = 'warning',
PKI = 'pki-only-info',
}
interface Args {
expiry: string;
autoloaded: boolean;
}
/**
* @module LicenseBanners
* LicenseBanners components are used to display Vault-specific license messages
*
* @example
* ```js
* <LicenseBanners @expiry={expiryDate} />
* ```
* @param {string} expiry - RFC3339 date timestamp
*/
export default class LicenseBanners extends Component<Args> {
@service declare readonly version: VersionService;
@tracked warningDismissed = false;
@tracked expiredDismissed = false;
@tracked infoDismissed = false;
banners = Banners;
constructor(owner: unknown, args: Args) {
super(owner, args);
// reset and show a previously dismissed license banner if:
// the version has been updated or the license has been updated (indicated by a change in the expiry date).
const item = localStorage.getItem(this.dismissedBannerKey) ?? []; // returns warning, expired and/or pki-only-info
// older entries will not be an array as it was either "expired" OR "warning"
// with the addition of "pki-only-info", it can hold all values
// this check maintains backwards compatibility with the previous format
const bannerTypes = Array.isArray(item) ? item : [item];
bannerTypes.forEach((type) => {
this.updateDismissType(type);
});
}
get currentVersion() {
return this.version.version;
}
get dismissedBannerKey() {
return `dismiss-license-banner-${this.currentVersion}-${this.args.expiry}`;
}
get licenseExpired() {
if (!this.args.expiry) return false;
return isAfter(timestamp.now(), new Date(this.args.expiry));
}
get licenseExpiringInDays() {
// Anything more than 30 does not render a warning
if (!this.args.expiry) return 99;
return differenceInDays(new Date(this.args.expiry), timestamp.now());
}
get isPKIOnly() {
return this.version.hasPKIOnly;
}
@action
dismissBanner(dismissAction: Banners) {
// if a client's version changed their old localStorage key will still exists.
localStorage.cleanupStorage('dismiss-license-banner', this.dismissedBannerKey);
// updates localStorage and then updates the template by calling updateDismissType
const item = localStorage.getItem(this.dismissedBannerKey) ?? [];
// older entries will not be an array as it was either "expired" OR "warning"
// with the addition of "pki-only-info", it can hold all values
// this check maintains backwards compatibility with the previous format
const bannerTypes = Array.isArray(item) ? item : [item];
localStorage.setItem(this.dismissedBannerKey, [...bannerTypes, dismissAction]);
this.updateDismissType(dismissAction);
}
updateDismissType(dismissType?: Banners) {
// updates tracked properties to update template
switch (dismissType) {
case this.banners.WARNING:
this.warningDismissed = true;
break;
case this.banners.EXPIRED:
this.expiredDismissed = true;
break;
case this.banners.PKI:
this.infoDismissed = true;
break;
default:
break;
}
}
}

View file

@ -4,7 +4,7 @@
*/
import Component from '@glimmer/component';
import { allFeatures } from 'vault/helpers/all-features';
import { allFeatures } from 'core/utils/all-features';
/**
* @module LicenseInfo
*

View file

@ -85,7 +85,12 @@
/>
{{/if}}
{{#if
(and (has-permission "clients" routeParams="activity") (not this.cluster.dr.isSecondary) (not this.hasChrootNamespace))
(and
(has-permission "clients" routeParams="activity")
(not this.cluster.dr.isSecondary)
(not this.hasChrootNamespace)
(not this.version.hasPKIOnly)
)
}}
<Nav.Link
@route="vault.cluster.clients"

View file

@ -28,6 +28,10 @@ export default class VersionService extends Service {
}
/* Features */
get hasPKIOnly() {
return this.features.includes('PKI-only Secrets');
}
get hasPerfReplication() {
return this.features.includes('Performance Replication');
}

View file

@ -3,8 +3,6 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { helper as buildHelper } from '@ember/component/helper';
const ALL_FEATURES = [
'HSM',
'Performance Replication',
@ -19,10 +17,9 @@ const ALL_FEATURES = [
'Entropy Augmentation',
'Transform Secrets Engine',
'Secrets Sync',
'PKI-only Secrets',
];
export function allFeatures() {
return ALL_FEATURES;
}
export default buildHelper(allFeatures);

View file

@ -11,6 +11,7 @@ import formatRFC3339 from 'date-fns/formatRFC3339';
import { addDays, subDays } from 'date-fns';
import timestamp from 'core/utils/timestamp';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { GENERAL } from '../helpers/general-selectors';
const generateHealthResponse = (now, state) => {
let expiry;
@ -58,15 +59,15 @@ module('Acceptance | Enterprise | License banner warnings', function (hooks) {
const healthResp = generateHealthResponse(this.now);
this.server.get('/sys/health', () => healthResp);
await visit('/vault/auth');
assert.dom('[data-test-license-banner-expired]').doesNotExist('expired banner does not show');
assert.dom('[data-test-license-banner-warning]').doesNotExist('warning banner does not show');
assert.dom(GENERAL.licenseBanner('expired')).doesNotExist('expired banner does not show');
assert.dom(GENERAL.licenseBanner('warning')).doesNotExist('warning banner does not show');
this.server.shutdown();
});
test('it shows license banner warning if license expires within 30 days', async function (assert) {
const healthResp = generateHealthResponse(this.now, 'expiring');
this.server.get('/sys/health', () => healthResp);
await visit('/vault/auth');
assert.dom('[data-test-license-banner-warning]').exists('license warning shows');
assert.dom(GENERAL.licenseBanner('warning')).exists('license warning shows');
this.server.shutdown();
});
@ -74,7 +75,7 @@ module('Acceptance | Enterprise | License banner warnings', function (hooks) {
const healthResp = generateHealthResponse(this.now, 'expired');
this.server.get('/sys/health', () => healthResp);
await visit('/vault/auth');
assert.dom('[data-test-license-banner-expired]').exists('expired license message shows');
assert.dom(GENERAL.licenseBanner('expired')).exists('expired license message shows');
this.server.shutdown();
});
});

View file

@ -3,7 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { allFeatures } from 'vault/helpers/all-features';
import { allFeatures } from 'core/utils/all-features';
import sinon from 'sinon';
/**

View file

@ -21,7 +21,10 @@ export const GENERAL = {
tab: (name: string) => `[data-test-tab="${name}"]`,
hdsTab: (name: string) => `[data-test-tab="${name}"] button`, // HDS tab buttons
secretTab: (name: string) => `[data-test-secret-list-tab="${name}"]`,
navLink: (label: string) => `[data-test-sidebar-nav-link="${label}"]`,
navLink: (label: string) =>
label ? `[data-test-sidebar-nav-link="${label}"]` : '[data-test-sidebar-nav-link]',
navHeading: (label: string) =>
label ? `[data-test-sidebar-nav-heading="${label}"]` : '[data-test-sidebar-nav-heading]',
linkTo: (label: string) => `[data-test-link-to="${label}"]`,
/* ────── Buttons ────── */
@ -177,6 +180,7 @@ export const GENERAL = {
/* ────── Misc ────── */
icon: (name: string) => (name ? `[data-test-icon="${name}"]` : '[data-test-icon]'),
badge: (name: string) => (name ? `[data-test-badge="${name}"]` : '[data-test-badge]'),
licenseBanner: (name: string) => `[data-test-license-banner="${name}"]`,
tooltip: (label: string) => `[data-test-tooltip="${label}"]`,
tooltipText: '.hds-tooltip-container',
manageDropdown: '[data-test-manage-dropdown]',

View file

@ -178,7 +178,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
assert.dom(DASHBOARD.cardName('client-count')).exists();
});
test('it should show not show client count on enterprise in child namespaces called "admin" when running a managed mode', async function (assert) {
test('it should hide client count on enterprise in child namespaces called "admin" when running a managed mode', async function (assert) {
this.permissions.exactPaths = {
'admin/sys/internal/counters/activity': {
capabilities: ['read'],
@ -218,6 +218,17 @@ module('Integration | Component | dashboard/overview', function (hooks) {
assert.dom(DASHBOARD.cardName('client-count')).doesNotExist();
});
test('it should hide client count on PKI-only Secrets clusters', async function (assert) {
this.permissions.exactPaths = {
'sys/internal/counters/activity': {
capabilities: ['read'],
},
};
this.version.features = ['PKI-only Secrets'];
await this.renderComponent();
assert.dom(DASHBOARD.cardName('client-count')).doesNotExist();
});
test('it should hide cards on enterprise in root namespace but no permission', async function (assert) {
await this.renderComponent();
assert.dom(DASHBOARD.cardName('client-count')).doesNotExist();

View file

@ -12,6 +12,7 @@ import subDays from 'date-fns/subDays';
import addDays from 'date-fns/addDays';
import formatRFC3339 from 'date-fns/formatRFC3339';
import timestamp from 'core/utils/timestamp';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
module('Integration | Component | license-banners', function (hooks) {
setupRenderingTest(hooks);
@ -31,17 +32,17 @@ module('Integration | Component | license-banners', function (hooks) {
test('it does not render if no expiry', async function (assert) {
assert.expect(2);
await render(hbs`<LicenseBanners />`);
assert.dom('[data-test-license-banner-expired]').doesNotExist();
assert.dom('[data-test-license-banner-warning]').doesNotExist();
assert.dom(GENERAL.licenseBanner('expired')).doesNotExist();
assert.dom(GENERAL.licenseBanner('warning')).doesNotExist();
});
test('it renders an error if expiry is before now', async function (assert) {
assert.expect(2);
this.set('expiry', formatRFC3339(this.yesterday));
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert.dom('[data-test-license-banner-expired]').exists('Expired license banner renders');
assert.dom(GENERAL.licenseBanner('expired')).exists('Expired license banner renders');
assert
.dom('[data-test-license-banner-expired] .hds-alert__title')
.dom(`${GENERAL.licenseBanner('expired')} .hds-alert__title`)
.hasText('License expired', 'Shows correct title on alert');
});
@ -49,9 +50,9 @@ module('Integration | Component | license-banners', function (hooks) {
assert.expect(2);
this.set('expiry', formatRFC3339(this.nextMonth));
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert.dom('[data-test-license-banner-warning]').exists('Warning license banner renders');
assert.dom(GENERAL.licenseBanner('warning')).exists('Warning license banner renders');
assert
.dom('[data-test-license-banner-warning] .hds-alert__title')
.dom(`${GENERAL.licenseBanner('warning')} .hds-alert__title`)
.hasText('Vault license expiring', 'Shows correct title on alert');
});
@ -59,8 +60,8 @@ module('Integration | Component | license-banners', function (hooks) {
assert.expect(2);
this.set('expiry', formatRFC3339(this.outside30));
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert.dom('[data-test-license-banner-expired]').doesNotExist();
assert.dom('[data-test-license-banner-warning]').doesNotExist();
assert.dom(GENERAL.licenseBanner('expired')).doesNotExist();
assert.dom(GENERAL.licenseBanner('warning')).doesNotExist();
});
test('it does not render the expired banner if it has been dismissed', async function (assert) {
@ -68,14 +69,14 @@ module('Integration | Component | license-banners', function (hooks) {
this.set('expiry', formatRFC3339(this.yesterday));
const key = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
await click('[data-test-license-banner-expired] [data-test-icon="x"]');
assert.dom('[data-test-license-banner-expired]').doesNotExist('Expired license banner does not render');
await click(`${GENERAL.licenseBanner('expired')} ${GENERAL.icon('x')}`);
assert.dom(GENERAL.licenseBanner('expired')).doesNotExist('Expired license banner does not render');
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
const localStorageResult = JSON.parse(localStorage.getItem(key));
assert.strictEqual(localStorageResult, 'expired');
assert.deepEqual(localStorageResult, ['expired']);
assert
.dom('[data-test-license-banner-expired]')
.dom(GENERAL.licenseBanner('expired'))
.doesNotExist('The expired banner still does not render after a re-render.');
localStorage.removeItem(key);
});
@ -85,14 +86,14 @@ module('Integration | Component | license-banners', function (hooks) {
this.set('expiry', formatRFC3339(this.nextMonth));
const key = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
await click('[data-test-license-banner-warning] [data-test-icon="x"]');
assert.dom('[data-test-license-banner-warning]').doesNotExist('Warning license banner does not render');
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
assert.dom(GENERAL.licenseBanner('warning')).doesNotExist('Warning license banner does not render');
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
const localStorageResult = JSON.parse(localStorage.getItem(key));
assert.strictEqual(localStorageResult, 'warning');
assert.deepEqual(localStorageResult, ['warning']);
assert
.dom('[data-test-license-banner-warning]')
.dom(GENERAL.licenseBanner('warning'))
.doesNotExist('The warning banner still does not render after a re-render.');
localStorage.removeItem(key);
});
@ -103,22 +104,23 @@ module('Integration | Component | license-banners', function (hooks) {
this.set('expiry', formatRFC3339(this.nextMonth));
const keyOldVersion = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
await click('[data-test-license-banner-warning] [data-test-icon="x"]');
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
this.version.version = '1.13.1+ent';
const keyNewVersion = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert
.dom('[data-test-license-banner-warning]')
.dom(GENERAL.licenseBanner('warning'))
.exists('The warning banner shows even though we have dismissed it earlier.');
await click('[data-test-license-banner-warning] [data-test-icon="x"]');
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
const localStorageResultNewVersion = JSON.parse(localStorage.getItem(keyNewVersion));
const localStorageResultOldVersion = JSON.parse(localStorage.getItem(keyOldVersion));
// Check that localStorage was cleaned and no longer contains the old version storage key.
assert.strictEqual(localStorageResultOldVersion, null, 'local storage was cleared for the old version');
assert.strictEqual(
assert.deepEqual(
localStorageResultNewVersion,
'warning',
['warning'],
'local storage holds the new version with a warning'
);
// If debugging this test remember to clear localStorage if the test was not run to completion.
@ -130,25 +132,75 @@ module('Integration | Component | license-banners', function (hooks) {
this.set('expiry', formatRFC3339(this.tomorrow));
const keyOldExpiry = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
await click('[data-test-license-banner-warning] [data-test-icon="x"]');
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
this.set('expiry', formatRFC3339(this.nextMonth));
const keyNewExpiry = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert
.dom('[data-test-license-banner-warning]')
.dom(GENERAL.licenseBanner('warning'))
.exists('The warning banner shows even though we have dismissed it earlier.');
await click('[data-test-license-banner-warning] [data-test-icon="x"]');
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
const localStorageResultNewExpiry = JSON.parse(localStorage.getItem(keyNewExpiry));
const localStorageResultOldExpiry = JSON.parse(localStorage.getItem(keyOldExpiry));
// Check that localStorage was cleaned and no longer contains the old version storage key.
assert.strictEqual(localStorageResultOldExpiry, null, 'local storage was cleared for the old expiry');
assert.strictEqual(
assert.deepEqual(
localStorageResultNewExpiry,
'warning',
['warning'],
'local storage holds the new expiry with a warning'
);
// If debugging this test remember to clear localStorage if the test was not run to completion.
localStorage.removeItem(keyNewExpiry);
});
test('it renders a banner if the cluster is in PKI-only mode', async function (assert) {
assert.expect(3);
this.version.features = ['PKI-only Secrets'];
this.set('expiry', formatRFC3339(this.outside30));
const key = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert.dom(GENERAL.licenseBanner('pki-only-info')).exists('The info banner renders');
await click(`${GENERAL.licenseBanner('pki-only-info')} ${GENERAL.icon('x')}`);
assert
.dom(GENERAL.licenseBanner('pki-only-info'))
.doesNotExist('The info banner does not show after being dismissed.');
const localStorageResult = JSON.parse(localStorage.getItem(key));
assert.deepEqual(localStorageResult, ['pki-only-info']);
// If debugging this test remember to clear localStorage if the test was not run to completion.
localStorage.removeItem(key);
});
test('it renders multiple banners', async function (assert) {
assert.expect(8);
this.version.features = ['PKI-only Secrets'];
this.set('expiry', formatRFC3339(this.tomorrow));
const key = `dismiss-license-banner-${this.version.version}-${this.expiry}`;
await render(hbs`<LicenseBanners @expiry={{this.expiry}} />`);
assert.dom(GENERAL.licenseBanner('warning')).exists('The warning banner renders');
assert.dom(GENERAL.licenseBanner('pki-only-info')).exists('The info banner renders');
await click(`${GENERAL.licenseBanner('pki-only-info')} ${GENERAL.icon('x')}`);
assert
.dom(GENERAL.licenseBanner('pki-only-info'))
.doesNotExist('The info banner does not show after being dismissed.');
assert
.dom(GENERAL.licenseBanner('warning'))
.exists('The warning banner is still shown after an info banner is dismissed.');
const localStorageResult = JSON.parse(localStorage.getItem(key));
assert.deepEqual(localStorageResult, ['pki-only-info']);
await click(`${GENERAL.licenseBanner('warning')} ${GENERAL.icon('x')}`);
assert
.dom(GENERAL.licenseBanner('pki-only-info'))
.doesNotExist('The info banner still does not show after another banner type dismissed.');
assert
.dom(GENERAL.licenseBanner('warning'))
.doesNotExist('The warning banner does not show after being dismissed.');
const updatedLocalStorageResult = JSON.parse(localStorage.getItem(key));
assert.deepEqual(updatedLocalStorageResult, ['pki-only-info', 'warning']);
// If debugging this test remember to clear localStorage if the test was not run to completion.
localStorage.removeItem(key);
});
});

View file

@ -10,7 +10,7 @@ import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { create } from 'ember-cli-page-object';
import license from '../../pages/components/license-info';
import { allFeatures } from 'vault/helpers/all-features';
import { allFeatures } from 'core/utils/all-features';
import { setRunOptions } from 'ember-a11y-testing/test-support';
const FEATURES = allFeatures();

View file

@ -10,6 +10,7 @@ import hbs from 'htmlbars-inline-precompile';
import { stubFeaturesAndPermissions } from 'vault/tests/helpers/components/sidebar-nav';
import { setRunOptions } from 'ember-a11y-testing/test-support';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { allFeatures } from 'core/utils/all-features';
const renderComponent = () => {
return render(hbs`
@ -39,13 +40,9 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
stubFeaturesAndPermissions(this.owner, true, true);
await renderComponent();
assert
.dom('[data-test-sidebar-nav-heading]')
.exists({ count: headings.length }, 'Correct number of headings render');
assert.dom(GENERAL.navHeading()).exists({ count: headings.length }, 'Correct number of headings render');
headings.forEach((heading) => {
assert
.dom(`[data-test-sidebar-nav-heading="${heading}"]`)
.hasText(heading, `${heading} heading renders`);
assert.dom(GENERAL.navHeading(heading)).hasText(heading, `${heading} heading renders`);
});
});
@ -53,11 +50,9 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
await renderComponent();
assert
.dom('[data-test-sidebar-nav-link]')
.dom(GENERAL.navLink())
.exists({ count: 3 }, 'Nav links are hidden other than secrets, recovery and dashboard');
assert
.dom('[data-test-sidebar-nav-heading]')
.exists({ count: 1 }, 'Headings are hidden other than Vault');
assert.dom(GENERAL.navHeading()).exists({ count: 1 }, 'Headings are hidden other than Vault');
});
test('it should render nav links', async function (assert) {
@ -78,14 +73,14 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
'Custom Messages',
'UI Login Settings',
];
stubFeaturesAndPermissions(this.owner, true, true);
// do not add PKI-only Secrets feature as it hides Client Count nav link
const features = allFeatures().filter((feat) => feat !== 'PKI-only Secrets');
stubFeaturesAndPermissions(this.owner, true, true, features);
await renderComponent();
assert
.dom('[data-test-sidebar-nav-link]')
.exists({ count: links.length }, 'Correct number of links render');
assert.dom(GENERAL.navLink()).exists({ count: links.length }, 'Correct number of links render');
links.forEach((link) => {
assert.dom(`[data-test-sidebar-nav-link="${link}"]`).hasText(link, `${link} link renders`);
assert.dom(GENERAL.navLink(link)).hasText(link, `${link} link renders`);
});
});
@ -105,15 +100,13 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
await renderComponent();
assert
.dom('[data-test-sidebar-nav-heading="Monitoring"]')
.dom(GENERAL.navHeading('Monitoring'))
.doesNotExist(
'Monitoring heading is hidden in child namespace when user does not have access to Client Count'
);
links.forEach((link) => {
assert
.dom(`[data-test-sidebar-nav-link="${link}"]`)
.doesNotExist(`${link} is hidden in child namespace`);
assert.dom(GENERAL.navLink(link)).doesNotExist(`${link} is hidden in child namespace`);
});
});
@ -134,15 +127,19 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
await renderComponent();
assert
.dom('[data-test-sidebar-nav-heading="Monitoring"]')
.dom(GENERAL.navHeading('Monitoring'))
.doesNotExist('Monitoring heading is hidden in chroot namespace');
links.forEach((link) => {
assert
.dom(`[data-test-sidebar-nav-link="${link}"]`)
.doesNotExist(`${link} is hidden in chroot namespace`);
assert.dom(GENERAL.navLink(link)).doesNotExist(`${link} is hidden in chroot namespace`);
});
});
test('it should hide client counts link in PKI-only Secrets clusters', async function (assert) {
stubFeaturesAndPermissions(this.owner, true, false);
await renderComponent();
assert.dom(GENERAL.navHeading('Client Counts')).doesNotExist('Client count link is hidden.');
});
test('it should render badge for promotional links on managed clusters', async function (assert) {
this.flags.featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE'];
const promotionalLinks = ['Secrets Sync'];
@ -150,9 +147,7 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
await renderComponent();
promotionalLinks.forEach((link) => {
assert
.dom(`[data-test-sidebar-nav-link="${link}"]`)
.hasText(`${link} Plus`, `${link} link renders Plus badge`);
assert.dom(GENERAL.navLink(link)).hasText(`${link} Plus`, `${link} link renders Plus badge`);
});
});

View file

@ -47,6 +47,13 @@ module('Unit | Service | version', function (hooks) {
assert.true(service.hasDRReplication);
});
test('hasPKIOnly', function (assert) {
const service = this.owner.lookup('service:version');
assert.false(service.hasPKIOnly);
service.features = ['PKI-only Secrets'];
assert.true(service.hasPKIOnly);
});
// SHOW SECRETS SYNC TESTS
test('hasSecretsSync: it returns false when version is community', function (assert) {
const service = this.owner.lookup('service:version');