From 601e2cea29f46dacdbb2e6842d6f83a5152ec37d Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Thu, 18 Dec 2025 09:29:04 -0700 Subject: [PATCH] [UI][VAULT-40916] Update page headers ui/app/components (#11104) (#11417) * Update page headers for database components * Update generate forms and license info * Mount backend form page headers.. * Raft page headers * Update role-aws-edit page header * Tools, role-ssh, mount-backend-form and fix tests * OIDC, TOTP and userpass page headers and tsts * odic, keymgmt, and dashboard * Fix dashboard title tests * Fix breadcrumbs * MFA, idenitity, and generated item * Fix mfa enforcement page header tests * Move tabs out of page header Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com> --- ui/app/components/alphabet-edit.hbs | 21 ++------- ui/app/components/alphabet-edit.js | 10 +++++ .../dashboard/vault-version-title.hbs | 19 ++++---- ui/app/components/database-connection.hbs | 19 ++------ ui/app/components/database-connection.js | 10 +++++ ui/app/components/database-role-edit.hbs | 21 +++------ ui/app/components/database-role-edit.js | 10 +++++ .../generate-credentials-database.hbs | 22 +++------- .../generate-credentials-database.js | 10 +++++ .../components/generate-credentials-totp.hbs | 24 +++-------- .../components/generate-credentials-totp.js | 12 ++++++ ui/app/components/generate-credentials.hbs | 25 +++-------- ui/app/components/generate-credentials.ts | 13 ++++++ ui/app/components/generated-item-list.hbs | 19 +++----- ui/app/components/generated-item-list.js | 10 +++++ ui/app/components/generated-item.hbs | 42 +++++++----------- ui/app/components/generated-item.js | 14 ++++++ ui/app/components/identity/entity-nav.hbs | 40 ++++++++--------- ui/app/components/keymgmt/key-edit.hbs | 21 ++------- ui/app/components/keymgmt/key-edit.js | 11 +++++ ui/app/components/keymgmt/provider-edit.hbs | 20 ++------- ui/app/components/keymgmt/provider-edit.js | 16 +++++++ ui/app/components/license-info.hbs | 6 +-- .../mfa/mfa-login-enforcement-header.hbs | 30 ++++--------- .../mfa/mfa-login-enforcement-header.js | 12 ++++++ ui/app/components/mount-backend-form.hbs | 25 ++--------- ui/app/components/mount-backend-form.ts | 16 +++++++ .../components/mount/secrets-engine-form.hbs | 15 +++---- .../components/mount/secrets-engine-form.ts | 15 +++++++ ui/app/components/not-found.hbs | 9 +--- ui/app/components/oidc/client-form.hbs | 28 +++--------- ui/app/components/oidc/client-form.js | 11 +++++ ui/app/components/oidc/provider-form.hbs | 27 +++--------- ui/app/components/oidc/provider-form.js | 11 +++++ ui/app/components/oidc/scope-form.hbs | 31 +++---------- ui/app/components/oidc/scope-form.js | 11 +++++ .../page/userpass-reset-password.hbs | 8 +--- ui/app/components/raft-storage-restore.hbs | 20 +++------ ui/app/components/role-aws-edit.hbs | 21 ++------- ui/app/components/role-aws-edit.js | 17 ++++++++ ui/app/components/role-ssh-edit.hbs | 20 ++------- ui/app/components/role-ssh-edit.js | 17 ++++++++ .../secret-engine/page/general-settings.hbs | 16 +++---- .../secret-engine/page/plugin-settings.hbs | 12 +++--- ui/app/components/tools/hash.hbs | 8 +--- ui/app/components/tools/lookup.hbs | 8 +--- ui/app/components/tools/random.hbs | 8 +--- ui/app/components/tools/rewrap.hbs | 8 +--- ui/app/components/tools/unwrap.hbs | 8 +--- ui/app/components/tools/wrap.hbs | 8 +--- ui/app/components/totp-edit.hbs | 18 ++------ ui/app/components/totp-edit.js | 13 ++++++ ui/app/components/transform-role-edit.hbs | 20 ++------- ui/app/components/transform-role-edit.js | 17 ++++++++ ui/app/components/transform-template-edit.hbs | 20 ++------- ui/app/components/transform-template-edit.js | 16 +++++++ ui/app/components/transformation-edit.hbs | 20 ++------- ui/app/components/transformation-edit.js | 17 ++++++++ ui/app/components/transit-edit.hbs | 21 +++------ ui/app/components/transit-edit.js | 16 +++++++ .../secrets/backend/configuration/edit.hbs | 12 +++--- ui/lib/core/addon/components/page/header.hbs | 12 ++---- ui/lib/ldap/addon/components/ldap-header.hbs | 35 ++++++++------- .../addon/components/page/library/details.hbs | 15 +++---- .../pki/addon/components/pki-page-header.hbs | 43 +++++++++---------- ui/lib/pki/addon/templates/error.hbs | 21 +++++---- ui/tests/acceptance/auth/auth-list-test.js | 5 ++- ui/tests/acceptance/dashboard-test.js | 11 ++--- .../acceptance/mfa-login-enforcement-test.js | 2 +- ui/tests/acceptance/oidc-auth-method-test.js | 6 +-- .../acceptance/reduced-disclosure-test.js | 7 ++- ui/tests/acceptance/reset-password-test.js | 2 +- .../secrets/backend/aws/aws-test.js | 2 +- .../secrets/backend/ssh/roles-test.js | 4 +- .../secrets/backend/totp/key-test.js | 9 ++-- .../components/dashboard/overview-test.js | 7 +-- .../components/keymgmt/key-edit-test.js | 7 +-- .../components/keymgmt/provider-edit-test.js | 9 ++-- .../mfa-login-enforcement-header-test.js | 11 ++--- .../components/mount-backend-form-test.js | 2 +- .../components/oidc/client-form-test.js | 6 +-- .../components/oidc/provider-form-test.js | 6 +-- .../components/oidc/scope-form-test.js | 4 +- .../components/totp/key-form-test.js | 4 +- 84 files changed, 596 insertions(+), 659 deletions(-) diff --git a/ui/app/components/alphabet-edit.hbs b/ui/app/components/alphabet-edit.hbs index 1f91fd4951..4bab79d111 100644 --- a/ui/app/components/alphabet-edit.hbs +++ b/ui/app/components/alphabet-edit.hbs @@ -2,24 +2,11 @@ Copyright IBM Corp. 2016, 2025 SPDX-License-Identifier: BUSL-1.1 }} - - - + + <:breadcrumbs> - - -

- {{#if (eq @mode "create")}} - Create Alphabet - {{else if (eq @mode "edit")}} - Edit Alphabet - {{else}} - Alphabet - {{@model.id}} - {{/if}} -

-
-
+ + {{#if (eq @mode "show")}} diff --git a/ui/app/components/alphabet-edit.js b/ui/app/components/alphabet-edit.js index 64fea376f8..3f8e9db83f 100644 --- a/ui/app/components/alphabet-edit.js +++ b/ui/app/components/alphabet-edit.js @@ -41,6 +41,16 @@ export default class AlphabetEditComponent extends Component { ]; } + get title() { + if (this.args?.mode === 'create') { + return 'Create Alphabet'; + } else if (this.args?.mode === 'edit') { + return 'Edit Alphabet'; + } else { + return this.args?.model?.id; + } + } + transition(route = 'show') { this.errorMessage = ''; const { backend, id } = this.args.model; diff --git a/ui/app/components/dashboard/vault-version-title.hbs b/ui/app/components/dashboard/vault-version-title.hbs index af6b1dc23c..fe32f375af 100644 --- a/ui/app/components/dashboard/vault-version-title.hbs +++ b/ui/app/components/dashboard/vault-version-title.hbs @@ -3,15 +3,12 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Vault - {{@version.versionDisplay}} - {{#if @version.isEnterprise}} - - {{/if}} -

-
-
+ + <:badges> + {{#if @version.isEnterprise}} + + {{/if}} + + +
\ No newline at end of file diff --git a/ui/app/components/database-connection.hbs b/ui/app/components/database-connection.hbs index 9ba8a6ae88..3554a1a00e 100644 --- a/ui/app/components/database-connection.hbs +++ b/ui/app/components/database-connection.hbs @@ -3,22 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq @mode "create")}} - Create Connection - {{else if (eq @mode "edit")}} - Edit Connection - {{else}} - {{@model.id}} - {{/if}} -

-
-
+ + {{#if @model.isAvailablePlugin}} {{#if (eq @mode "show")}} diff --git a/ui/app/components/database-connection.js b/ui/app/components/database-connection.js index b75a88d29d..173892cbbe 100644 --- a/ui/app/components/database-connection.js +++ b/ui/app/components/database-connection.js @@ -33,6 +33,16 @@ export default class DatabaseConnectionEdit extends Component { @tracked showSaveModal = false; // used for create mode + get title() { + if (this.args?.mode === 'create') { + return 'Create Connection'; + } else if (this.args?.mode === 'edit') { + return 'Edit Connection'; + } else { + return this.args?.model?.id; + } + } + rotateCredentials(backend, name) { const adapter = this.store.adapterFor('database/connection'); return adapter.rotateRootCredentials(backend, name); diff --git a/ui/app/components/database-role-edit.hbs b/ui/app/components/database-role-edit.hbs index 79368ff6b9..17cf745a7a 100644 --- a/ui/app/components/database-role-edit.hbs +++ b/ui/app/components/database-role-edit.hbs @@ -2,22 +2,13 @@ Copyright IBM Corp. 2016, 2025 SPDX-License-Identifier: BUSL-1.1 }} - - + + + <:breadcrumbs> - - -

- {{#if (eq @mode "create")}} - Create Role - {{else if (eq @mode "edit")}} - Edit Role - {{else}} - {{@model.id}} - {{/if}} -

-
-
+ + + {{#if (eq @mode "show")}} diff --git a/ui/app/components/database-role-edit.js b/ui/app/components/database-role-edit.js index c87fa4a8cb..9d015b867b 100644 --- a/ui/app/components/database-role-edit.js +++ b/ui/app/components/database-role-edit.js @@ -57,6 +57,16 @@ export default class DatabaseRoleEdit extends Component { this.modelValidations = null; } + get title() { + if (this.args?.mode === 'create') { + return 'Create Role'; + } else if (this.args?.mode === 'edit') { + return 'Edit Role'; + } else { + return this.args?.model?.id; + } + } + get warningMessages() { const warnings = {}; const { canCreateDynamic, canCreateStatic, type } = this.args.model; diff --git a/ui/app/components/generate-credentials-database.hbs b/ui/app/components/generate-credentials-database.hbs index dbd941909a..14bb19abe8 100644 --- a/ui/app/components/generate-credentials-database.hbs +++ b/ui/app/components/generate-credentials-database.hbs @@ -3,23 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - -

- {{@roleName}} -

-
-
+ + <:breadcrumbs> + + +
{{! If no role type, that means both static and dynamic requests returned an error }} diff --git a/ui/app/components/generate-credentials-database.js b/ui/app/components/generate-credentials-database.js index 420253d6a5..c30a105a36 100644 --- a/ui/app/components/generate-credentials-database.js +++ b/ui/app/components/generate-credentials-database.js @@ -22,6 +22,16 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; export default class GenerateCredentialsDatabase extends Component { + get breadcrumbs() { + return [ + { + label: this.args.backendPath, + route: 'vault.cluster.secrets.backend.overview', + model: this.args.backendPath, + }, + { label: this.args.roleName }, + ]; + } get errorTitle() { return this.args.model.errorTitle || 'Something went wrong'; } diff --git a/ui/app/components/generate-credentials-totp.hbs b/ui/app/components/generate-credentials-totp.hbs index 7853338bc1..0442436dd7 100644 --- a/ui/app/components/generate-credentials-totp.hbs +++ b/ui/app/components/generate-credentials-totp.hbs @@ -3,25 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - - -

- {{this.title}} -

-
-
+ + <:breadcrumbs> + + +
diff --git a/ui/app/components/generate-credentials-totp.js b/ui/app/components/generate-credentials-totp.js index 137408a3e3..f8b22220fd 100644 --- a/ui/app/components/generate-credentials-totp.js +++ b/ui/app/components/generate-credentials-totp.js @@ -24,6 +24,18 @@ export default class GenerateCredentialsTotp extends Component { this.startTimer.perform(); } + get breadcrumbs() { + return [ + { + label: this.args.backendPath, + route: 'vault.cluster.secrets.backend', + model: this.args.backendPath, + }, + { label: this.args.keyName, route: 'vault.cluster.secrets.backend.show', model: this.args.keyName }, + { label: this.title }, + ]; + } + get remainingTime() { const { totpCodePeriod } = this.args; diff --git a/ui/app/components/generate-credentials.hbs b/ui/app/components/generate-credentials.hbs index 5ca853534a..37ce0bf7ac 100644 --- a/ui/app/components/generate-credentials.hbs +++ b/ui/app/components/generate-credentials.hbs @@ -3,26 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - - - -

- {{this.options.title}} -

-
-
+ + <:breadcrumbs> + + + {{#if this.hasGenerated}}
diff --git a/ui/app/components/generate-credentials.ts b/ui/app/components/generate-credentials.ts index 481ba84ec0..93930ef03b 100644 --- a/ui/app/components/generate-credentials.ts +++ b/ui/app/components/generate-credentials.ts @@ -89,6 +89,19 @@ export default class GenerateCredentials extends Component { return undefined; } + get breadcrumbs() { + return [ + { + label: this.args.backendPath, + route: 'vault.cluster.secrets.backend', + model: this.args.backendPath, + }, + { label: 'Credentials', route: 'vault.cluster.secrets.backend', model: this.args.backendPath }, + { label: this.args.roleName, route: 'vault.cluster.secrets.backend.show', model: this.args.roleName }, + { label: this.options.title }, + ]; + } + get helpText(): string { let message = ''; if (this.cannotReadAwsRole) { diff --git a/ui/app/components/generated-item-list.hbs b/ui/app/components/generated-item-list.hbs index 0ae73d9ba5..0ec1ed8b89 100644 --- a/ui/app/components/generated-item-list.hbs +++ b/ui/app/components/generated-item-list.hbs @@ -3,19 +3,12 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - -

- {{@methodModel.id}} -

-
-
+ + <:breadcrumbs> + + + + {{#let (tabs-for-auth-section @methodModel "authConfig" @paths) as |tabs|}} {{#if tabs.length}}
diff --git a/ui/app/components/generated-item-list.js b/ui/app/components/generated-item-list.js index c638ca6300..4b31e5a275 100644 --- a/ui/app/components/generated-item-list.js +++ b/ui/app/components/generated-item-list.js @@ -29,6 +29,16 @@ export default class GeneratedItemList extends Component { @service pagination; @tracked itemToDelete = null; + get breadcrumbs() { + return [ + { + label: 'Auth Methods', + route: 'vault.cluster.access.methods', + }, + { label: this.args.methodModel.id }, + ]; + } + @action refreshItemList() { const route = getOwner(this).lookup(`route:${this.router.currentRouteName}`); diff --git a/ui/app/components/generated-item.hbs b/ui/app/components/generated-item.hbs index 4b260006f1..14052097de 100644 --- a/ui/app/components/generated-item.hbs +++ b/ui/app/components/generated-item.hbs @@ -3,33 +3,21 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - - {{#if (eq this.mode "show")}} -

- {{this.model.id}} -

- {{else}} -

- {{capitalize this.mode}} - {{singularize this.itemType}} - {{#if (eq this.mode "edit")}} - {{this.model.id}} - {{/if}} -

- {{/if}} -
-
+ + <:breadcrumbs> + + + + {{#if (eq this.mode "show")}} diff --git a/ui/app/components/generated-item.js b/ui/app/components/generated-item.js index 4f11df0ea4..348a77655e 100644 --- a/ui/app/components/generated-item.js +++ b/ui/app/components/generated-item.js @@ -9,6 +9,8 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { task } from 'ember-concurrency'; import { waitFor } from '@ember/test-waiters'; +import { capitalize } from '@ember/string'; +import { singularize } from 'ember-inflector'; /** * @module GeneratedItem @@ -35,6 +37,18 @@ export default Component.extend({ props: computed('model', function () { return this.model.serialize(); }), + + get title() { + const title = `${capitalize(this.mode)} ${singularize(this.itemType)}`; + if (this.mode === 'show') { + return this.model.id; + } else if (this.mode === 'edit') { + return `${title} ${this.model.id}`; + } + + return `${capitalize(this.mode)} ${singularize(this.itemType)}`; + }, + validateForm() { // Only validate on new models because blank passwords will not be updated // in practice this only happens for userpass users diff --git a/ui/app/components/identity/entity-nav.hbs b/ui/app/components/identity/entity-nav.hbs index ed17d823ed..d6ce787145 100644 --- a/ui/app/components/identity/entity-nav.hbs +++ b/ui/app/components/identity/entity-nav.hbs @@ -3,29 +3,23 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- {{titleize (pluralize this.identityType)}} -

-
-
-
- -
+ + + + {{#if this.model.meta.total}} diff --git a/ui/app/components/keymgmt/key-edit.hbs b/ui/app/components/keymgmt/key-edit.hbs index 94749fc796..c98a4d253c 100644 --- a/ui/app/components/keymgmt/key-edit.hbs +++ b/ui/app/components/keymgmt/key-edit.hbs @@ -3,24 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if this.isDistributing}} - Distribute Key - {{else if (eq @mode "create")}} - Create Key - {{else if (eq @mode "edit")}} - Edit Key - {{else}} - {{@model.id}} - {{/if}} -

-
-
+ +
{{#if this.isDistributing}} diff --git a/ui/app/components/keymgmt/key-edit.js b/ui/app/components/keymgmt/key-edit.js index 27dbfde1a3..383e9096ca 100644 --- a/ui/app/components/keymgmt/key-edit.js +++ b/ui/app/components/keymgmt/key-edit.js @@ -31,6 +31,17 @@ export default class KeymgmtKeyEdit extends Component { @service flashMessages; @tracked isDeleteModalOpen = false; + get title() { + if (this.isDistributing) { + return 'Distribute Key'; + } else if (this.args.mode === 'create') { + return 'Create Key'; + } else if (this.args.mode === 'edit') { + return 'Edit Key'; + } + return this.args.model.id; + } + get mode() { return this.args.mode || 'show'; } diff --git a/ui/app/components/keymgmt/provider-edit.hbs b/ui/app/components/keymgmt/provider-edit.hbs index 3e9dd6c1ce..8ba9b1b3bd 100644 --- a/ui/app/components/keymgmt/provider-edit.hbs +++ b/ui/app/components/keymgmt/provider-edit.hbs @@ -3,23 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if this.isDistributing}} - Distribute Key to Provider - {{else if this.isShowing}} - Provider - {{@model.id}} - {{else}} - {{if this.isCreating "Create Provider" "Update Credentials"}} - {{/if}} -

-
-
+ + {{#if this.isDistributing}} diff --git a/ui/app/components/keymgmt/provider-edit.js b/ui/app/components/keymgmt/provider-edit.js index bb046f8cce..cf53eed6af 100644 --- a/ui/app/components/keymgmt/provider-edit.js +++ b/ui/app/components/keymgmt/provider-edit.js @@ -38,6 +38,22 @@ export default class KeymgmtProviderEdit extends Component { @tracked modelValidations; + get title() { + if (this.isDistributing) { + return 'Distribute Key to Provider'; + } else if (this.isShowing) { + return 'Provider'; + } else if (this.isCreating) { + return 'Create Provider'; + } + + return 'Update Credentials'; + } + + get subtitle() { + return this.isShowing ? this.args.model.id : ''; + } + get isShowing() { return this.args.mode === 'show'; } diff --git a/ui/app/components/license-info.hbs b/ui/app/components/license-info.hbs index 9a59e97a5b..7799eccb3e 100644 --- a/ui/app/components/license-info.hbs +++ b/ui/app/components/license-info.hbs @@ -3,11 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

License

-
-
+
Details diff --git a/ui/app/components/mfa/mfa-login-enforcement-header.hbs b/ui/app/components/mfa/mfa-login-enforcement-header.hbs index 420960fafb..2d45899244 100644 --- a/ui/app/components/mfa/mfa-login-enforcement-header.hbs +++ b/ui/app/components/mfa/mfa-login-enforcement-header.hbs @@ -3,26 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} -{{#if @isInline}} -

Enforcement

-{{else}} - - - - - - - - -

- - {{@heading}} -

-
-
-{{/if}} -
-

+ + <:breadcrumbs> + + + <:description> {{#if @isInline}} An enforcement includes the authentication types, authentication methods, groups, and entities that will require this MFA method. This is optional and can be added later. @@ -32,7 +17,10 @@ all entities and groups which make use of that authentication method will be subject to an MFA request. Learn more here. {{/if}} -

+ + + +
{{#if @isInline}}
- -

- {{#if this.showEnable}} - {{#let (find-by "type" this.mountForm.type @mountTypes) as |typeInfo|}} - {{#if typeInfo.glyph}} - - {{/if}} - {{#if (eq @mountCategory "secret")}} - {{concat "Enable " (or typeInfo.displayName this.mountForm.type) " Secrets Engine"}} - {{else}} - {{concat "Enable " (or typeInfo.displayName this.mountForm.type) " Authentication Method"}} - {{/if}} - {{/let}} - {{else if (eq @mountCategory "secret")}} - Enable a Secrets Engine - {{else}} - Enable an Authentication Method - {{/if}} -

-
- +{{#let (find-by "type" this.mountForm.type @mountTypes) as |typeInfo|}} + +{{/let}}
diff --git a/ui/app/components/mount-backend-form.ts b/ui/app/components/mount-backend-form.ts index cba81cc9b6..c75e918baf 100644 --- a/ui/app/components/mount-backend-form.ts +++ b/ui/app/components/mount-backend-form.ts @@ -32,8 +32,14 @@ import type { ValidationMap } from 'vault/vault/app-types'; * */ +interface TypeInfo { + type: string; + displayName: string; + glyph: string; +} interface Args { mountModel: AuthMethodForm; + mountTypes: TypeInfo[] | undefined; onMountSuccess: (type: string, path: string, useEngineRoute: boolean) => void; } @@ -59,6 +65,16 @@ export default class MountBackendForm extends Component { return !!this.mountForm.type; } + get title(): string { + const typeInfo = (this.args?.mountTypes || []).find( + (mountType: TypeInfo) => this.mountForm.type === mountType.type + ); + if (this.showEnable) { + return `Enable ${typeInfo?.displayName || this.mountForm.type} Authentication Method`; + } + return 'Enable an Authentication Method'; + } + constructor(owner: unknown, args: Args) { super(owner, args); } diff --git a/ui/app/components/mount/secrets-engine-form.hbs b/ui/app/components/mount/secrets-engine-form.hbs index 1c3bb0c18a..8ac9c632e3 100644 --- a/ui/app/components/mount/secrets-engine-form.hbs +++ b/ui/app/components/mount/secrets-engine-form.hbs @@ -4,16 +4,11 @@ }}
- - - - - - - - - - + + <:breadcrumbs> + + + diff --git a/ui/app/components/mount/secrets-engine-form.ts b/ui/app/components/mount/secrets-engine-form.ts index 06d592b3b4..8dd87066ab 100644 --- a/ui/app/components/mount/secrets-engine-form.ts +++ b/ui/app/components/mount/secrets-engine-form.ts @@ -9,6 +9,8 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import { task } from 'ember-concurrency'; import { set } from '@ember/object'; +import { capitalize } from '@ember/string'; + import type FlashMessagesService from 'ember-cli-flash/services/flash-messages'; import type ApiService from 'vault/services/api'; import type CapabilitiesService from 'vault/services/capabilities'; @@ -41,6 +43,19 @@ export default class MountSecretsEngineFormComponent extends Component { @tracked invalidFormAlert: string | null = null; @tracked errorMessage: string | string[] = ''; + get breadcrumbs() { + const breadcrumbs: { label: string; route?: string }[] = [ + { label: 'Secrets engines', route: 'vault.cluster.secrets.backends' }, + { label: 'Enable secrets engine', route: 'vault.cluster.secrets.enable' }, + ]; + + if (this.args?.model.type) { + breadcrumbs.push({ label: capitalize(this.args?.model?.type) }); + } + + return breadcrumbs; + } + get mountForm(): SecretsEngineForm { return this.args.model; } diff --git a/ui/app/components/not-found.hbs b/ui/app/components/not-found.hbs index c24a50a733..ba6a411b2b 100644 --- a/ui/app/components/not-found.hbs +++ b/ui/app/components/not-found.hbs @@ -4,13 +4,8 @@ }}
- - -

- 404 Not Found -

-
-
+ +

Sorry, we were unable to find any content at {{or @model.path this.path}}.

diff --git a/ui/app/components/oidc/client-form.hbs b/ui/app/components/oidc/client-form.hbs index 73480db2f3..25db12ed84 100644 --- a/ui/app/components/oidc/client-form.hbs +++ b/ui/app/components/oidc/client-form.hbs @@ -3,28 +3,12 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - {{#if @model.isNew}} - - {{else}} - - {{/if}} - - - - -

- {{if @model.isNew "Create" "Edit"}} - Application -

- - + + <:breadcrumbs> + + + +
diff --git a/ui/app/components/oidc/client-form.js b/ui/app/components/oidc/client-form.js index 4622c4bbe0..2dc6848a98 100644 --- a/ui/app/components/oidc/client-form.js +++ b/ui/app/components/oidc/client-form.js @@ -32,6 +32,17 @@ export default class OidcClientForm extends Component { ? 'allow_all' : 'limited'; + get breadcrumbs() { + const firstBreadcrumb = this.args.model.isNew + ? { label: 'Applications', route: 'vault.cluster.access.oidc.clients' } + : { + label: 'Details', + route: 'vault.cluster.access.oidc.clients.client.details', + model: this.args.model.name, + }; + return [firstBreadcrumb, { label: this.args.model.isNew ? 'Create Application' : 'Edit Application' }]; + } + get modelAssignments() { const { assignments } = this.args.model; if (assignments.includes('allow_all') && assignments.length === 1) { diff --git a/ui/app/components/oidc/provider-form.hbs b/ui/app/components/oidc/provider-form.hbs index 4c1c5cdf5f..7e39b8263b 100644 --- a/ui/app/components/oidc/provider-form.hbs +++ b/ui/app/components/oidc/provider-form.hbs @@ -3,28 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - {{#if @model.isNew}} - - {{else}} - - {{/if}} - - - - -

- {{if @model.isNew "Create" "Edit"}} - Provider -

-
-
+ + <:breadcrumbs> + + +
diff --git a/ui/app/components/oidc/provider-form.js b/ui/app/components/oidc/provider-form.js index 29e6d3a27e..90964c7800 100644 --- a/ui/app/components/oidc/provider-form.js +++ b/ui/app/components/oidc/provider-form.js @@ -36,6 +36,17 @@ export default class OidcProviderForm extends Component { ? 'allow_all' : 'limited'; + get breadcrumbs() { + const firstBreadcrumb = this.args.model.isNew + ? { label: 'Providers', route: 'vault.cluster.access.oidc.providers' } + : { + label: 'Details', + route: 'vault.cluster.access.oidc.providers.provider.details', + model: this.args.model.name, + }; + return [firstBreadcrumb, { label: this.args.model.isNew ? 'Create Provider' : 'Edit Provider' }]; + } + // function passed to search select renderTooltip(selection, dropdownOptions) { // if a client has been deleted it will not exist in dropdownOptions (response from search select's query) diff --git a/ui/app/components/oidc/scope-form.hbs b/ui/app/components/oidc/scope-form.hbs index 7be4c22990..5c57a2c9ef 100644 --- a/ui/app/components/oidc/scope-form.hbs +++ b/ui/app/components/oidc/scope-form.hbs @@ -3,28 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - {{#if @model.isNew}} - - {{else}} - - {{/if}} - - - - -

- {{if @model.isNew "Create" "Edit"}} - Scope -

-
- + + <:breadcrumbs> + + + <:actions> - -
+ +
diff --git a/ui/app/components/oidc/scope-form.js b/ui/app/components/oidc/scope-form.js index 6f472e7f31..2a97c50f98 100644 --- a/ui/app/components/oidc/scope-form.js +++ b/ui/app/components/oidc/scope-form.js @@ -39,6 +39,17 @@ export default class OidcScopeFormComponent extends Component { "groups": {{identity.entity.groups.names}} }`; + get breadcrumbs() { + const firstBreadcrumb = this.args.model.isNew + ? { label: 'Scopes', route: 'vault.cluster.access.oidc.scopes' } + : { + label: 'Details', + route: 'vault.cluster.access.oidc.scopes.scope.details', + model: this.args.model.name, + }; + return [firstBreadcrumb, { label: this.args.model.isNew ? 'Create Scope' : 'Edit Scope' }]; + } + @task *save(event) { event.preventDefault(); diff --git a/ui/app/components/page/userpass-reset-password.hbs b/ui/app/components/page/userpass-reset-password.hbs index 72d7ebfd1a..5825519bff 100644 --- a/ui/app/components/page/userpass-reset-password.hbs +++ b/ui/app/components/page/userpass-reset-password.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Reset password -

-
-
+
diff --git a/ui/app/components/raft-storage-restore.hbs b/ui/app/components/raft-storage-restore.hbs index a1343ebdf1..45fbd6e51e 100644 --- a/ui/app/components/raft-storage-restore.hbs +++ b/ui/app/components/raft-storage-restore.hbs @@ -3,19 +3,13 @@ SPDX-License-Identifier: BUSL-1.1 }} - - - - - - - - -

- Restore Snapshot -

-
-
+ + <:breadcrumbs> + + + diff --git a/ui/app/components/role-aws-edit.hbs b/ui/app/components/role-aws-edit.hbs index 4b5302c9b7..d7adce185b 100644 --- a/ui/app/components/role-aws-edit.hbs +++ b/ui/app/components/role-aws-edit.hbs @@ -3,8 +3,8 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq this.mode "create")}} - Create an AWS Role - {{else if (eq this.mode "edit")}} - Edit AWS Role - {{this.model.id}} - {{else}} - AWS Role - {{this.model.id}} - {{/if}} -

-
-
+ + {{#if (eq this.mode "show")}} diff --git a/ui/app/components/role-aws-edit.js b/ui/app/components/role-aws-edit.js index eab1bf040f..f7bb51db2c 100644 --- a/ui/app/components/role-aws-edit.js +++ b/ui/app/components/role-aws-edit.js @@ -6,9 +6,26 @@ import { isBlank } from '@ember/utils'; import { set } from '@ember/object'; import RoleEdit from './role-edit'; +import { computed } from '@ember/object'; const SHOW_ROUTE = 'vault.cluster.secrets.backend.show'; export default RoleEdit.extend({ + title: computed('mode', function () { + if (this.mode === 'create') { + return 'Create an AWS Role'; + } else if (this.mode === 'edit') { + return 'Edit AWS Role'; + } else { + return 'AWS Role'; + } + }), + + subtitle: computed('mode', 'model.id', function () { + if (this.mode === 'create') return; + + return this.model.id; + }), + actions: { createOrUpdate(type, event) { event.preventDefault(); diff --git a/ui/app/components/role-ssh-edit.hbs b/ui/app/components/role-ssh-edit.hbs index 17f87b270c..4aea89fb62 100644 --- a/ui/app/components/role-ssh-edit.hbs +++ b/ui/app/components/role-ssh-edit.hbs @@ -3,8 +3,8 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq this.mode "create")}} - Create an SSH Role - {{else if (eq this.mode "edit")}} - Edit SSH Role - {{else}} - SSH Role - {{this.model.id}} - {{/if}} -

-
-
+ + {{#if (eq this.mode "show")}} diff --git a/ui/app/components/role-ssh-edit.js b/ui/app/components/role-ssh-edit.js index 233019213e..091141cfd9 100644 --- a/ui/app/components/role-ssh-edit.js +++ b/ui/app/components/role-ssh-edit.js @@ -4,6 +4,7 @@ */ import RoleEdit from './role-edit'; +import { computed } from '@ember/object'; export default RoleEdit.extend({ init() { @@ -11,6 +12,22 @@ export default RoleEdit.extend({ this.set('backendType', 'ssh'); }, + title: computed('mode', function () { + if (this.mode === 'create') { + return 'Create SSH Role'; + } else if (this.mode === 'edit') { + return 'Edit SSH Role'; + } else { + return 'SSH Role'; + } + }), + + subtitle: computed('mode', 'model.id', function () { + if (this.mode === 'create' || this.mode === 'edit') return; + + return this.model.id; + }), + actions: { updateTtl(path, val) { const model = this.model; diff --git a/ui/app/components/secret-engine/page/general-settings.hbs b/ui/app/components/secret-engine/page/general-settings.hbs index e6516460f2..4f572f9abc 100644 --- a/ui/app/components/secret-engine/page/general-settings.hbs +++ b/ui/app/components/secret-engine/page/general-settings.hbs @@ -11,16 +11,6 @@ <:breadcrumbs> - <:tabs> - - <:actions> + {{/let}} - {{#if this.saveGeneralSettings.isRunning}} {{! TODO: Fix loading state styles }} diff --git a/ui/app/components/secret-engine/page/plugin-settings.hbs b/ui/app/components/secret-engine/page/plugin-settings.hbs index dc92c7dace..8a18b34586 100644 --- a/ui/app/components/secret-engine/page/plugin-settings.hbs +++ b/ui/app/components/secret-engine/page/plugin-settings.hbs @@ -12,13 +12,6 @@ <:breadcrumbs> - <:tabs> - - <:actions> + {{#if @model.config}} diff --git a/ui/app/components/tools/hash.hbs b/ui/app/components/tools/hash.hbs index 47007389b9..f55706de20 100644 --- a/ui/app/components/tools/hash.hbs +++ b/ui/app/components/tools/hash.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Hash Data -

-
-
+ {{#if this.sum}}
diff --git a/ui/app/components/tools/lookup.hbs b/ui/app/components/tools/lookup.hbs index 98e229d9de..e22af36856 100644 --- a/ui/app/components/tools/lookup.hbs +++ b/ui/app/components/tools/lookup.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Lookup Token -

-
-
+ {{#if this.lookupData}}
diff --git a/ui/app/components/tools/random.hbs b/ui/app/components/tools/random.hbs index df24057c46..7374ac382f 100644 --- a/ui/app/components/tools/random.hbs +++ b/ui/app/components/tools/random.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Random Bytes -

-
-
+ {{#if this.randomBytes}}
diff --git a/ui/app/components/tools/rewrap.hbs b/ui/app/components/tools/rewrap.hbs index ab28289a26..d3b6837920 100644 --- a/ui/app/components/tools/rewrap.hbs +++ b/ui/app/components/tools/rewrap.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Rewrap Token -

-
-
+ {{#if this.rewrappedToken}}
diff --git a/ui/app/components/tools/unwrap.hbs b/ui/app/components/tools/unwrap.hbs index d99433e69c..3c8282d71c 100644 --- a/ui/app/components/tools/unwrap.hbs +++ b/ui/app/components/tools/unwrap.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Unwrap Data -

-
-
+ {{#if this.unwrapData}} diff --git a/ui/app/components/tools/wrap.hbs b/ui/app/components/tools/wrap.hbs index f215c8cb9a..660c8ae663 100644 --- a/ui/app/components/tools/wrap.hbs +++ b/ui/app/components/tools/wrap.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 }} - - -

- Wrap Data -

-
-
+ {{#if this.token}}
diff --git a/ui/app/components/totp-edit.hbs b/ui/app/components/totp-edit.hbs index 3dd1288c09..066cca5166 100644 --- a/ui/app/components/totp-edit.hbs +++ b/ui/app/components/totp-edit.hbs @@ -3,8 +3,8 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq @mode "create")}} - Create a TOTP key - {{else}} - TOTP key - {{@model.id}} - {{/if}} -

-
-
+ + {{#if (eq @mode "show")}} diff --git a/ui/app/components/totp-edit.js b/ui/app/components/totp-edit.js index bd0621cb21..e72b8df9cd 100644 --- a/ui/app/components/totp-edit.js +++ b/ui/app/components/totp-edit.js @@ -34,6 +34,19 @@ export default class TotpEdit extends Component { successCallback; + get title() { + if (this.args.mode === 'create') { + return 'Create a TOTP key'; + } + return 'TOTP key'; + } + + get subtitle() { + if (this.args.mode === 'create') return ''; + + return this.args.model.id; + } + get defaultKeyFormFields() { const shared = ['name', 'generate', 'issuer', 'accountName']; const generated = [...shared, 'exported']; diff --git a/ui/app/components/transform-role-edit.hbs b/ui/app/components/transform-role-edit.hbs index ea7c6bfe87..5011674cad 100644 --- a/ui/app/components/transform-role-edit.hbs +++ b/ui/app/components/transform-role-edit.hbs @@ -3,8 +3,8 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq this.mode "create")}} - Create Role - {{else if (eq this.mode "edit")}} - Edit Role - {{else}} - Role - {{this.model.id}} - {{/if}} -

-
-
+ + {{#if (eq this.mode "show")}} diff --git a/ui/app/components/transform-role-edit.js b/ui/app/components/transform-role-edit.js index e839619d3e..e48633ab96 100644 --- a/ui/app/components/transform-role-edit.js +++ b/ui/app/components/transform-role-edit.js @@ -5,6 +5,7 @@ import TransformBase, { addToList, removeFromList } from './transform-edit-base'; import { service } from '@ember/service'; +import { computed } from '@ember/object'; export default TransformBase.extend({ flashMessages: service(), @@ -16,6 +17,22 @@ export default TransformBase.extend({ this.set('initialTransformations', this.model.transformations); }, + title: computed('mode', function () { + if (this.mode === 'create') { + return 'Create Role'; + } else if (this.mode === 'edit') { + return 'Edit Role'; + } else { + return 'Role'; + } + }), + + subtitle: computed('mode', 'model.id', function () { + if (this.mode === 'create' || this.mode === 'edit') return; + + return this.model.id; + }), + handleUpdateTransformations(updateTransformations, roleId, type = 'update') { if (!updateTransformations) return; const backend = this.model.backend; diff --git a/ui/app/components/transform-template-edit.hbs b/ui/app/components/transform-template-edit.hbs index 7b6cae1517..0d7914e964 100644 --- a/ui/app/components/transform-template-edit.hbs +++ b/ui/app/components/transform-template-edit.hbs @@ -3,23 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq @mode "create")}} - Create Template - {{else if (eq @mode "edit")}} - Edit Template - {{else}} - Template - {{@model.id}} - {{/if}} -

-
-
+ + {{#if (eq @mode "show")}} diff --git a/ui/app/components/transform-template-edit.js b/ui/app/components/transform-template-edit.js index 0ab9c85acb..041450650e 100644 --- a/ui/app/components/transform-template-edit.js +++ b/ui/app/components/transform-template-edit.js @@ -41,6 +41,22 @@ export default class TransformTemplateEditComponent extends Component { ]; } + get title() { + if (this.args.mode === 'create') { + return 'Create Template'; + } else if (this.args.mode === 'edit') { + return 'Edit Template'; + } else { + return 'Template'; + } + } + + get subtitle() { + if (this.args.mode === 'create' || this.args.mode === 'edit') return ''; + + return this.args?.model?.id; + } + transition(route = 'show') { this.errorMessage = ''; const { backend, id } = this.args.model; diff --git a/ui/app/components/transformation-edit.hbs b/ui/app/components/transformation-edit.hbs index 7951437d2c..a0ddefd6c5 100644 --- a/ui/app/components/transformation-edit.hbs +++ b/ui/app/components/transformation-edit.hbs @@ -3,8 +3,8 @@ Copyright IBM Corp. 2016, 2025 SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq this.mode "create")}} - Create Transformation - {{else if (eq this.mode "edit")}} - Edit Transformation - {{else}} - Transformation - {{this.model.id}} - {{/if}} -

-
-
+ + {{#if (eq this.mode "show")}} diff --git a/ui/app/components/transformation-edit.js b/ui/app/components/transformation-edit.js index 0ca9598045..1b792574ad 100644 --- a/ui/app/components/transformation-edit.js +++ b/ui/app/components/transformation-edit.js @@ -5,6 +5,7 @@ import TransformBase, { addToList, removeFromList } from './transform-edit-base'; import { service } from '@ember/service'; +import { computed } from '@ember/object'; export default TransformBase.extend({ flashMessages: service(), @@ -17,6 +18,22 @@ export default TransformBase.extend({ this.set('initialRoles', this.model.allowed_roles); }, + title: computed('mode', function () { + if (this.mode === 'create') { + return 'Create Transformation'; + } else if (this.mode === 'edit') { + return 'Edit Transformation'; + } else { + return 'Transformation'; + } + }), + + subtitle: computed('mode', 'model.id', function () { + if (this.mode === 'create' || this.mode === 'edit') return; + + return this.model.id; + }), + async updateOrCreateRole(role, transformationId, backend) { const roleRecord = await this.store .queryRecord('transform/role', { diff --git a/ui/app/components/transit-edit.hbs b/ui/app/components/transit-edit.hbs index b2895282e8..0aa4491f57 100644 --- a/ui/app/components/transit-edit.hbs +++ b/ui/app/components/transit-edit.hbs @@ -3,23 +3,12 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:breadcrumbs> - - -

- {{#if (eq this.mode "create")}} - Create Key - {{else if (eq this.mode "edit")}} - Edit Key - {{else}} - Key - {{this.key.id}} - {{/if}} -

-
-
+ + + {{#if (eq this.mode "create")}} - <:tabs> - - + {{/let}} diff --git a/ui/lib/core/addon/components/page/header.hbs b/ui/lib/core/addon/components/page/header.hbs index 777c014490..163e0bc7be 100644 --- a/ui/lib/core/addon/components/page/header.hbs +++ b/ui/lib/core/addon/components/page/header.hbs @@ -21,16 +21,12 @@ {{#if @subtitle}} {{@subtitle}} {{/if}} - {{#if @description}} + {{#if (has-block "description")}} + {{yield to="description"}} + {{else if @description}} {{@description}} {{/if}} {{#if (has-block "actions")}} {{yield to="actions"}} {{/if}} - - -{{#if (has-block "tabs")}} -
- {{yield to="tabs"}} -
-{{/if}} \ No newline at end of file + \ No newline at end of file diff --git a/ui/lib/ldap/addon/components/ldap-header.hbs b/ui/lib/ldap/addon/components/ldap-header.hbs index ce23be56aa..422c21b0ba 100644 --- a/ui/lib/ldap/addon/components/ldap-header.hbs +++ b/ui/lib/ldap/addon/components/ldap-header.hbs @@ -32,26 +32,25 @@ {{/if}} - <:tabs> - {{#if @configRoute}} - - {{else}} - - {{/if}} - +{{#if @configRoute}} + +{{else}} + +{{/if}} + {{#if this.engineToDisable}} - <:tabs> - - + + {{#if this.showConfirmModal}} - <:tabs> - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/error.hbs b/ui/lib/pki/addon/templates/error.hbs index 27e81c8771..41107bdba7 100644 --- a/ui/lib/pki/addon/templates/error.hbs +++ b/ui/lib/pki/addon/templates/error.hbs @@ -7,17 +7,16 @@ <:breadcrumbs> - <:tabs> - - + + \ No newline at end of file diff --git a/ui/tests/acceptance/auth/auth-list-test.js b/ui/tests/acceptance/auth/auth-list-test.js index 4bebbe016f..5ea6fd0bfd 100644 --- a/ui/tests/acceptance/auth/auth-list-test.js +++ b/ui/tests/acceptance/auth/auth-list-test.js @@ -49,7 +49,7 @@ module('Acceptance | auth backend list', function (hooks) { await createUser(this.path1, this.user1); // navigate back to the methods list - await click(SELECTORS.methods); + await click(GENERAL.breadcrumbAtIdx(0)); assert.strictEqual(currentURL(), '/vault/access'); // enable a second user in the second userpass backend @@ -59,7 +59,7 @@ module('Acceptance | auth backend list', function (hooks) { assert.dom(SELECTORS.listItem).hasText(this.user2, 'user2 exists in the list'); // check that switching back to the first auth method shows the first user - await click(SELECTORS.methods); + await click(GENERAL.breadcrumbAtIdx(0)); await click(GENERAL.linkedBlock(this.path1)); assert.dom(SELECTORS.listItem).hasText(this.user1, 'user1 exists in the list'); @@ -98,6 +98,7 @@ module('Acceptance | auth backend list', function (hooks) { const itemSelector = `${GENERAL.linkedBlock(path)} .hds-dropdown-list-item`; await click(triggerSelector); + assert .dom(itemSelector) .exists({ count: itemCount }, `shows ${itemCount} dropdown items for ${type}`); diff --git a/ui/tests/acceptance/dashboard-test.js b/ui/tests/acceptance/dashboard-test.js index dc50cc39b5..77afdc6c16 100644 --- a/ui/tests/acceptance/dashboard-test.js +++ b/ui/tests/acceptance/dashboard-test.js @@ -8,6 +8,7 @@ import { visit, currentURL } from '@ember/test-helpers'; import { setupApplicationTest } from 'vault/tests/helpers'; import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { DASHBOARD } from 'vault/tests/helpers/components/dashboard/dashboard-selectors'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; import Sinon from 'sinon'; module('Acceptance | landing page dashboard', function (hooks) { @@ -25,12 +26,12 @@ module('Acceptance | landing page dashboard', function (hooks) { test('display the version number for the title', async function (assert) { await login(); - // Since we're using mirage, version is mocked static value - const versionText = this.version.isEnterprise - ? `Vault ${this.version.versionDisplay} root` - : `Vault ${this.version.versionDisplay}`; + assert.dom(GENERAL.hdsPageHeaderTitle).hasText(`Vault ${this.version.versionDisplay}`); + }); - assert.dom(DASHBOARD.cardHeader('Vault version')).hasText(versionText); + test('display the namespace badge for enterprise', async function (assert) { + await login(); + assert.dom('.hds-badge').hasText('root', 'shows root namespace for enterprise'); }); test('hides the configuration details card on a non-root namespace enterprise version', async function (assert) { diff --git a/ui/tests/acceptance/mfa-login-enforcement-test.js b/ui/tests/acceptance/mfa-login-enforcement-test.js index 9966837f25..eb0904c9b4 100644 --- a/ui/tests/acceptance/mfa-login-enforcement-test.js +++ b/ui/tests/acceptance/mfa-login-enforcement-test.js @@ -52,7 +52,7 @@ module('Acceptance | mfa-login-enforcement', function (hooks) { await click(GENERAL.tab('enforcements')); await click('[data-test-enforcement-create]'); - assert.dom('[data-test-mleh-title]').hasText('New enforcement', 'Title renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('New enforcement', 'Title renders'); await click('[data-test-mlef-save]'); assert .dom('[data-test-inline-error-message]') diff --git a/ui/tests/acceptance/oidc-auth-method-test.js b/ui/tests/acceptance/oidc-auth-method-test.js index ed2fa0f718..7c719c06a2 100644 --- a/ui/tests/acceptance/oidc-auth-method-test.js +++ b/ui/tests/acceptance/oidc-auth-method-test.js @@ -52,10 +52,8 @@ module('Acceptance | oidc auth method', function (hooks) { triggerMessageEvent('oidc'); await click(GENERAL.submitButton); - await waitFor('[data-test-dashboard-card-header="Vault version"]'); - assert - .dom('[data-test-dashboard-card-header="Vault version"]') - .exists('Render the dashboard landing page.'); + await waitFor(GENERAL.hdsPageHeaderTitle); + assert.dom(GENERAL.hdsPageHeaderTitle).exists('Render the dashboard landing page.'); await logout(); assert.dom(AUTH_FORM.selectMethod).hasValue('oidc', 'Previous auth method selected on logout'); diff --git a/ui/tests/acceptance/reduced-disclosure-test.js b/ui/tests/acceptance/reduced-disclosure-test.js index 8188286ba9..c8b6f59f19 100644 --- a/ui/tests/acceptance/reduced-disclosure-test.js +++ b/ui/tests/acceptance/reduced-disclosure-test.js @@ -18,7 +18,6 @@ import { GENERAL } from 'vault/tests/helpers/general-selectors'; const { unsealKeys } = VAULT_KEYS; const SELECTORS = { footerVersion: `[data-test-footer-version]`, - dashboardTitle: `[data-test-dashboard-card-header="Vault version"]`, }; module('Acceptance | reduced disclosure test', function (hooks) { @@ -39,7 +38,7 @@ module('Acceptance | reduced disclosure test', function (hooks) { assert.strictEqual(currentURL(), '/vault/dashboard'); // Ensure it shows version on dashboard - assert.dom(SELECTORS.dashboardTitle).includesText(`Vault v1.`); + assert.dom(GENERAL.hdsPageHeaderTitle).includesText(`Vault v1.`); assert .dom(SELECTORS.footerVersion) .hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version after login'); @@ -160,7 +159,7 @@ module('Acceptance | reduced disclosure test', function (hooks) { `Vault ${this.versionSvc.version}`, 'shows Vault version for default policy in child namespace' ); - assert.dom(SELECTORS.dashboardTitle).includesText('Vault v1.'); + assert.dom(GENERAL.hdsPageHeaderTitle).includesText('Vault v1.'); // log in to "root" before deleting await login(); @@ -174,7 +173,7 @@ module('Acceptance | reduced disclosure test', function (hooks) { assert.strictEqual(currentURL(), '/vault/dashboard'); // Ensure it shows version on dashboard - assert.dom(SELECTORS.dashboardTitle).includesText(`Vault v1.`); + assert.dom(GENERAL.hdsPageHeaderTitle).includesText(`Vault v1.`); assert .dom(SELECTORS.footerVersion) .hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version after login'); diff --git a/ui/tests/acceptance/reset-password-test.js b/ui/tests/acceptance/reset-password-test.js index 1c744b177d..998d73ae0f 100644 --- a/ui/tests/acceptance/reset-password-test.js +++ b/ui/tests/acceptance/reset-password-test.js @@ -63,7 +63,7 @@ module('Acceptance | reset password', function (hooks) { 'shows correct banner text' ); - assert.dom('[data-test-title]').hasText('Reset password', 'page title'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Reset password', 'page title'); await fillIn('[data-test-input="reset-password"]', 'newpassword'); await click(GENERAL.submitButton); await waitFor('[data-test-flash-message]'); diff --git a/ui/tests/acceptance/secrets/backend/aws/aws-test.js b/ui/tests/acceptance/secrets/backend/aws/aws-test.js index 776856d81e..4a205c1761 100644 --- a/ui/tests/acceptance/secrets/backend/aws/aws-test.js +++ b/ui/tests/acceptance/secrets/backend/aws/aws-test.js @@ -82,7 +82,7 @@ module('Acceptance | aws secret backend', function (hooks) { ); await click(SES.createSecretLink); - assert.dom(SES.secretHeader).hasText('Create an AWS Role', 'It renders the create role page'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create an AWS Role', 'It renders the create role page'); await fillIn(GENERAL.inputByAttr('name'), roleName); await click(GENERAL.submitButton); diff --git a/ui/tests/acceptance/secrets/backend/ssh/roles-test.js b/ui/tests/acceptance/secrets/backend/ssh/roles-test.js index d0e3b47b4c..2de39cf5b6 100644 --- a/ui/tests/acceptance/secrets/backend/ssh/roles-test.js +++ b/ui/tests/acceptance/secrets/backend/ssh/roles-test.js @@ -121,7 +121,9 @@ module('Acceptance | ssh | roles', function (hooks) { for (const role of ROLES) { // create a role await click(SES.createSecretLink); - assert.dom(SES.secretHeader).includesText('SSH Role', `${role.type}: renders the create page`); + assert + .dom(GENERAL.hdsPageHeaderTitle) + .includesText('SSH Role', `${role.type}: renders the create page`); await fillIn(GENERAL.inputByAttr('name'), role.name); await fillIn(GENERAL.inputByAttr('keyType'), role.type); diff --git a/ui/tests/acceptance/secrets/backend/totp/key-test.js b/ui/tests/acceptance/secrets/backend/totp/key-test.js index ede917e9ad..a2b4e8e82a 100644 --- a/ui/tests/acceptance/secrets/backend/totp/key-test.js +++ b/ui/tests/acceptance/secrets/backend/totp/key-test.js @@ -74,7 +74,8 @@ module('Acceptance | totp key backend', function (hooks) { await click(GENERAL.menuTrigger); await click(`${GENERAL.menuItem('details')}`); - assert.dom('.title').hasText(`TOTP key ${this.keyName}`); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('TOTP key'); + assert.dom(GENERAL.hdsPageHeaderSubtitle).hasText(this.keyName); assert.dom('[data-test-totp-key-details]').exists(); assert.strictEqual( @@ -101,7 +102,7 @@ module('Acceptance | totp key backend', function (hooks) { test('it creates a key with Vault as the provider', async function (assert) { await click(SES.createSecretLink); - assert.dom(SES.secretHeader).hasText('Create a TOTP key', 'It renders the create key page'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create a TOTP key', 'It renders the create key page'); await createVaultKey(this.keyName, this.issuer, this.accountName); assert.dom('[data-test-qrcode]').exists('QR code exists'); @@ -121,7 +122,7 @@ module('Acceptance | totp key backend', function (hooks) { test('it creates a key with another service as the provider with URL', async function (assert) { await click(SES.createSecretLink); - assert.dom(SES.secretHeader).hasText('Create a TOTP key', 'It renders the create key page'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create a TOTP key', 'It renders the create key page'); await createNonVaultKey(this.keyName, this.issuer, this.accountName, this.url); await waitUntil(() => currentURL() === `/vault/secrets-engines/${this.path}/show/${this.keyName}`); assert.strictEqual( @@ -136,7 +137,7 @@ module('Acceptance | totp key backend', function (hooks) { test('it creates a key with another service as the provider with key', async function (assert) { await click(SES.createSecretLink); - assert.dom(SES.secretHeader).hasText('Create a TOTP key', 'It renders the create key page'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create a TOTP key', 'It renders the create key page'); await createNonVaultKey(this.keyName, this.issuer, this.accountName, undefined, this.key); await waitUntil(() => currentURL() === `/vault/secrets-engines/${this.path}/show/${this.keyName}`); assert.strictEqual( diff --git a/ui/tests/integration/components/dashboard/overview-test.js b/ui/tests/integration/components/dashboard/overview-test.js index 94ce005fa1..bb36d52cb0 100644 --- a/ui/tests/integration/components/dashboard/overview-test.js +++ b/ui/tests/integration/components/dashboard/overview-test.js @@ -10,6 +10,7 @@ import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { DASHBOARD } from 'vault/tests/helpers/components/dashboard/dashboard-selectors'; import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; module('Integration | Component | dashboard/overview', function (hooks) { setupRenderingTest(hooks); @@ -81,7 +82,7 @@ module('Integration | Component | dashboard/overview', function (hooks) { this.replication = null; this.vaultConfiguration = null; await this.renderComponent(); - assert.dom(DASHBOARD.cardHeader('Vault version')).exists(); + assert.dom(GENERAL.hdsPageHeaderTitle).exists(); assert.dom(DASHBOARD.cardName('secrets-engines')).exists(); assert.dom(DASHBOARD.emptyState('secrets-engines')).exists(); assert.dom(DASHBOARD.cardName('learn-more')).exists(); @@ -115,7 +116,7 @@ module('Integration | Component | dashboard/overview', function (hooks) { ); await this.renderComponent(); - assert.dom(DASHBOARD.cardHeader('Vault version')).exists(); + assert.dom(GENERAL.hdsPageHeaderTitle).exists(); assert.dom(DASHBOARD.cardName('secrets-engines')).exists(); assert.dom(DASHBOARD.cardName('learn-more')).exists(); assert.dom(DASHBOARD.cardName('quick-actions')).exists(); @@ -149,7 +150,7 @@ module('Integration | Component | dashboard/overview', function (hooks) { }, }; await this.renderComponent(); - assert.dom(DASHBOARD.cardHeader('Vault version')).exists(); + assert.dom(GENERAL.hdsPageHeaderTitle).exists(); assert.dom(DASHBOARD.cardName('secrets-engines')).exists(); assert.dom(DASHBOARD.cardName('learn-more')).exists(); assert.dom(DASHBOARD.cardName('quick-actions')).exists(); diff --git a/ui/tests/integration/components/keymgmt/key-edit-test.js b/ui/tests/integration/components/keymgmt/key-edit-test.js index 1caefb0575..7bc8b5364d 100644 --- a/ui/tests/integration/components/keymgmt/key-edit-test.js +++ b/ui/tests/integration/components/keymgmt/key-edit-test.js @@ -9,6 +9,7 @@ import EmberObject from '@ember/object'; import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; import timestamp from 'core/utils/timestamp'; module('Integration | Component | keymgmt/key-edit', function (hooks) { @@ -43,7 +44,7 @@ module('Integration | Component | keymgmt/key-edit', function (hooks) { test('it renders show view as default', async function (assert) { assert.expect(8); await render(hbs``); - assert.dom('[data-test-secret-header]').hasText('Unicorns', 'Shows key name'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Unicorns', 'Shows key name'); assert.dom('[data-test-keymgmt-key-toolbar]').exists('Subnav toolbar exists'); assert.dom('[data-test-tab="Details"]').exists('Details tab exists'); assert.dom('[data-test-tab="Versions"]').exists('Versions tab exists'); @@ -67,7 +68,7 @@ module('Integration | Component | keymgmt/key-edit', function (hooks) { this.set('model', model); await render(hbs``); - assert.dom('[data-test-secret-header]').hasText('Edit Key', 'Shows edit header'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Edit Key', 'Shows edit header'); assert.dom('[data-test-keymgmt-key-toolbar]').doesNotExist('Subnav toolbar does not exist'); assert.dom('[data-test-tab="Details"]').doesNotExist('Details tab does not exist'); assert.dom('[data-test-tab="Versions"]').doesNotExist('Versions tab does not exist'); @@ -80,7 +81,7 @@ module('Integration | Component | keymgmt/key-edit', function (hooks) { this.set('model', model); await render(hbs``); - assert.dom('[data-test-secret-header]').hasText('Create Key', 'Shows edit header'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create Key', 'Shows edit header'); assert.dom('[data-test-keymgmt-key-toolbar]').doesNotExist('Subnav toolbar does not exist'); assert.dom('[data-test-tab="Details"]').doesNotExist('Details tab does not exist'); assert.dom('[data-test-tab="Versions"]').doesNotExist('Versions tab does not exist'); diff --git a/ui/tests/integration/components/keymgmt/provider-edit-test.js b/ui/tests/integration/components/keymgmt/provider-edit-test.js index 7d07618a28..9eceaf1676 100644 --- a/ui/tests/integration/components/keymgmt/provider-edit-test.js +++ b/ui/tests/integration/components/keymgmt/provider-edit-test.js @@ -58,7 +58,7 @@ module('Integration | Component | keymgmt/provider-edit', function (hooks) { }); test('it should render show view', async function (assert) { - assert.expect(10); + assert.expect(11); // override capability getters Object.defineProperties(this.model, { @@ -88,7 +88,8 @@ module('Integration | Component | keymgmt/provider-edit', function (hooks) { @tab={{this.tab}} />`); - assert.dom(`[${ts}-header]`).hasText('Provider foo-bar', 'Page header renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Provider', 'Page header renders'); + assert.dom(GENERAL.hdsPageHeaderSubtitle).hasText(' foo-bar', 'Page header renders subtitle'); assert.dom(`[${ts}-tab="details"]`).hasClass('active', 'Details tab is active'); const infoRows = this.element.querySelectorAll('[data-test-component="info-table-row"]'); @@ -194,7 +195,7 @@ module('Integration | Component | keymgmt/provider-edit', function (hooks) { @mode="create" />`); - assert.dom(`[${ts}-header]`).hasText('Create Provider', 'Page header renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create Provider', 'Page header renders'); assert.dom(`[${ts}-config-title]`).exists('Config header shown in create mode'); assert.dom(`[${ts}-creds-title]`).doesNotExist('New credentials header hidden in create mode'); @@ -256,7 +257,7 @@ module('Integration | Component | keymgmt/provider-edit', function (hooks) { @mode="edit" />`); - assert.dom(`[${ts}-header]`).hasText('Update Credentials', 'Page header renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Update Credentials', 'Page header renders'); assert.dom(`[${ts}-config-title]`).doesNotExist('Config header hidden in edit mode'); assert.dom(`[${ts}-creds-title]`).exists('New credentials header shown in edit mode'); diff --git a/ui/tests/integration/components/mfa-login-enforcement-header-test.js b/ui/tests/integration/components/mfa-login-enforcement-header-test.js index ffceab90ae..a289c36e2a 100644 --- a/ui/tests/integration/components/mfa-login-enforcement-header-test.js +++ b/ui/tests/integration/components/mfa-login-enforcement-header-test.js @@ -10,6 +10,7 @@ import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { clickTrigger } from 'ember-power-select/test-support/helpers'; import { setRunOptions } from 'ember-a11y-testing/test-support'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; module('Integration | Component | mfa-login-enforcement-header', function (hooks) { setupRenderingTest(hooks); @@ -31,10 +32,10 @@ module('Integration | Component | mfa-login-enforcement-header', function (hooks test('it renders heading', async function (assert) { await render(hbs``); - assert.dom('[data-test-mleh-title]').includesText('New enforcement'); - assert.dom('[data-test-mleh-title] svg').hasClass('hds-icon-lock', 'Lock icon renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).includesText('New enforcement'); + assert.dom(GENERAL.icon('lock')).hasClass('hds-icon-lock', 'Lock icon renders'); assert - .dom('[data-test-mleh-description]') + .dom(GENERAL.hdsPageHeaderDescription) .includesText('An enforcement will define which auth types', 'Description renders'); assert.dom('[data-test-mleh-radio]').doesNotExist('Radio cards are hidden when not inline display mode'); assert @@ -66,9 +67,9 @@ module('Integration | Component | mfa-login-enforcement-header', function (hooks /> `); - assert.dom('[data-test-mleh-title]').includesText('Enforcement'); + assert.dom(GENERAL.hdsPageHeaderTitle).includesText('Enforcement'); assert - .dom('[data-test-mleh-description]') + .dom(GENERAL.hdsPageHeaderDescription) .includesText('An enforcement includes the authentication types', 'Description renders'); for (const option of ['new', 'existing', 'skip']) { await click(`[data-test-mleh-radio="${option}"] input`); diff --git a/ui/tests/integration/components/mount-backend-form-test.js b/ui/tests/integration/components/mount-backend-form-test.js index 6bc07a0d63..8c7b25abf9 100644 --- a/ui/tests/integration/components/mount-backend-form-test.js +++ b/ui/tests/integration/components/mount-backend-form-test.js @@ -44,7 +44,7 @@ module('Integration | Component | mount backend form', function (hooks) { hbs`` ); assert - .dom(GENERAL.title) + .dom(GENERAL.hdsPageHeaderTitle) .hasText('Enable an Authentication Method', 'renders auth header in default state'); for (const method of filterEnginesByMountCategory({ diff --git a/ui/tests/integration/components/oidc/client-form-test.js b/ui/tests/integration/components/oidc/client-form-test.js index b689f242a5..ee7fe62c26 100644 --- a/ui/tests/integration/components/oidc/client-form-test.js +++ b/ui/tests/integration/components/oidc/client-form-test.js @@ -83,9 +83,7 @@ module('Integration | Component | oidc/client-form', function (hooks) { /> `); await click(GENERAL.button('More options')); - assert - .dom('[data-test-oidc-client-title]') - .hasText('Create Application', 'Form title renders correct text'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create Application', 'Form title renders correct text'); assert.dom(SELECTORS.clientSaveButton).hasText('Create', 'Save button has correct text'); assert.strictEqual(findAll('[data-test-field]').length, 6, 'renders all attribute fields'); assert @@ -150,7 +148,7 @@ module('Integration | Component | oidc/client-form', function (hooks) { /> `); await click(GENERAL.button('More options')); - assert.dom('[data-test-oidc-client-title]').hasText('Edit Application', 'Title renders correct text'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Edit Application', 'Title renders correct text'); assert.dom(SELECTORS.clientSaveButton).hasText('Update', 'Save button has correct text'); assert.dom('[data-test-input="name"]').isDisabled('Name input is disabled when editing'); assert.dom('[data-test-input="name"]').hasValue('test-app', 'Name input is populated with model value'); diff --git a/ui/tests/integration/components/oidc/provider-form-test.js b/ui/tests/integration/components/oidc/provider-form-test.js index 14b4f55e77..61b1bbe88d 100644 --- a/ui/tests/integration/components/oidc/provider-form-test.js +++ b/ui/tests/integration/components/oidc/provider-form-test.js @@ -67,9 +67,7 @@ module('Integration | Component | oidc/provider-form', function (hooks) { /> `); - assert - .dom('[data-test-oidc-provider-title]') - .hasText('Create Provider', 'Form title renders correct text'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create Provider', 'Form title renders correct text'); assert.dom(SELECTORS.providerSaveButton).hasText('Create', 'Save button has correct text'); assert .dom('[data-test-input="issuer"]') @@ -133,7 +131,7 @@ module('Integration | Component | oidc/provider-form', function (hooks) { /> `); - assert.dom('[data-test-oidc-provider-title]').hasText('Edit Provider', 'Title renders correct text'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Edit Provider', 'Title renders correct text'); assert.dom(SELECTORS.providerSaveButton).hasText('Update', 'Save button has correct text'); assert.dom('[data-test-input="name"]').isDisabled('Name input is disabled when editing'); assert diff --git a/ui/tests/integration/components/oidc/scope-form-test.js b/ui/tests/integration/components/oidc/scope-form-test.js index 0569b061aa..5e984df0ec 100644 --- a/ui/tests/integration/components/oidc/scope-form-test.js +++ b/ui/tests/integration/components/oidc/scope-form-test.js @@ -39,7 +39,7 @@ module('Integration | Component | oidc/scope-form', function (hooks) { /> `); - assert.dom('[data-test-oidc-scope-title]').hasText('Create Scope', 'Form title renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create Scope', 'Form title renders'); assert.dom(SELECTORS.scopeSaveButton).hasText('Create', 'Save button has correct label'); await click(SELECTORS.scopeSaveButton); @@ -88,7 +88,7 @@ module('Integration | Component | oidc/scope-form', function (hooks) { /> `); - assert.dom('[data-test-oidc-scope-title]').hasText('Edit Scope', 'Form title renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Edit Scope', 'Form title renders'); assert.dom(SELECTORS.scopeSaveButton).hasText('Update', 'Save button has correct label'); assert.dom(GENERAL.inputByAttr('name')).isDisabled('Name input is disabled when editing'); assert.dom(GENERAL.inputByAttr('name')).hasValue('test', 'Name input is populated with model value'); diff --git a/ui/tests/integration/components/totp/key-form-test.js b/ui/tests/integration/components/totp/key-form-test.js index 3a4af7d910..0ac788caf5 100644 --- a/ui/tests/integration/components/totp/key-form-test.js +++ b/ui/tests/integration/components/totp/key-form-test.js @@ -52,7 +52,7 @@ module('Integration | Component | totp/key-form', function (hooks) { /> `); - assert.dom('[data-test-secret-header]').hasText('Create a TOTP key', 'Form title renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create a TOTP key', 'Form title renders'); // check validation errors await click(GENERAL.submitButton); @@ -110,7 +110,7 @@ module('Integration | Component | totp/key-form', function (hooks) { /> `); - assert.dom('[data-test-secret-header]').hasText('Create a TOTP key', 'Form title renders'); + assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create a TOTP key', 'Form title renders'); // switch to non-generated form fields await click(GENERAL.radioByAttr('Other service'));