UI: Cleanup dashboard (#22574)

This commit is contained in:
Kianna
2023-08-30 14:17:06 -07:00
committed by GitHub
parent efca08d295
commit fc9e308f02
20 changed files with 497 additions and 210 deletions

View File

@@ -7,7 +7,7 @@ import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { pathIsDirectory } from 'kv/utils/kv-breadcrumbs';
/**
* @module DashboardQuickActionsCard
* DashboardQuickActionsCard component allows users to see a list of secrets engines filtered by
@@ -19,7 +19,7 @@ import { inject as service } from '@ember/service';
* ```
*/
const QUICK_ACTION_ENGINES = ['pki', 'kv', 'database'];
const QUICK_ACTION_ENGINES = ['pki', 'database'];
export default class DashboardQuickActionsCard extends Component {
@service router;
@@ -30,7 +30,7 @@ export default class DashboardQuickActionsCard extends Component {
get actionOptions() {
switch (this.selectedEngine.type) {
case `kv version ${this.selectedEngine?.version}`:
case 'kv':
return ['Find KV secrets'];
case 'database':
return ['Generate credentials for database'];
@@ -46,11 +46,13 @@ export default class DashboardQuickActionsCard extends Component {
case 'Find KV secrets':
return {
title: 'Secret path',
subText: 'Path of the secret you want to read, including the mount. E.g., secret/data/foo.',
subText: 'Path of the secret you want to read.',
buttonText: 'Read secrets',
// check kv version to figure out which model to use
model: this.selectedEngine.version === 2 ? 'secret-v2' : 'secret',
route: 'vault.cluster.secrets.backend.show',
model: 'kv/metadata',
route: 'vault.cluster.secrets.backend.kv.secret.details',
nameKey: 'path',
queryObject: { pathToSecret: '', backend: this.selectedEngine.id },
objectKeys: ['path', 'id'],
};
case 'Generate credentials for database':
return {
@@ -58,6 +60,7 @@ export default class DashboardQuickActionsCard extends Component {
buttonText: 'Generate credentials',
model: 'database/role',
route: 'vault.cluster.secrets.backend.credentials',
queryObject: { backend: this.selectedEngine.id },
};
case 'Issue certificate':
return {
@@ -66,6 +69,7 @@ export default class DashboardQuickActionsCard extends Component {
buttonText: 'Issue leaf certificate',
model: 'pki/role',
route: 'vault.cluster.secrets.backend.pki.roles.role.generate',
queryObject: { backend: this.selectedEngine.id },
};
case 'View certificate':
return {
@@ -74,6 +78,7 @@ export default class DashboardQuickActionsCard extends Component {
buttonText: 'View certificate',
model: 'pki/certificate/base',
route: 'vault.cluster.secrets.backend.pki.certificates.certificate.details',
queryObject: { backend: this.selectedEngine.id },
};
case 'View issuer':
return {
@@ -81,8 +86,10 @@ export default class DashboardQuickActionsCard extends Component {
placeholder: 'Type issuer name or ID',
buttonText: 'View issuer',
model: 'pki/issuer',
nameKey: 'issuerName',
route: 'vault.cluster.secrets.backend.pki.issuers.issuer.details',
nameKey: 'issuerName',
queryObject: { backend: this.selectedEngine.id },
objectKeys: ['id', 'issuerName'],
};
default:
return {
@@ -94,15 +101,16 @@ export default class DashboardQuickActionsCard extends Component {
}
get filteredSecretEngines() {
return this.args.secretsEngines.filter((engine) => QUICK_ACTION_ENGINES.includes(engine.type));
return this.args.secretsEngines.filter(
(engine) => (engine.type === 'kv' && engine.version == 2) || QUICK_ACTION_ENGINES.includes(engine.type)
);
}
get mountOptions() {
return this.filteredSecretEngines.map((engine) => {
let { id, type, version } = engine;
if (type === 'kv') type = `kv version ${version}`;
const { id, type } = engine;
return { name: id, type, id, version };
return { name: id, type, id };
});
}
@@ -131,17 +139,18 @@ export default class DashboardQuickActionsCard extends Component {
@action
navigateToPage() {
let searchSelectParamRoute = this.searchSelectParams.route;
let route = this.searchSelectParams.route;
let param = this.paramValue.id;
// kv has a special use case where if the paramValue ends in a '/' you should
// link to different route
if (this.selectedEngine.type === 'kv') {
searchSelectParamRoute =
this.paramValue && this.paramValue?.endsWith('/')
? 'vault.cluster.secrets.backend.list'
: 'vault.cluster.secrets.backend.show';
route = pathIsDirectory(this.paramValue?.path)
? 'vault.cluster.secrets.backend.kv.list-directory'
: 'vault.cluster.secrets.backend.kv.secret.details';
param = this.paramValue?.path;
}
this.router.transitionTo(searchSelectParamRoute, this.selectedEngine.id, this.paramValue);
this.router.transitionTo(route, this.selectedEngine.id, param);
}
}

View File

@@ -22,6 +22,10 @@ export default class DashboardSecretsEnginesCard extends Component {
(secretEngine) => secretEngine.shouldIncludeInList
);
return filteredEngines.slice(0, 5);
return filteredEngines;
}
get firstFiveSecretsEngines() {
return this.filteredSecretsEngines.slice(0, 5);
}
}

View File

@@ -1,4 +1,4 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l has-bottom-padding-m" data-test-client-count-card>
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l has-bottom-padding-m" data-test-card="client-count">
<div class="is-flex-between">
<h3 class="title is-4 has-bottom-margin-xxs" data-test-client-count-title>
Client count

View File

@@ -1,4 +1,4 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l">
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l" data-test-card="learn-more">
<h3 class="title is-4 has-bottom-margin-xxs" data-test-learn-more-title>Learn more</h3>
<div class="sub-text" data-test-learn-more-subtext>
Explore the features of Vault and learn advance practices with the following tutorials and documentation.

View File

@@ -0,0 +1,48 @@
<Dashboard::VaultVersionTitle @version={{@version}} />
<div class="has-bottom-margin-xl">
<div class="is-flex-row has-gap-l">
{{#if (and @version.isEnterprise (or @license @isRootNamespace))}}
<div class="is-flex-column is-flex-1 has-gap-l">
{{#if @license}}
<Dashboard::ClientCountCard @license={{@license}} />
{{/if}}
{{#if @isRootNamespace}}
<Dashboard::ReplicationCard
@replication={{@replication}}
@version={{@version}}
@refresh={{@refreshModel}}
@updatedAt={{@replicationUpdatedAt}}
/>
{{/if}}
<Dashboard::SecretsEnginesCard @secretsEngines={{@secretsEngines}} />
</div>
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::QuickActionsCard @secretsEngines={{@secretsEngines}} />
{{#if @vaultConfiguration}}
<Dashboard::VaultConfigurationDetailsCard @vaultConfiguration={{@vaultConfiguration}} />
{{/if}}
<div>
<Dashboard::LearnMoreCard @isEnterprise={{@version.isEnterprise}} />
<Dashboard::SurveyLinkText />
</div>
</div>
{{else}}
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::SecretsEnginesCard @secretsEngines={{@secretsEngines}} />
<div>
<Dashboard::LearnMoreCard @isEnterprise={{@version.isEnterprise}} />
<Dashboard::SurveyLinkText />
</div>
</div>
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::QuickActionsCard @secretsEngines={{@secretsEngines}} />
{{#if @vaultConfiguration}}
<Dashboard::VaultConfigurationDetailsCard @vaultConfiguration={{@vaultConfiguration}} />
{{/if}}
</div>
{{/if}}
</div>
</div>

View File

@@ -1,8 +1,9 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l">
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l" data-test-card="quick-actions">
<h3 class="title is-4">Quick actions</h3>
<div class="has-bottom-margin-m">
<h4 class="title is-6">Secrets engines</h4>
<div class="has-top-margin-m has-bottom-margin-m">
<h4 class="title is-marginless is-6">Secrets engines</h4>
<p class="is-size-8 has-top-margin-xxs has-bottom-margin-s has-text-grey">Supported engines include databases, KV version
2, and PKI.</p>
<SearchSelect
@id="secrets-engines-select"
@options={{this.mountOptions}}
@@ -38,13 +39,16 @@
class="is-flex-grow-1"
@selectLimit="1"
@models={{array this.searchSelectParams.model}}
@backend={{this.selectedEngine.id}}
@placeholder={{this.searchSelectParams.placeholder}}
@disallowNewItems={{true}}
@onChange={{this.handleActionSelect}}
@fallbackComponent="input-search"
@nameKey={{this.searchSelectParams.nameKey}}
@disabled={{not this.searchSelectParams.model}}
@nameKey={{this.searchSelectParams.nameKey}}
@queryObject={{this.searchSelectParams.queryObject}}
@objectKeys={{this.searchSelectParams.objectKeys}}
@passObject={{true}}
@shouldRenderName={{this.searchSelectParams.nameKey}}
data-test-param-select
/>

View File

@@ -2,16 +2,18 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l has-bottom-padding-m" data-test-replication-card>
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l has-bottom-padding-m" data-test-card="replication">
<div class="is-flex-between">
<h3 class="title is-4 has-bottom-margin-xxs" data-test-client-count-title>
Replication
</h3>
<LinkTo class="is-no-underline" @route="vault.cluster.replication.index">
Details
</LinkTo>
{{#if (or @replication.dr.clusterId @replication.performance.clusterId)}}
<LinkTo class="is-no-underline" @route="vault.cluster.replication.index">
Details
</LinkTo>
{{/if}}
</div>
{{! check if dr replication and performance replication exists }}
@@ -30,7 +32,7 @@
@clusterStates={{cluster-states @replication.dr.state}}
/>
<Dashboard::ReplicationStateText
@title="Perf primary"
@title="Performance {{if @replication.performance.isPrimary 'primary' 'secondary'}}"
@name="performance"
@state={{if @replication.performance.clusterId @replication.performance.state "not set up"}}
@clusterStates={{if @replication.performance.clusterId (cluster-states @replication.performance.state)}}

View File

@@ -4,20 +4,9 @@
~}}
<div>
{{#if @name}}
<LinkTo
class="title is-5 has-text-weight-semibold has-bottom-margin-xs"
@route="vault.cluster.replication.mode.index"
@model={{@name}}
data-test-title={{@title}}
>
{{@title}}
</LinkTo>
{{else}}
<h2 class="title is-5 has-text-weight-semibold has-bottom-margin-xs" data-test-title={{@title}}>
{{@title}}
</h2>
{{/if}}
<h2 class="is-size-5 has-text-weight-semibold has-bottom-margin-xs" data-test-title={{@title}}>
{{@title}}
</h2>
{{#if @subText}}
<div class="title is-8 has-font-weight-normal has-text-grey-dark" data-test-subtext={{@title}}>
@@ -29,7 +18,7 @@
<T.Trigger
data-test-tooltip-trigger
tabindex="-1"
class="title is-3 has-font-weight-normal has-top-margin-xxs has-bottom-margin-xxs"
class="title is-4 has-text-weight-semibold has-top-margin-xxs has-bottom-margin-xxs"
data-test-tooltip-title={{@title}}
>
{{or @state "not set up"}}

View File

@@ -1,8 +1,15 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l secrets-engines-card">
<h3 class="title is-4 has-left-margin-xxs" data-test-dashboard-secrets-engines-header>Secrets engines</h3>
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l secrets-engines-card" data-test-card="secrets-engines">
<div class="is-flex-between">
<h3 class="title is-4 has-left-margin-xxs" data-test-dashboard-secrets-engines-header>Secrets engines</h3>
<LinkTo class="is-no-underline has-right-margin-xxs" @route="vault.cluster.secrets.backends">
Details
</LinkTo>
</div>
<Hds::Table @caption="Five secrets engines" class="is-border-spacing-revert" data-test-dashboard-secrets-engines-table>
<:body as |B|>
{{#each this.filteredSecretsEngines as |backend|}}
{{#each this.firstFiveSecretsEngines as |backend|}}
<B.Tr data-test-secrets-engines-row={{backend.id}}>
<B.Td class="is-flex-between is-flex-center has-gap-m">
<div>
@@ -60,12 +67,14 @@
{{/each}}
</:body>
</Hds::Table>
{{#if (gt this.filteredSecretsEngines.length 4)}}
<div class="is-flex-end has-top-margin-s" data-test-secrets-engines-card-show-all>
<LinkTo @route="vault.cluster.secrets.backends" class="has-text-weight-semibold is-size-7 is-no-underline">
Show all
<Icon @name="arrow-right" />
</LinkTo>
</div>
{{#if (gt this.filteredSecretsEngines.length 5)}}
<p class="is-size-9 has-top-margin-xs has-text-grey" data-test-secrets-engine-total-help-text>
Showing 5 out of
{{this.filteredSecretsEngines.length}}
secret engines. Navigate to
<Hds::Link::Inline @route="vault.cluster.secrets.backends">details</Hds::Link::Inline>
to view more.
</p>
{{/if}}
</Hds::Card::Container>

View File

@@ -1,4 +1,4 @@
<div class="has-padding-s has-top-margin-m" data-test-feedback-form>
<div class="has-top-padding-xs has-bottom-padding-xs has-top-margin-m" data-test-feedback-form>
<small>
Don't see what you're looking for on this page? Let us know via our
<Hds::Link::Inline @icon="external-link" @href="https://hashicorp.sjc1.qualtrics.com/jfe/form/SV_1SNUsZLdWHpfw0e">

View File

@@ -1,4 +1,4 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l">
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l" data-test-card="configuration-details">
<h3 class="title is-4" data-test-configuration-details-title>Configuration details</h3>
<Hds::Table @caption="Vault configuration details" class="is-border-spacing-revert">

View File

@@ -1,49 +1,10 @@
<Dashboard::VaultVersionTitle @version={{@model.version}} />
<div class="has-bottom-margin-xl">
<div class="is-flex-row has-gap-l">
{{#if (and @model.version.isEnterprise (or @model.license @model.isRootNamespace))}}
<div class="is-flex-column is-flex-1 has-gap-l">
{{#if @model.license}}
<Dashboard::ClientCountCard @license={{@model.license}} />
{{/if}}
{{#if @model.isRootNamespace}}
<Dashboard::ReplicationCard
@replication={{@model.replication}}
@version={{@model.version}}
@refresh={{this.refreshModel}}
@updatedAt={{this.replicationUpdatedAt}}
/>
{{/if}}
<Dashboard::SecretsEnginesCard @secretsEngines={{@model.secretsEngines}} />
</div>
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::QuickActionsCard @secretsEngines={{@model.secretsEngines}} />
{{#if @model.vaultConfiguration}}
<Dashboard::VaultConfigurationDetailsCard @vaultConfiguration={{@model.vaultConfiguration}} />
{{/if}}
<div>
<Dashboard::LearnMoreCard @isEnterprise={{@model.version.isEnterprise}} />
<Dashboard::SurveyLinkText />
</div>
</div>
{{else}}
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::SecretsEnginesCard @secretsEngines={{@model.secretsEngines}} />
<div>
<Dashboard::LearnMoreCard @isEnterprise={{@model.version.isEnterprise}} />
<Dashboard::SurveyLinkText />
</div>
</div>
<div class="is-flex-column is-flex-1 has-gap-l">
<Dashboard::QuickActionsCard @secretsEngines={{@model.secretsEngines}} />
{{#if @model.vaultConfiguration}}
<Dashboard::VaultConfigurationDetailsCard @vaultConfiguration={{@model.vaultConfiguration}} />
{{/if}}
</div>
{{/if}}
</div>
</div>
<Dashboard::Overview
@replication={{@model.replication}}
@secretsEngines={{@model.secretsEngines}}
@license={{@model.license}}
@isRootNamespace={{@model.isRootNamespace}}
@version={{@model.version}}
@vaultConfiguration={{@model.vaultConfiguration}}
@refreshModel={{this.refreshModel}}
@replicationUpdatedAt={{this.replicationUpdatedAt}}
/>

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: MPL-2.0
*/
import { module, test, skip } from 'qunit';
import { module, test } from 'qunit';
import {
visit,
currentURL,
@@ -34,6 +34,7 @@ import SECRETS_ENGINE_SELECTORS from 'vault/tests/helpers/components/dashboard/s
import VAULT_CONFIGURATION_SELECTORS from 'vault/tests/helpers/components/dashboard/vault-configuration-details-card';
import QUICK_ACTION_SELECTORS from 'vault/tests/helpers/components/dashboard/quick-actions-card';
import REPLICATION_CARD_SELECTORS from 'vault/tests/helpers/components/dashboard/replication-card';
import { SELECTORS } from 'vault/tests/helpers/components/dashboard/dashboard-selectors';
const consoleComponent = create(consoleClass);
@@ -81,38 +82,6 @@ module('Acceptance | landing page dashboard', function (hooks) {
});
});
module('learn more card', function (hooks) {
hooks.beforeEach(function () {
return authPage.login();
});
skip('shows the learn more card on community', async function (assert) {
await visit('/vault/dashboard');
assert.dom('[data-test-learn-more-title]').hasText('Learn more');
assert
.dom('[data-test-learn-more-subtext]')
.hasText(
'Explore the features of Vault and learn advance practices with the following tutorials and documentation.'
);
assert.dom('[data-test-learn-more-links] a').exists({ count: 3 });
assert
.dom('[data-test-feedback-form]')
.hasText("Don't see what you're looking for on this page? Let us know via our feedback form .");
});
test('shows the learn more card on enterprise', async function (assert) {
await visit('/vault/dashboard');
assert.dom('[data-test-learn-more-title]').hasText('Learn more');
assert
.dom('[data-test-learn-more-subtext]')
.hasText(
'Explore the features of Vault and learn advance practices with the following tutorials and documentation.'
);
assert.dom('[data-test-learn-more-links] a').exists({ count: 4 });
assert
.dom('[data-test-feedback-form]')
.hasText("Don't see what you're looking for on this page? Let us know via our feedback form .");
});
});
module('configuration details card', function (hooks) {
hooks.beforeEach(async function () {
this.data = {
@@ -363,24 +332,6 @@ module('Acceptance | landing page dashboard', function (hooks) {
});
});
skip('replication and client count card community version', function (hooks) {
hooks.beforeEach(async function () {
this.store = this.owner.lookup('service:store');
await authPage.login();
});
test('hides replication card for community version', async function (assert) {
const version = this.owner.lookup('service:version');
assert.false(version.isEnterprise, 'version is not enterprise');
assert.dom('[data-test-replication-card]').doesNotExist();
});
test('hides the client count card in community version', async function (assert) {
assert.dom('[data-test-client-count-card]').doesNotExist();
});
});
module('client counts card enterprise', function (hooks) {
hooks.before(async function () {
ENV['ember-cli-mirage'].handler = 'clients';
@@ -400,7 +351,7 @@ module('Acceptance | landing page dashboard', function (hooks) {
const version = this.owner.lookup('service:version');
assert.true(version.isEnterprise, 'version is enterprise');
assert.strictEqual(currentURL(), '/vault/dashboard');
assert.dom('[data-test-client-count-card]').exists();
assert.dom(SELECTORS.cardName('client-count')).exists();
const response = await this.store.peekRecord('clients/activity', 'some-activity-id');
assert.dom('[data-test-client-count-title]').hasText('Client count');
assert.dom('[data-test-stat-text="total-clients"] .stat-label').hasText('Total');
@@ -463,13 +414,13 @@ module('Acceptance | landing page dashboard', function (hooks) {
.dom(REPLICATION_CARD_SELECTORS.getStateTooltipIcon('dr-perf', 'DR primary', 'x-circle'))
.exists();
assert
.dom(REPLICATION_CARD_SELECTORS.getReplicationTitle('dr-perf', 'Perf primary'))
.hasText('Perf primary');
.dom(REPLICATION_CARD_SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
assert
.dom(REPLICATION_CARD_SELECTORS.getStateTooltipTitle('dr-perf', 'Perf primary'))
.dom(REPLICATION_CARD_SELECTORS.getStateTooltipTitle('dr-perf', 'Performance primary'))
.hasText('running');
assert
.dom(REPLICATION_CARD_SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'check-circle'))
.dom(REPLICATION_CARD_SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'check-circle'))
.exists();
});
});

View File

@@ -0,0 +1,3 @@
export const SELECTORS = {
cardName: (name) => `[data-test-card="${name}"]`,
};

View File

@@ -12,15 +12,15 @@ const SELECTORS = {
knownSecondariesLabel: '[data-test-stat-text="known secondaries"] .stat-label',
knownSecondariesSubtext: '[data-test-stat-text="known secondaries"] .stat-text',
knownSecondariesValue: '[data-test-stat-text="known secondaries"] .stat-value',
replicationEmptyState: '[data-test-replication-card] [data-test-component="empty-state"]',
replicationEmptyState: '[data-test-card="replication"] [data-test-component="empty-state"]',
replicationEmptyStateTitle:
'[data-test-replication-card] [data-test-component="empty-state"] .empty-state-title',
'[data-test-card="replication"] [data-test-component="empty-state"] .empty-state-title',
replicationEmptyStateMessage:
'[data-test-replication-card] [data-test-component="empty-state"] .empty-state-message',
'[data-test-card="replication"] [data-test-component="empty-state"] .empty-state-message',
replicationEmptyStateActions:
'[data-test-replication-card] [data-test-component="empty-state"] .empty-state-actions',
'[data-test-card="replication"] [data-test-component="empty-state"] .empty-state-actions',
replicationEmptyStateActionsLink:
'[data-test-replication-card] [data-test-component="empty-state"] .empty-state-actions a',
'[data-test-card="replication"] [data-test-component="empty-state"] .empty-state-actions a',
};
export default SELECTORS;

View File

@@ -0,0 +1,233 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { SELECTORS } from 'vault/tests/helpers/components/dashboard/dashboard-selectors';
module('Integration | Component | dashboard/overview', function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.replication = {
dr: {
clusterId: '123',
state: 'running',
},
performance: {
clusterId: 'abc-1',
state: 'running',
},
};
this.store.pushPayload('secret-engine', {
modelName: 'secret-engine',
data: {
accessor: 'kv_f3400dee',
path: 'kv-test/',
type: 'kv',
},
});
this.store.pushPayload('secret-engine', {
modelName: 'secret-engine',
data: {
accessor: 'kv_f3300dee',
path: 'kv-1/',
type: 'kv',
},
});
this.secretsEngines = this.store.peekAll('secret-engine', {});
this.vaultConfiguration = {
api_addr: 'http://127.0.0.1:8200',
default_lease_ttl: 0,
max_lease_ttl: 0,
listeners: [
{
config: {
address: '127.0.0.1:8200',
tls_disable: 1,
},
type: 'tcp',
},
],
};
this.refreshModel = () => {};
});
test('it should hide client count and replication card on community', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1';
this.isRootNamespace = true;
await render(
hbs`
<Dashboard::Overview
@secretsEngines={{this.secretsEngines}}
@vaultConfiguration={{this.vaultConfiguration}}
@replication={{this.replication}}
@version={{this.version}}
@isRootNamespace={{this.isRootNamespace}}
@refreshModel={{this.refreshModel}} />
`
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
assert.dom(SELECTORS.cardName('configuration-details')).exists();
assert.dom(SELECTORS.cardName('replication')).doesNotExist();
assert.dom(SELECTORS.cardName('client-count')).doesNotExist();
});
test('it should show client count and replication card on enterprise w/ license + namespace enabled', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.license = {
autoloaded: {
license_id: '7adbf1f4-56ef-35cd-3a6c-50ef2627865d',
},
};
await render(
hbs`
<Dashboard::Overview
@secretsEngines={{this.secretsEngines}}
@vaultConfiguration={{this.vaultConfiguration}}
@replication={{this.replication}}
@version={{this.version}}
@isRootNamespace={{true}}
@license={{this.license}}
@refreshModel={{this.refreshModel}} />`
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
assert.dom(SELECTORS.cardName('configuration-details')).exists();
assert.dom(SELECTORS.cardName('replication')).exists();
assert.dom(SELECTORS.cardName('client-count')).exists();
});
test('it should hide client count on enterprise w/o license ', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.isRootNamespace = true;
await render(
hbs`
<Dashboard::Overview
@secretsEngines={{this.secretsEngines}}
@vaultConfiguration={{this.vaultConfiguration}}
@replication={{this.replication}}
@version={{this.version}}
@isRootNamespace={{this.isRootNamespace}}
@refreshModel={{this.refreshModel}}
/>`
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
assert.dom(SELECTORS.cardName('configuration-details')).exists();
assert.dom(SELECTORS.cardName('replication')).exists();
assert.dom(SELECTORS.cardName('client-count')).doesNotExist();
});
test('it should hide replication on enterprise not on root namespace', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.isRootNamespace = false;
this.license = {
autoloaded: {
license_id: '7adbf1f4-56ef-35cd-3a6c-50ef2627865d',
},
};
await render(
hbs`
<Dashboard::Overview
@version={{this.version}}
@isRootNamespace={{this.isRootNamespace}}
@secretsEngines={{this.secretsEngines}}
@vaultConfiguration={{this.vaultConfiguration}}
@replication={{this.replication}}
@license={{this.license}}
@refreshModel={{this.refreshModel}} />`
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
assert.dom(SELECTORS.cardName('configuration-details')).exists();
assert.dom(SELECTORS.cardName('replication')).doesNotExist();
assert.dom(SELECTORS.cardName('client-count')).exists();
});
module('learn more card', function () {
test('shows the learn more card on community', async function (assert) {
await render(
hbs`<Dashboard::Overview @secretsEngines={{this.secretsEngines}} @vaultConfiguration={{this.vaultConfiguration}} @replication={{this.replication}} @refreshModel={{this.refreshModel}} />`
);
assert.dom('[data-test-learn-more-title]').hasText('Learn more');
assert
.dom('[data-test-learn-more-subtext]')
.hasText(
'Explore the features of Vault and learn advance practices with the following tutorials and documentation.'
);
assert.dom('[data-test-learn-more-links] a').exists({ count: 3 });
assert
.dom('[data-test-feedback-form]')
.hasText("Don't see what you're looking for on this page? Let us know via our feedback form .");
});
test('shows the learn more card on enterprise', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.features = [
'Performance Replication',
'DR Replication',
'Namespaces',
'Transform Secrets Engine',
];
this.isRootNamespace = true;
this.license = {
autoloaded: {
license_id: '7adbf1f4-56ef-35cd-3a6c-50ef2627865d',
},
};
await render(
hbs`
<Dashboard::Overview
@version={{this.version}}
@isRootNamespace={{this.isRootNamespace}}
@license={{this.license}}
@secretsEngines={{this.secretsEngines}}
@vaultConfiguration={{this.vaultConfiguration}}
@replication={{this.replication}}
@refreshModel={{this.refreshModel}} />
`
);
assert.dom('[data-test-learn-more-title]').hasText('Learn more');
assert
.dom('[data-test-learn-more-subtext]')
.hasText(
'Explore the features of Vault and learn advance practices with the following tutorials and documentation.'
);
assert.dom('[data-test-learn-more-links] a').exists({ count: 4 });
assert
.dom('[data-test-feedback-form]')
.hasText("Don't see what you're looking for on this page? Let us know via our feedback form .");
});
});
});

View File

@@ -5,7 +5,7 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { render, findAll, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { fillIn } from '@ember/test-helpers';
import { selectChoose } from 'ember-power-select/test-support/helpers';
@@ -78,7 +78,10 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks
modelName: 'secret-engine',
data: {
accessor: 'secrets_j2350ii',
path: 'secrets-1-test/',
path: 'kv-v2-test/',
options: {
version: 2,
},
type: 'kv',
},
});
@@ -122,7 +125,13 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks
});
test('it should show correct actions for kv', async function (assert) {
await this.renderComponent();
await selectChoose(SELECTORS.secretsEnginesSelect, 'secrets-1-test');
await click('[data-test-component="search-select"]#secrets-engines-select .ember-basic-dropdown-trigger');
assert.strictEqual(
findAll('li.ember-power-select-option').length,
5,
'renders only kv v2, pki and db engines'
);
await selectChoose(SELECTORS.secretsEnginesSelect, 'kv-v2-test');
assert.dom(SELECTORS.emptyState).doesNotExist();
await fillIn(SELECTORS.actionSelect, 'Find KV secrets');
assert.dom(SELECTORS.paramsTitle).hasText('Secret path');

View File

@@ -24,6 +24,7 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
performance: {
clusterId: 'abc-1',
state: 'running',
isPrimary: true,
},
};
this.version = {
@@ -37,19 +38,21 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
test('it should display replication information if both dr and performance replication are enabled as features', async function (assert) {
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'DR primary')).hasText('DR primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'DR primary')).hasText('running');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'DR primary', 'check-circle')).exists();
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'Perf primary')).hasText('Perf primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Perf primary')).hasText('running');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'check-circle')).exists();
assert
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Performance primary')).hasText('running');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'check-circle')).exists();
});
test('it should display replication information if both dr and performance replication are enabled as features and only dr is setup', async function (assert) {
this.replication = {
@@ -59,14 +62,15 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
},
performance: {
clusterId: '',
isPrimary: true,
},
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
@@ -77,12 +81,14 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'DR primary', 'check-circle'))
.hasClass('has-text-success');
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'Perf primary')).hasText('Perf primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Perf primary')).hasText('not set up');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle'))
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Performance primary')).hasText('not set up');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle'))
.hasClass('has-text-danger');
});
@@ -100,10 +106,10 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
@@ -126,14 +132,15 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
performance: {
clusterId: 'def',
state: 'shutdown',
isPrimary: true,
},
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
@@ -144,14 +151,65 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'DR primary', 'x-square'))
.hasClass('has-text-danger');
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'Perf primary')).hasText('Perf primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Perf primary')).hasText('shutdown');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle'))
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Performance primary')).hasText('shutdown');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle'))
.hasClass('has-text-danger');
});
test('it should show correct performance titles if primary vs secondary', async function (assert) {
this.replication = {
dr: {
clusterId: 'abc',
state: 'running',
},
performance: {
clusterId: 'def',
isPrimary: true,
},
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'DR primary')).hasText('DR primary');
assert
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
this.replication = {
dr: {
clusterId: 'abc',
state: 'running',
},
performance: {
clusterId: 'def',
isPrimary: false,
},
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);
assert
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance secondary'))
.hasText('Performance secondary');
});
test('it should show empty state', async function (assert) {
this.replication = {
dr: {
@@ -163,10 +221,10 @@ module('Integration | Component | dashboard/replication-card', function (hooks)
};
await render(
hbs`
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
<Dashboard::ReplicationCard
@replication={{this.replication}}
@version={{this.version}}
@updatedAt={{this.updatedAt}}
@refresh={{this.refresh}} />
`
);

View File

@@ -50,11 +50,13 @@ module('Integration | Component | dashboard/replication-state-text', function (h
@clusterStates={{this.clusterStates}} />
`
);
assert.dom(SELECTORS.getReplicationTitle('dr-perf', 'Perf primary')).hasText('Perf primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Perf primary')).hasText('running');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Perf primary', 'x-circle'))
.dom(SELECTORS.getReplicationTitle('dr-perf', 'Performance primary'))
.hasText('Performance primary');
assert.dom(SELECTORS.getStateTooltipTitle('dr-perf', 'Performance primary')).hasText('running');
assert.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle')).exists();
assert
.dom(SELECTORS.getStateTooltipIcon('dr-perf', 'Performance primary', 'x-circle'))
.hasClass('has-text-danger');
});
});

View File

@@ -100,11 +100,16 @@ module('Integration | Component | dashboard/secrets-engines-card', function (hoo
};
});
test('it should display only five secrets engines', async function (assert) {
test('it should display only five secrets engines and show help text for more than 5 engines', async function (assert) {
await this.renderComponent();
assert.dom(SELECTORS.cardTitle).hasText('Secrets engines');
assert.dom(SELECTORS.secretEnginesTableRows).exists({ count: 5 });
assert.dom('[data-test-secrets-engines-card-show-all]').exists();
assert.dom('[data-test-secrets-engine-total-help-text]').exists();
assert
.dom('[data-test-secrets-engine-total-help-text]')
.hasText(
`Showing 5 out of ${this.secretsEngines.length} secret engines. Navigate to details to view more.`
);
});
test('it should display the secrets engines accessor and path', async function (assert) {