mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
* update styles and casing * fix check for default secret engines * show content behind intro modal * revert header changes * update alert and test * add test coverage and update flash message * update doc link style class and update test * update tests * update test to avoid state mismatches * update test Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com>
219 lines
9.7 KiB
JavaScript
219 lines
9.7 KiB
JavaScript
/**
|
|
* Copyright IBM Corp. 2016, 2025
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { module, test } from 'qunit';
|
|
import { setupRenderingTest } from 'vault/tests/helpers';
|
|
import { render, click, findAll, triggerEvent, fillIn } from '@ember/test-helpers';
|
|
import { hbs } from 'ember-cli-htmlbars';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import sinon from 'sinon';
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
|
import { overrideResponse } from 'vault/tests/helpers/stubs';
|
|
import { createSecretsEngine } from 'vault/tests/helpers/secret-engine/secret-engine-helpers';
|
|
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
|
|
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
|
|
|
const SELECTORS = {
|
|
intro: '[data-test-intro]',
|
|
};
|
|
|
|
module('Integration | Component | secret-engine/list', function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
setupMirage(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
this.server.post('/sys/capabilities-self', () => ({
|
|
data: {
|
|
capabilities: ['root'],
|
|
},
|
|
}));
|
|
this.version = this.owner.lookup('service:version');
|
|
this.router = this.owner.lookup('service:router');
|
|
this.router.transitionTo = sinon.stub();
|
|
this.router.refresh = sinon.stub();
|
|
this.flashMessages = this.owner.lookup('service:flash-messages');
|
|
this.flashMessages.registerTypes(['success', 'danger']);
|
|
this.flashSuccessSpy = sinon.spy(this.flashMessages, 'success');
|
|
this.flashDangerSpy = sinon.spy(this.flashMessages, 'danger');
|
|
this.uid = uuidv4();
|
|
// generate a model of cubbyhole, kv, and nomad
|
|
this.secretEngineModels = [
|
|
createSecretsEngine(undefined, 'cubbyhole', 'cubbyhole-test'),
|
|
createSecretsEngine(undefined, 'kv', 'kv-test'),
|
|
createSecretsEngine(undefined, 'aws', 'aws-1', 'v1.0.0'),
|
|
createSecretsEngine(undefined, 'aws', 'aws-2', 'v2.0.0'),
|
|
createSecretsEngine(undefined, 'nomad', 'nomad-test'),
|
|
createSecretsEngine(undefined, 'badType', 'external-test'),
|
|
];
|
|
});
|
|
|
|
hooks.afterEach(async function () {
|
|
// ensure clean state
|
|
localStorage.clear();
|
|
});
|
|
|
|
test('it allows you to disable an engine', async function (assert) {
|
|
const enginePath = 'kv-test';
|
|
this.server.delete(`sys/mounts/${enginePath}`, () => {
|
|
assert.true(true, 'Request is made to delete engine');
|
|
return overrideResponse(204);
|
|
});
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
|
|
assert.dom(GENERAL.linkTo(`${enginePath}/`)).exists('shows the link for the kvv2 secrets engine');
|
|
await click(`${GENERAL.listItem(`${enginePath}/`)} ${GENERAL.menuTrigger}`);
|
|
await click(GENERAL.menuItem('Delete'));
|
|
await click(GENERAL.confirmButton);
|
|
|
|
assert.true(
|
|
this.flashSuccessSpy.calledWith(`The kv Secrets Engine at ${enginePath}/ has been disabled.`),
|
|
'Flash message shows that engine was disabled.'
|
|
);
|
|
});
|
|
|
|
test('hovering over the icon of an external unrecognized engine type sets unrecognized tooltip text', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), 'external-test');
|
|
|
|
const engineTooltip = document.querySelector(GENERAL.tooltip('Backend type'));
|
|
await triggerEvent(engineTooltip, 'mouseenter');
|
|
|
|
assert
|
|
.dom(engineTooltip.nextSibling)
|
|
.hasText(
|
|
`This engine's type is not recognized by the UI. Please use the CLI to manage this engine.`,
|
|
'shows tooltip text for unknown engine'
|
|
);
|
|
});
|
|
|
|
test('hovering over the icon of an unsupported engine sets unsupported tooltip text', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), 'nomad');
|
|
|
|
const engineTooltip = document.querySelector(GENERAL.tooltip('Backend type'));
|
|
await triggerEvent(engineTooltip, 'mouseenter');
|
|
|
|
assert
|
|
.dom(engineTooltip.nextSibling)
|
|
.hasText(
|
|
'The UI only supports configuration views for these secret engines. The CLI must be used to manage other engine resources.',
|
|
'shows tooltip text for unsupported engine'
|
|
);
|
|
});
|
|
|
|
test('hovering over the icon of a supported engine sets engine name as tooltip', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), 'aws-1');
|
|
|
|
const engineTooltip = document.querySelector(GENERAL.tooltip('Backend type'));
|
|
await triggerEvent(engineTooltip, 'mouseenter');
|
|
|
|
assert.dom(engineTooltip.nextSibling).hasText('AWS', 'shows tooltip text for supported engine with name');
|
|
});
|
|
|
|
test('hovering over the icon of a kv engine shows engine name and version', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}}/>`);
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), `kv-test`);
|
|
|
|
const engineTooltip = document.querySelector(GENERAL.tooltip('Backend type'));
|
|
await triggerEvent(engineTooltip, 'mouseenter');
|
|
assert
|
|
.dom(engineTooltip.nextSibling)
|
|
.hasText('KV version 1', 'shows tooltip text for kv engine with version');
|
|
});
|
|
|
|
test('path name does not render as link for unsupported secret engines', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
assert
|
|
.dom(GENERAL.linkTo('nomad-test/'))
|
|
.doesNotExist(`path text doesn't render as a link for unsupported nomad engine.`);
|
|
assert.dom(GENERAL.linkTo('aws-1/')).exists(`path text renders as a link for supported aws engines.`);
|
|
});
|
|
|
|
test('it filters by engine path and engine type', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
// filter by type
|
|
await click(GENERAL.toggleInput('filter-by-engine-type'));
|
|
await click(GENERAL.checkboxByAttr('aws'));
|
|
|
|
const rows = findAll(SES.secretPath());
|
|
const rowsAws = Array.from(rows).filter((row) => row.innerText.includes('aws'));
|
|
assert.strictEqual(rows.length, rowsAws.length, 'all rows returned are aws');
|
|
|
|
// clear filter by type
|
|
await click(GENERAL.button('Clear all'));
|
|
assert.true(document.querySelectorAll(GENERAL.tableRow()).length > 1, 'filter has been removed');
|
|
|
|
// filter by path
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), 'kv');
|
|
const singleRow = document.querySelectorAll(SES.secretPath());
|
|
assert.dom(singleRow[0]).includesText('kv', 'shows the filtered by path engine');
|
|
|
|
// clear filter by engine path
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), '');
|
|
const rowsAgain = document.querySelectorAll(GENERAL.tableRow());
|
|
assert.true(rowsAgain.length > 1, 'search filter text has been removed');
|
|
});
|
|
|
|
test('it filters by engine version', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
// select engine type
|
|
await click(GENERAL.toggleInput('filter-by-engine-type'));
|
|
await click(GENERAL.checkboxByAttr('aws'));
|
|
|
|
// filter by version
|
|
await click(GENERAL.toggleInput('filter-by-engine-version'));
|
|
await click(GENERAL.checkboxByAttr('v2.0.0'));
|
|
const singleRow = document.querySelectorAll(SES.secretPath());
|
|
assert.dom(singleRow[0]).includesText('aws-2', 'shows the single engine filtered by version');
|
|
});
|
|
|
|
test('it applies overflow styling', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
assert
|
|
.dom(GENERAL.tableData(0, 'path'))
|
|
.hasClass('text-overflow-ellipsis', 'secret engine path has text overflow class ');
|
|
});
|
|
|
|
test('it shows the intro page when only default engines are enabled', async function (assert) {
|
|
// Only cubbyhole engine exists (default engine)
|
|
const defaultEngines = [createSecretsEngine(undefined, 'cubbyhole', 'cubbyhole')];
|
|
this.secretEngineModels = defaultEngines;
|
|
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
|
|
assert.dom(SELECTORS.intro).exists('Intro page is shown');
|
|
assert.dom(GENERAL.button('enable')).exists('Shows enable button');
|
|
assert.dom(GENERAL.button('Skip')).exists('Shows skip button');
|
|
});
|
|
|
|
test('it does not show the intro page when other engines exist', async function (assert) {
|
|
// Has engines beyond the default cubbyhole
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
|
|
assert.dom(SELECTORS.intro).doesNotExist('Intro modal is not shown when engines exist');
|
|
assert.dom(GENERAL.button('intro')).doesNotExist('Intro button is not shown');
|
|
});
|
|
|
|
test('it does not show the intro page when a filter has no results', async function (assert) {
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
await fillIn(GENERAL.inputSearch('secret-engine-path'), `foobar`);
|
|
assert.dom(SELECTORS.intro).doesNotExist('Intro modal is not shown when engines exist');
|
|
assert.dom(GENERAL.button('intro')).doesNotExist('Intro button is not shown');
|
|
});
|
|
|
|
test('it can show the intro modal after dismissal', async function (assert) {
|
|
const defaultEngines = [createSecretsEngine(undefined, 'cubbyhole', 'cubbyhole')];
|
|
this.secretEngineModels = defaultEngines;
|
|
|
|
await render(hbs`<SecretEngine::List @secretEngines={{this.secretEngineModels}} />`);
|
|
await click(GENERAL.button('Skip'));
|
|
assert.dom(SELECTORS.intro).doesNotExist('Intro is dismissed');
|
|
|
|
await click(GENERAL.button('intro'));
|
|
assert.dom(SELECTORS.intro).exists('Intro can be shown again after reset');
|
|
});
|
|
});
|