mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-18 18:38:08 -05:00
* 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:
parent
4b54a577fc
commit
a92bffe5ce
17 changed files with 302 additions and 156 deletions
2
changelog/_10513.txt
Normal file
2
changelog/_10513.txt
Normal 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.
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
ui/app/components/license-banners.ts
Normal file
118
ui/app/components/license-banners.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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]',
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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`);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Reference in a new issue