Backport UI: General settings Integration and Acceptance Tests. into ce/main (#9382)

* UI: General settings Integration and Acceptance Tests. (#9363)

* General settings integration tests

* Add page header integration tests

* Add page header test for plugin settings as a tab too

* More tests!

* Acceptance tests!

* Add more acceptnace tests

* Add copywrite headers

* Fix linting error

* Fix accessibility errors

* Remove unused vars

* Put mock secret engine back into beforeHook

* Add enterprise to key management test (#9392)

---------

Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com>
This commit is contained in:
Vault Automation 2025-09-17 19:47:58 -04:00 committed by GitHub
parent 8debe72733
commit 02dd079e91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 561 additions and 26 deletions

View File

@ -2,7 +2,13 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<Hds::Card::Container @level="mid" @hasBorder={{true}} class="has-padding-m has-top-bottom-margin" ...attributes>
<Hds::Card::Container
@level="mid"
@hasBorder={{true}}
class="has-padding-m has-top-bottom-margin"
data-test-card-container="lease-duration"
...attributes
>
<Hds::Text::Display @size="300" @tag="h2" class="has-bottom-margin-s hds-foreground-strong">Lease Duration</Hds::Text::Display>
<Hds::Layout::Flex @align="start" @gap="16" class="has-bottom-margin-m">

View File

@ -2,7 +2,13 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<Hds::Card::Container @level="mid" @hasBorder={{true}} class="has-padding-m has-top-bottom-margin-12" ...attributes>
<Hds::Card::Container
@level="mid"
@hasBorder={{true}}
class="has-padding-m has-top-bottom-margin-12"
data-test-card-container="metadata"
...attributes
>
<Hds::Text::Display @size="300" @tag="h2" class="has-bottom-margin-s hds-foreground-strong">Metadata</Hds::Text::Display>
<div class="flex gap-16 is-flex-column has-top-padding-s">
@ -13,6 +19,7 @@
<Hds::Layout::Flex @gap="8">
<Hds::Form::TextInput::Field
@value={{@model.secretsEngine.path}}
aria-label="Secret engine path"
autocomplete="off"
disabled
class="path-input-text"
@ -31,6 +38,7 @@
<Hds::Layout::Flex @gap="8">
<Hds::Form::TextInput::Field
@value={{@model.secretsEngine.accessor}}
aria-label="Secret engine accessor"
autocomplete="off"
disabled
name="Accessor"

View File

@ -2,7 +2,13 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<Hds::Card::Container @level="mid" @hasBorder={{true}} class="has-padding-m has-top-bottom-margin" ...attributes>
<Hds::Card::Container
@level="mid"
@hasBorder={{true}}
class="has-padding-m has-top-bottom-margin"
data-test-card-container="security"
...attributes
>
<Hds::Form::Toggle::Group as |G|>
<G.Legend>
<Hds::Text::Display
@ -12,13 +18,13 @@
>Security</Hds::Text::Display>
</G.Legend>
{{! TODO: Confirm with design to see if we want these two fields to be disabled }}
<G.ToggleField name="local" checked={{@model.secretsEngine.local}} disabled as |F|>
<F.Label>Local</F.Label>
<F.HelperText>Secrets stay in one cluster and are not replicated.</F.HelperText>
<G.ToggleField name="local" checked={{@model.secretsEngine.local}} disabled data-test-input="local" as |F|>
<F.Label data-test-label="local">Local</F.Label>
<F.HelperText data-test-helper-text="local">Secrets stay in one cluster and are not replicated.</F.HelperText>
</G.ToggleField>
<G.ToggleField name="seal-wrap" checked={{@model.secretsEngine.seal_wrap}} disabled as |F|>
<F.Label>Seal wrap</F.Label>
<F.HelperText>Wrap secrets with an additional encryption layer using a seal.</F.HelperText>
<G.ToggleField name="seal-wrap" checked={{@model.secretsEngine.seal_wrap}} disabled data-test-input="seal_wrap" as |F|>
<F.Label data-test-label="seal_wrap">Seal wrap</F.Label>
<F.HelperText data-test-helper-text="seal_wrap">Wrap secrets with an additional encryption layer using a seal.</F.HelperText>
</G.ToggleField>
</Hds::Form::Toggle::Group>
</Hds::Card::Container>

View File

@ -2,7 +2,13 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<Hds::Card::Container @level="mid" @hasBorder={{true}} class="has-padding-m has-top-bottom-margin" ...attributes>
<Hds::Card::Container
@level="mid"
@hasBorder={{true}}
class="has-padding-m has-top-bottom-margin"
data-test-card-container="version"
...attributes
>
<Hds::Text::Display @size="300" @tag="h2" class="has-bottom-margin-s hds-foreground-strong">Version</Hds::Text::Display>
<Hds::Layout::Grid @columnMinWidth="10%" @gap="12" {{style height="100%" grid-template-rows="min-content"}} as |LG|>
@ -15,9 +21,13 @@
<Hds::Text::Body
@tag="p"
class="hds-border-strong has-side-padding-8 border-radius-4 is-inline-block has-bottom-margin-s"
data-test-engine-type
>{{@model.secretsEngine.type}}</Hds::Text::Body>
{{! TODO: Verify if we want to display the full version or chop down ie. v0.17.1 vs v0.17.1-0.230942309423094... }}
<Hds::Text::Body @tag="p">{{@model.secretsEngine.running_plugin_version}}</Hds::Text::Body>
<Hds::Text::Body
@tag="p"
data-test-engine-current-version
>{{@model.secretsEngine.running_plugin_version}}</Hds::Text::Body>
</LG.Item>
</Hds::Layout::Grid>
@ -25,7 +35,7 @@
<Hds::Separator />
<Hds::Layout::Flex @isInline="true">
<Hds::Form::Select::Field name="plugin-version" as |F|>
<Hds::Form::Select::Field name="plugin-version" data-test-versions-dropdown as |F|>
<F.Label>Update version to:</F.Label>
<F.Options>
<option value="">Select version</option>

View File

@ -26,14 +26,14 @@
<div class="tabs-container box is-marginless is-fullwidth is-paddingless">
<nav class="tabs" aria-label={{@model.secretsEngine.id}}>
<ul>
<li>
<li data-test-tab="general-settings">
<LinkTo @route="vault.cluster.secrets.backend.configuration.general-settings" @model={{@model.secretsEngine.id}}>
General settings
</LinkTo>
</li>
{{! If engine is not configurable, hide plugin settings link }}
{{#if (get (engines-display-data @model.secretsEngine.type) "isConfigurable")}}
<li>
<li data-test-tab="plugin-settings">
<LinkTo
@route="vault.cluster.secrets.backend.configuration.plugin-settings"
@model={{@model.secretsEngine.id}}

View File

@ -2,7 +2,6 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<SecretEngine::PageHeader @model={{@model}} />
{{#if this.saveGeneralSettings.isRunning}}
@ -46,11 +45,11 @@
{{/if}}
{{#if this.showUnsavedChangesModal}}
<Hds::Modal id="unsavedChangesModal" @onClose={{this.closeUnsavedChangesModal}} as |M|>
<M.Header>
<Hds::Modal id="unsavedChangesModal" @onClose={{this.closeUnsavedChangesModal}} data-test-modal="unsaved-changes" as |M|>
<M.Header data-test-modal-header="unsaved-changes">
Unsaved changes
</M.Header>
<M.Body>
<M.Body data-test-modal-body="unsaved-changes">
<p class="hds-typography-body-300 hds-foreground-primary">You've made changes to the following
<Hds::Text::Display>{{@model.secretsEngine.id}}</Hds::Text::Display>
settings:</p>
@ -64,8 +63,19 @@
</M.Body>
<M.Footer>
<Hds::ButtonSet>
<Hds::Button type="button" @text="Save changes" {{on "click" (perform this.saveGeneralSettings)}} />
<Hds::Button type="button" @text="Discard changes" @color="secondary" {{on "click" this.discardChanges}} />
<Hds::Button
type="button"
@text="Save changes"
data-test-button="save"
{{on "click" (perform this.saveGeneralSettings)}}
/>
<Hds::Button
type="button"
@text="Discard changes"
@color="secondary"
data-test-button="discard"
{{on "click" this.discardChanges}}
/>
</Hds::ButtonSet>
</M.Footer>
</Hds::Modal>

View File

@ -3,9 +3,9 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Hds::Form::Field @layout="vertical" @isInvalid={{this.errorMessage}} as |F|>
<F.Label>{{this.formField.label}}</F.Label>
<F.HelperText>{{this.formField.helperText}}</F.HelperText>
<Hds::Form::Field @layout="vertical" @isInvalid={{this.errorMessage}} data-test-ttl-picker-v2 as |F|>
<F.Label data-test-form-field-label={{@ttlKey}}>{{this.formField.label}}</F.Label>
<F.HelperText data-test-help-text={{@ttlKey}}>{{this.formField.helperText}}</F.HelperText>
<F.Control>
<Hds::SegmentedGroup as |SG|>
<SG.TextInput
@ -14,9 +14,18 @@
@value={{this.time}}
{{on "input" this.setTtlTime}}
name="{{@ttlKey}}-time"
aria-label={{concat this.formField.label "time"}}
autocomplete="off"
data-test-input={{@ttlKey}}
/>
<SG.Select @width="100px" name="{{@ttlKey}}-unit" {{on "input" this.setUnit}} as |S|>
<SG.Select
@width="100px"
name="{{@ttlKey}}-unit"
{{on "input" this.setUnit}}
aria-label={{concat this.formField.label "unit"}}
data-test-select={{@ttlKey}}
as |S|
>
<S.Options>
{{#each this.unitOptions as |unit|}}
<option value={{unit.value}} selected={{eq unit.value this.selectedUnit}}>{{unit.label}}</option>
@ -26,6 +35,6 @@
</Hds::SegmentedGroup>
</F.Control>
{{#if this.errorMessage}}
<F.Error>{{this.errorMessage}}</F.Error>
<F.Error data-test-message-error>{{this.errorMessage}}</F.Error>
{{/if}}
</Hds::Form::Field>

View File

@ -63,11 +63,16 @@
{{! TODO: Hook dropdown actions to the appropriate routes & actions }}
<Hds::Dropdown as |D|>
<D.ToggleButton @text="Manage" @color="secondary" data-test-manage-dropdown />
<D.Interactive @icon="settings" @route="vault.cluster.secrets.backend.configuration.index">Configure</D.Interactive>
<D.Interactive
@icon="settings"
@route="vault.cluster.secrets.backend.configuration.index"
data-test-manage-dropdown-item="Configure"
>Configure</D.Interactive>
<D.Interactive
{{on "click" (fn (mut this.engineToDisable) this.backendModel)}}
@color="critical"
@icon="trash"
data-test-manage-dropdown-item="Delete"
>Delete</D.Interactive>
</Hds::Dropdown>

View File

@ -0,0 +1,85 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { settled, click, fillIn, visit, currentRouteName } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
import { login } from 'vault/tests/helpers/auth/auth-helpers';
import { mountBackend } from 'vault/tests/helpers/components/mount-backend-form-helpers';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { SELECTORS } from 'vault/tests/helpers/secret-engine/general-settings-selectors';
import { create } from 'ember-cli-page-object';
import consoleClass from 'vault/tests/pages/components/console/ui-panel';
const consoleComponent = create(consoleClass);
module('Acceptance | Enterprise | keymgmt-configuration-workflow', function (hooks) {
setupApplicationTest(hooks);
hooks.beforeEach(function () {
return login();
});
test('it should display keymgmt configuration and tune keymgmt in the general settings form', async function (assert) {
await consoleComponent.runCommands([
// delete any previous mount with same name
'delete sys/mounts/keymgmt',
]);
const keymgmtType = 'keymgmt';
await mountSecrets.visit();
await settled();
await mountBackend(keymgmtType, keymgmtType);
await click(SELECTORS.manageDropdown);
await click(SELECTORS.manageDropdownItem('Configure'));
assert
.dom(GENERAL.hdsPageHeaderTitle)
.hasText(`${keymgmtType} configuration`, 'displays configuration title');
assert.dom(GENERAL.tab('general-settings')).hasText(`General settings`);
assert.dom(GENERAL.cardContainer('version')).exists('version card exists');
assert.dom(SELECTORS.versionCard.engineType).hasText(keymgmtType, 'shows keymgmt engine type');
assert.dom(GENERAL.cardContainer('metadata')).exists('metadata card exists');
assert.dom(GENERAL.inputByAttr('path')).hasValue(`${keymgmtType}/`, 'show path value');
assert.dom(GENERAL.cardContainer('lease-duration')).exists('lease-duration card exists');
assert.dom(GENERAL.cardContainer('security')).exists('security card exists');
// fill in values to tune
await fillIn(GENERAL.inputByAttr('default_lease_ttl'), 10);
await fillIn(GENERAL.selectByAttr('default_lease_ttl'), 'm');
await fillIn(GENERAL.inputByAttr('max_lease_ttl'), 15);
await fillIn(GENERAL.selectByAttr('max_lease_ttl'), 'd');
await fillIn(GENERAL.textareaByAttr('description'), 'Some awesome description.');
await click(GENERAL.submitButton);
// after submitting go to list and back to configuration
await visit(`/vault/secrets/${keymgmtType}/list`);
await visit(`/vault/secrets/${keymgmtType}/configuration`);
// confirm that submitted values were saved and prepopulated with those saved values
assert
.dom(GENERAL.textareaByAttr('description'))
.hasValue('Some awesome description.', 'description was tuned');
assert.dom(GENERAL.inputByAttr('default_lease_ttl')).hasValue('10', 'default ttl value was tuned');
assert
.dom(GENERAL.selectByAttr('default_lease_ttl'))
.hasValue('m', 'default ttl value was tuned and shows correct unit');
assert.dom(GENERAL.inputByAttr('max_lease_ttl')).hasValue('15', 'max ttl value was tuned');
assert
.dom(GENERAL.selectByAttr('max_lease_ttl'))
.hasValue('d', 'max ttl value was tuned and shows correct unit');
// navigate back to keymgmt list view to delete the engine from the manage dropdown
await visit(`/vault/secrets/${keymgmtType}/list`);
await click(SELECTORS.manageDropdown);
await click(SELECTORS.manageDropdownItem('Delete'));
await click(GENERAL.confirmButton);
assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backends');
await consoleComponent.runCommands([
// cleanup after
'delete sys/mounts/keymgmt',
]);
});
});

View File

@ -13,6 +13,8 @@ export const GENERAL = {
breadcrumbs: '[data-test-breadcrumbs]',
headerContainer: 'header.page-header',
title: '[data-test-page-title]',
hdsPageHeaderTitle: '.hds-page-header__title',
hdsPageHeaderDescription: '.hds-page-header__description',
/* ────── Tabs & Navigation ────── */
tab: (name: string) => `[data-test-tab="${name}"]`,
@ -73,6 +75,7 @@ export const GENERAL = {
toggleInput: (attr: string) => `[data-test-toggle-input="${attr}"]`,
textToggle: '[data-test-text-toggle]',
filter: (name: string) => `[data-test-filter="${name}"]`,
textareaByAttr: (attr: string) => `textarea[name="${attr}"]`,
/* ────── Code Blocks / Editor ────── */
codemirror: `[data-test-component="code-mirror-modifier"]`,
@ -159,6 +162,11 @@ export const GENERAL = {
/* ────── Modals & Flyouts ────── */
flyout: '[data-test-flyout]',
modal: {
container: (title: string) => `[data-test-modal=${title}]`,
header: (title: string) => `[data-test-modal-header=${title}]`,
body: (title: string) => `[data-test-modal-body=${title}]`,
},
/* ────── Misc ────── */
icon: (name: string) => (name ? `[data-test-icon="${name}"]` : '[data-test-icon]'),

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export const SELECTORS = {
manageDropdown: '[data-test-manage-dropdown]',
manageDropdownItem: (name: string) => `[data-test-manage-dropdown-item="${name}"]`,
label: (name: string) => `[data-test-label="${name}"]`,
helperText: (name: string) => `[data-test-helper-text="${name}"]`,
ttlPickerV2: '[data-test-ttl-picker-v2]',
versionCard: {
engineType: '[data-test-engine-type]',
currentVersion: '[data-test-engine-current-version]',
versionsDropdown: `[data-test-versions-dropdown]`,
},
};

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export const keyMgmtMockModel = {
secretsEngine: {
accessor: 'keymgmt_accessor',
config: {
default_lease_ttl: 2073600,
force_no_cache: false,
listing_visibility: 'hidden',
max_lease_ttl: 4320000,
},
description: 'hello',
external_entropy_access: false,
local: true,
options: {},
path: 'keymgmt/',
plugin_version: '',
running_plugin_version: 'v0.17.1+builtin',
running_sha256: '',
seal_wrap: false,
type: 'keymgmt',
uuid: '4ea92618-5b52-f89a-9cbe-b65dc7e65689',
id: 'keymgmt',
backendConfigurationLink: `vault.cluster.secrets.backend.configuration`,
},
versions: ['v0.17.1+builtin'],
};

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { SELECTORS } from 'vault/tests/helpers/secret-engine/general-settings-selectors';
module('Integration | Component | SecretEngine::Card::LeaseDuration', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows default and max ttl pickers', async function (assert) {
assert.expect(5);
await render(hbs`
<SecretEngine::Card::LeaseDuration @model={{this.model}} />
`);
assert.dom(SELECTORS.ttlPickerV2).exists({ count: 2 });
assert.dom(GENERAL.fieldLabelbyAttr('default_lease_ttl')).hasText('Time-to-live (TTL)');
assert.dom(GENERAL.fieldLabelbyAttr('max_lease_ttl')).hasText('Maximum Time-to-live (TTL)');
assert.dom(GENERAL.helpTextByAttr('default_lease_ttl')).hasText('Standard expiry deadline.');
assert.dom(GENERAL.helpTextByAttr('max_lease_ttl')).hasText('Maximum possible extension for expiry.');
});
});

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render, fillIn } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
module('Integration | Component | SecretEngine::Card::Metadata', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows metadata card information', async function (assert) {
assert.expect(4);
await render(hbs`
<SecretEngine::Card::Metadata @model={{this.model}} />
`);
assert.dom(`${GENERAL.cardContainer('metadata')} h2`).hasText('Metadata');
assert.dom(GENERAL.inputByAttr('path')).hasValue(this.model.secretsEngine.path);
assert.dom(GENERAL.inputByAttr('accessor')).hasValue(this.model.secretsEngine.accessor);
await fillIn(GENERAL.textareaByAttr('description'), 'Some awesome description');
assert.dom(GENERAL.textareaByAttr('description')).hasValue('Some awesome description');
});
});

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { SELECTORS } from 'vault/tests/helpers/secret-engine/general-settings-selectors';
module('Integration | Component | SecretEngine::Card::Security', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows security card information', async function (assert) {
assert.expect(7);
await render(hbs`
<SecretEngine::Card::Security @model={{this.model}} />
`);
assert.dom(`${GENERAL.cardContainer('security')} h2`).hasText('Security');
assert.dom(SELECTORS.label('local')).hasText('Local');
assert.dom(SELECTORS.helperText('local')).hasText('Secrets stay in one cluster and are not replicated.');
assert.dom(GENERAL.inputByAttr('local')).isChecked();
assert.dom(SELECTORS.label('seal_wrap')).hasText('Seal wrap');
assert
.dom(SELECTORS.helperText('seal_wrap'))
.hasText('Wrap secrets with an additional encryption layer using a seal.');
assert.dom(GENERAL.inputByAttr('seal_wrap')).isNotChecked();
});
});

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
import { SELECTORS } from 'vault/tests/helpers/secret-engine/general-settings-selectors';
module('Integration | Component | SecretEngine::Card::Version', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows version card information', async function (assert) {
assert.expect(4);
await render(hbs`
<SecretEngine::Card::Version @model={{this.model}} />
`);
assert.dom(`${GENERAL.cardContainer('version')} h2`).hasText('Version');
assert.dom(SELECTORS.engineType).hasAnyText(keyMgmtMockModel.secretsEngine.type);
assert.dom(SELECTORS.currentVersion).hasAnyText(keyMgmtMockModel.secretsEngine.running_plugin_version);
assert.dom(SELECTORS.versionCard.versionsDropdown).doesNotExist();
});
});

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import engineDisplayData from 'vault/helpers/engines-display-data';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
module('Integration | Component | SecretEngine::PageHeader', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows page header title, description, and general settings tab', async function (assert) {
assert.expect(4);
await render(hbs`
<SecretEngine::PageHeader @model={{this.model}}/>
`);
assert.dom(GENERAL.tab('general-settings')).exists('contains general settings tab');
assert.dom(GENERAL.tab('plugin-settings')).doesNotExist('does not contain plugin settings tab');
assert
.dom(GENERAL.hdsPageHeaderTitle)
.hasText(`${this.model.secretsEngine.id} configuration`, 'displays page header title');
assert
.dom(GENERAL.hdsPageHeaderDescription)
.hasText(
engineDisplayData(this.model.secretsEngine.type).displayName,
'displays page header description'
);
});
test('it shows page header title, description, and general and plugin settings tab for configurable secret engines', async function (assert) {
assert.expect(4);
this.model.secretsEngine = {
type: 'aws',
id: 'aws',
config: {
region: 'us-west-2',
access_key: '123-key',
iam_endpoint: 'iam-endpoint',
sts_endpoint: 'sts-endpoint',
max_retries: 1,
},
};
await render(hbs`
<SecretEngine::PageHeader @model={{this.model}}/>
`);
assert.dom(GENERAL.tab('general-settings')).exists('contains general settings tab');
assert.dom(GENERAL.tab('plugin-settings')).exists('contains plugin settings tab');
assert
.dom(GENERAL.hdsPageHeaderTitle)
.hasText(`${this.model.secretsEngine.id} configuration`, 'displays page header title');
assert
.dom(GENERAL.hdsPageHeaderDescription)
.hasText(
engineDisplayData(this.model.secretsEngine.type).displayName,
'displays page header description'
);
});
});

View File

@ -0,0 +1,84 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render, click, fillIn } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
const keyManagementMockModel = {
secretsEngine: {
accessor: 'keymgmt_accessor',
config: {
default_lease_ttl: 2073600,
force_no_cache: false,
listing_visibility: 'hidden',
max_lease_ttl: 4320000,
},
description: 'hello',
external_entropy_access: false,
local: true,
options: {},
path: 'keymgmt/',
plugin_version: '',
running_plugin_version: 'v0.17.1+builtin',
running_sha256: '',
seal_wrap: false,
type: 'keymgmt',
uuid: '4ea92618-5b52-f89a-9cbe-b65dc7e65689',
id: 'keymgmt',
backendConfigurationLink: `vault.cluster.secrets.backend.configuration`,
},
versions: ['v0.17.1+builtin'],
};
module('Integration | Component | SecretEngine::Page::GeneralSettings', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyManagementMockModel;
});
test('it shows general settings form', async function (assert) {
assert.expect(4);
await render(hbs`
<SecretEngine::Page::GeneralSettings @model={{this.model}} />
`);
assert.dom(GENERAL.cardContainer('lease-duration')).exists(`Lease duration card exists`);
assert.dom(GENERAL.cardContainer('security')).exists(`Security card exists`);
assert.dom(GENERAL.cardContainer('version')).exists(`Version card exists`);
assert.dom(GENERAL.cardContainer('metadata')).exists(`Metadata card exists`);
});
test('it shows unsaved changes modal', async function (assert) {
assert.expect(3);
await render(hbs`
<SecretEngine::Page::GeneralSettings @model={{this.model}} />
`);
await fillIn(GENERAL.textareaByAttr('description'), 'Some awesome description');
await click(GENERAL.cancelButton);
assert.dom(GENERAL.modal.container('unsaved-changes')).exists('Unsaved changes exists');
assert.dom(GENERAL.modal.header('unsaved-changes')).hasText('Unsaved changes');
assert
.dom(GENERAL.modal.body('unsaved-changes'))
.hasText(
`You've made changes to the following ${this.model.secretsEngine.id} settings: Description Would you like to apply them?`
);
});
test('it does not show unsaved changes modal when there are no unsaved changes', async function (assert) {
assert.expect(1);
await render(hbs`
<SecretEngine::Page::GeneralSettings @model={{this.model}} />
`);
await click(GENERAL.cancelButton);
assert.dom(GENERAL.modal.container('unsaved-changes')).doesNotExist('Unsaved changes does not show');
});
});

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render, fillIn } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { keyMgmtMockModel } from 'vault/tests/helpers/secret-engine/mocks';
module('Integration | Component | SecretEngine::TtlPickerV2', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.model = keyMgmtMockModel;
});
test('it shows default ttl picker', async function (assert) {
assert.expect(4);
this.ttlKey = 'default_lease_ttl';
await render(hbs`
<SecretEngine::TtlPickerV2 @model={{this.model}} @ttlKey={{this.ttlKey}} />
`);
assert.dom(GENERAL.fieldLabelbyAttr(this.ttlKey)).hasText('Time-to-live (TTL)');
assert.dom(GENERAL.helpTextByAttr(this.ttlKey)).hasText('Standard expiry deadline.');
await fillIn(GENERAL.inputByAttr(this.ttlKey), 5);
await fillIn(GENERAL.selectByAttr(this.ttlKey), 'm');
assert.dom(GENERAL.inputByAttr(this.ttlKey)).hasValue('5');
assert.dom(GENERAL.selectByAttr(this.ttlKey)).hasValue('m');
});
test('it shows max ttl picker', async function (assert) {
assert.expect(4);
this.ttlKey = 'max_lease_duration';
await render(hbs`
<SecretEngine::TtlPickerV2 @model={{this.model}} @ttlKey={{this.ttlKey}} />
`);
assert.dom(GENERAL.fieldLabelbyAttr(this.ttlKey)).hasText('Maximum Time-to-live (TTL)');
assert.dom(GENERAL.helpTextByAttr(this.ttlKey)).hasText('Maximum possible extension for expiry.');
await fillIn(GENERAL.inputByAttr(this.ttlKey), 10);
await fillIn(GENERAL.selectByAttr(this.ttlKey), 'm');
assert.dom(GENERAL.inputByAttr(this.ttlKey)).hasValue('10');
assert.dom(GENERAL.selectByAttr(this.ttlKey)).hasValue('m');
});
test('it shows an error message if ttl picker time is not a number value', async function (assert) {
assert.expect(2);
this.ttlKey = 'max_lease_duration';
await render(hbs`
<SecretEngine::TtlPickerV2 @model={{this.model}} @ttlKey={{this.ttlKey}} />
`);
await fillIn(GENERAL.inputByAttr(this.ttlKey), 'some text');
await fillIn(GENERAL.selectByAttr(this.ttlKey), 'm');
assert.dom(GENERAL.messageError).exists();
assert.dom(GENERAL.messageError).hasText('Only use numbers for this setting.');
});
});