UI/pki key workflow tests (#18496)

* wip tests

* fix links

* Revert "wip tests"

This reverts commit aed9bb9b8fffb1b4d52d9c27644033ff3d983fff.

* wip tests

* add policy generator

* add workflow tests for key

* change apostrophe -___-

* fix workflow tests

* add update to key form tests

* fix capability check for read

* finish tests

* fix flash messages;

* rename policy generator file, update tests
This commit is contained in:
claire bontempo
2023-01-03 19:00:29 -07:00
committed by GitHub
parent 79c8f626c5
commit dcc4c031cc
17 changed files with 274 additions and 180 deletions

View File

@@ -51,7 +51,7 @@ export default class PkiKeyModel extends Model {
* Default to show UI elements unless we know they can't access the given path
*/
@lazyCapabilities(apiPath`${'backend'}/key/${'key_id'}`, 'backend', 'key_id') keyPath;
@lazyCapabilities(apiPath`${'backend'}/key/${'keyId'}`, 'backend', 'keyId') keyPath;
get canRead() {
return this.keyPath.get('canRead') !== false;
}

View File

@@ -13,7 +13,12 @@
<div class="toolbar-separator"></div>
{{/if}}
{{#if @key.privateKey}}
<DownloadButton class="toolbar-link" @filename={{this.model.name}} @data={{@key.privateKey}} @extension="pem">
<DownloadButton
class="toolbar-link"
@filename="{{@key.backend}}-{{or @key.keyName 'private-key'}}"
@data={{@key.privateKey}}
@extension="pem"
>
Download private key
<Chevron @isButton={{true}} />
</DownloadButton>

View File

@@ -22,7 +22,7 @@ export default class PkiKeyDetails extends Component<Args> {
async deleteKey() {
try {
await this.args.key.destroyRecord();
this.flashMessages.success('Key deleted successfully');
this.flashMessages.success('Key deleted successfully.');
this.router.transitionTo('vault.cluster.secrets.backend.pki.keys.index');
} catch (error) {
this.args.key.rollbackAttributes();

View File

@@ -40,7 +40,9 @@ export default class PkiKeyForm extends Component {
}
if (!isValid && isNew) return;
yield this.args.model.save({ adapterOptions: { import: false } });
this.flashMessages.success(`Successfully ${isNew ? 'generated' : 'updated'} the key ${keyName}.`);
this.flashMessages.success(
`Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}`
);
this.args.onSave();
} catch (error) {
this.errorBanner = errorMessage(error);

View File

@@ -35,7 +35,7 @@ export default class PkiKeyImport extends Component {
try {
const { keyName } = this.args.model;
yield this.args.model.save({ adapterOptions: { import: true } });
this.flashMessages.success(`Successfully imported key ${keyName}`);
this.flashMessages.success(`Successfully imported key${keyName ? ` ${keyName}.` : '.'}`);
this.args.onSave();
} catch (error) {
this.errorBanner = errorMessage(error);

View File

@@ -85,7 +85,7 @@
</LinkedBlock>
{{/each}}
{{else}}
<EmptyState @title="PKI not configured" @message="This PKI mount hasnt yet been configured with a certificate issuer.">
<EmptyState @title="PKI not configured" @message="This PKI mount hasn't yet been configured with a certificate issuer.">
<LinkTo @route="configuration.create">
Configure PKI
</LinkTo>

View File

@@ -19,7 +19,7 @@
/>
{{else}}
<Toolbar />
<EmptyState @title="PKI not configured" @message="This PKI mount hasnt yet been configured with a certificate issuer.">
<EmptyState @title="PKI not configured" @message="This PKI mount hasn't yet been configured with a certificate issuer.">
<LinkTo @route="configuration.create">
Configure PKI
</LinkTo>

View File

@@ -21,7 +21,7 @@
{{#if this.model.hasConfig}}
{{! TODO show overview items }}
{{else}}
<EmptyState @title="PKI not configured" @message="This PKI mount hasnt yet been configured with a certificate issuer.">
<EmptyState @title="PKI not configured" @message="This PKI mount hasn't yet been configured with a certificate issuer.">
<LinkTo @route="configuration.create" @model={{this.model.engine}}>
Configure PKI
</LinkTo>

View File

@@ -5,8 +5,9 @@ import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout';
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
import consoleClass from 'vault/tests/pages/components/console/ui-panel';
import { click, currentURL, fillIn, visit } from '@ember/test-helpers';
import { click, currentURL, fillIn, find, isSettled, visit } from '@ember/test-helpers';
import { SELECTORS } from 'vault/tests/helpers/pki/workflow';
import { adminPolicy, readerPolicy, updatePolicy } from 'vault/tests/helpers/policy-generator/pki';
const consoleComponent = create(consoleClass);
@@ -50,15 +51,7 @@ module('Acceptance | pki workflow', function (hooks) {
// Setup PKI engine
const mountPath = `pki-workflow-${new Date().getTime()}`;
await enablePage.enable('pki', mountPath);
await runCommands([
`write ${mountPath}/roles/some-role \
issuer_ref="default" \
allowed_domains="example.com" \
allow_subdomains=true \
max_ttl="720h"`,
]);
this.mountPath = mountPath;
await logout.visit();
});
@@ -70,6 +63,41 @@ module('Acceptance | pki workflow', function (hooks) {
await logout.visit();
});
test('empty state messages are correct when PKI not configured', async function (assert) {
assert.expect(10);
const assertEmptyState = (assert, resource) => {
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/${resource}`);
assert
.dom(SELECTORS.emptyStateTitle)
.hasText(
'PKI not configured',
`${resource} index renders correct empty state title when PKI not configured`
);
assert
.dom(SELECTORS.emptyStateMessage)
.hasText(
`This PKI mount hasn't yet been configured with a certificate issuer.`,
`${resource} index empty state message correct when PKI not configured`
);
};
await authPage.login(this.pkiAdminToken);
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
// TODO comment in when roles index empty state updated & update assert.expect() number
// await click(SELECTORS.rolesTab);
// assertEmptyState(assert, 'roles');
await click(SELECTORS.issuersTab);
assertEmptyState(assert, 'issuers');
await click(SELECTORS.certsTab);
assertEmptyState(assert, 'certificates');
await click(SELECTORS.keysTab);
assertEmptyState(assert, 'keys');
});
module('roles', function (hooks) {
hooks.beforeEach(async function () {
await authPage.login();
@@ -81,33 +109,9 @@ module('Acceptance | pki workflow', function (hooks) {
allow_subdomains=true \
max_ttl="720h"`,
]);
const pki_admin_policy = `
path "${this.mountPath}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
},
`;
const pki_reader_policy = `
path "${this.mountPath}/roles" {
capabilities = ["read", "list"]
},
path "${this.mountPath}/roles/*" {
capabilities = ["read", "list"]
},
`;
const pki_editor_policy = `
path "${this.mountPath}/roles" {
capabilities = ["read", "list"]
},
path "${this.mountPath}/roles/*" {
capabilities = ["read", "update"]
},
path "${this.mountPath}/issue/*" {
capabilities = ["update"]
},
path "${this.mountPath}/sign/*" {
capabilities = ["update"]
},
`;
const pki_admin_policy = adminPolicy(this.mountPath, 'roles');
const pki_reader_policy = readerPolicy(this.mountPath, 'roles');
const pki_editor_policy = updatePolicy(this.mountPath, 'roles');
this.pkiRoleReader = await tokenWithPolicy('pki-reader', pki_reader_policy);
this.pkiRoleEditor = await tokenWithPolicy('pki-editor', pki_editor_policy);
this.pkiAdminToken = await tokenWithPolicy('pki-admin', pki_admin_policy);
@@ -231,6 +235,129 @@ module('Acceptance | pki workflow', function (hooks) {
});
});
module('keys', function (hooks) {
hooks.beforeEach(async function () {
await authPage.login();
// base config pki so empty state doesn't show
await runCommands([`write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test"`]);
const pki_admin_policy = adminPolicy(this.mountPath);
const pki_reader_policy = readerPolicy(this.mountPath, 'keys', true);
const pki_editor_policy = updatePolicy(this.mountPath, 'keys');
this.pkiKeyReader = await tokenWithPolicy('pki-reader', pki_reader_policy);
this.pkiKeyEditor = await tokenWithPolicy('pki-editor', pki_editor_policy);
this.pkiAdminToken = await tokenWithPolicy('pki-admin', pki_admin_policy);
await logout.visit();
});
test('shows correct items if user has all permissions', async function (assert) {
await authPage.login(this.pkiAdminToken);
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
await click(SELECTORS.keysTab);
// index page
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys`);
assert
.dom(SELECTORS.keyPages.importKey)
.hasAttribute(
'href',
`/ui/vault/secrets/${this.mountPath}/pki/keys/import`,
'import link renders with correct url'
);
let keyId = find(SELECTORS.keyPages.keyId).innerText;
assert.dom('.linked-block').exists({ count: 1 }, 'One key is in list');
await click('.linked-block');
// details page
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`);
assert.dom(SELECTORS.keyPages.downloadButton).doesNotExist('does not download button for private key');
// edit page
await click(SELECTORS.keyPages.keyEditLink);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/edit`);
await click(SELECTORS.keyForm.keyCancelButton);
assert.strictEqual(
currentURL(),
`/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`,
'navigates back to details on cancel'
);
await visit(`/vault/secrets/${this.mountPath}/pki/keys/${keyId}/edit`);
await fillIn(SELECTORS.keyForm.keyNameInput, 'test-key');
await click(SELECTORS.keyForm.keyCreateButton);
assert.strictEqual(
currentURL(),
`/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`,
'navigates to details after save'
);
await this.pauseTest;
assert.dom(SELECTORS.keyPages.keyNameValue).hasText('test-key', 'updates key name');
// key generate and delete navigation
await visit(`/vault/secrets/${this.mountPath}/pki/keys`);
await click(SELECTORS.keyPages.generateKey);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/create`);
await fillIn(SELECTORS.keyForm.typeInput, 'exported');
await fillIn(SELECTORS.keyForm.keyTypeInput, 'rsa');
await click(SELECTORS.keyForm.keyCreateButton);
keyId = find(SELECTORS.keyPages.keyIdValue).innerText;
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`);
assert
.dom(SELECTORS.alertBanner)
.hasText(
'Next steps This private key material will only be available once. Copy or download it now.',
'renders banner to save private key'
);
assert.dom(SELECTORS.keyPages.downloadButton).exists('renders download button');
await click(SELECTORS.keyPages.keyDeleteButton);
await click(SELECTORS.keyPages.confirmDelete);
assert.strictEqual(
currentURL(),
`/vault/secrets/${this.mountPath}/pki/keys`,
'navigates back to key list view on delete'
);
});
test('it hide corrects actions for user with read policy', async function (assert) {
await authPage.login(this.pkiKeyReader);
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
await click(SELECTORS.keysTab);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys`);
await isSettled();
assert.dom(SELECTORS.keyPages.importKey).doesNotExist();
assert.dom(SELECTORS.keyPages.generateKey).doesNotExist();
assert.dom('.linked-block').exists({ count: 1 }, 'One key is in list');
const keyId = find(SELECTORS.keyPages.keyId).innerText;
await click(SELECTORS.keyPages.popupMenuTrigger);
assert.dom(SELECTORS.keyPages.popupMenuEdit).hasClass('disabled', 'popup menu edit link is disabled');
await click(SELECTORS.keyPages.popupMenuDetails);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`);
assert.dom(SELECTORS.keyPages.keyDeleteButton).doesNotExist('Delete key button is not shown');
assert.dom(SELECTORS.keyPages.keyEditLink).doesNotExist('Edit key button does not render');
});
test('it shows correct toolbar items for the user with update policy', async function (assert) {
await authPage.login(this.pkiKeyEditor);
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
await click(SELECTORS.keysTab);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys`);
await isSettled();
assert.dom(SELECTORS.keyPages.importKey).exists('import action exists');
assert.dom(SELECTORS.keyPages.generateKey).exists('generate action exists');
assert.dom('.linked-block').exists({ count: 1 }, 'One key is in list');
const keyId = find(SELECTORS.keyPages.keyId).innerText;
await click(SELECTORS.keyPages.popupMenuTrigger);
assert
.dom(SELECTORS.keyPages.popupMenuEdit)
.doesNotHaveClass('disabled', 'popup menu edit link is not disabled');
await click('.linked-block');
assert.dom(SELECTORS.keyPages.keyDeleteButton).doesNotExist('Delete key button is not shown');
await click(SELECTORS.keyPages.keyEditLink);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/edit`);
assert.dom(SELECTORS.keyPages.title).hasText('Edit key');
await click(SELECTORS.keyForm.keyCancelButton);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`);
});
});
module('issuers', function (hooks) {
hooks.beforeEach(async function () {
await authPage.login();

View File

@@ -1,9 +0,0 @@
export const SELECTORS = {
importKey: '[data-test-pki-key-import]',
generateKey: '[data-test-pki-key-generate]',
keyId: '[data-test-key-id]',
keyName: '[data-test-key-name]',
popupMenuTrigger: '[data-test-popup-menu-trigger]',
popupMenuDetails: '[data-test-key-menu-link="details"]',
popupMenuEdit: '[data-test-key-menu-link="edit"]',
};

View File

@@ -1,12 +1,20 @@
export const SELECTORS = {
breadcrumbContainer: '[data-test-breadcrumbs]',
breadcrumbs: '[data-test-breadcrumbs] li',
// key index
importKey: '[data-test-pki-key-import]',
generateKey: '[data-test-pki-key-generate]',
keyId: '[data-test-key-id]',
keyName: '[data-test-key-name]',
popupMenuTrigger: '[data-test-popup-menu-trigger]',
popupMenuDetails: '[data-test-key-menu-link="details"]',
popupMenuEdit: '[data-test-key-menu-link="edit"]',
// key details
title: '[data-test-key-details-title]',
keyIdValue: '[data-test-value-div="Key ID"]',
keyNameValue: '[data-test-value-div="Key name"]',
keyTypeValue: '[data-test-value-div="Key type"]',
keyBitsValue: '[data-test-value-div="Key bits"]',
keyDeleteButton: '[data-test-pki-key-delete] button',
downloadButton: '[data-test-download-button]',
keyEditLink: '[data-test-pki-key-edit]',
confirmDelete: '[data-test-confirm-button]',
};

View File

@@ -1,11 +1,16 @@
import { SELECTORS as ROLEFORM } from './pki-role-form';
import { SELECTORS as GENERATECERT } from './pki-role-generate';
import { SELECTORS as KEYFORM } from './pki-key-form';
import { SELECTORS as KEYPAGES } from './page/pki-keys';
import { SELECTORS as ISSUERDETAILS } from './pki-issuer-details';
export const SELECTORS = {
breadcrumbContainer: '[data-test-breadcrumbs]',
breadcrumbs: '[data-test-breadcrumbs] li',
pageTitle: '[data-test-pki-role-page-title]',
alertBanner: '[data-test-alert-banner="alert"]',
emptyStateTitle: '[data-test-empty-state-title]',
emptyStateMessage: '[data-test-empty-state-message]',
// TABS
overviewTab: '[data-test-secret-list-tab="Overview"]',
rolesTab: '[data-test-secret-list-tab="Roles"]',
@@ -25,6 +30,14 @@ export const SELECTORS = {
generateCertForm: {
...GENERATECERT,
},
// KEYS
keyForm: {
...KEYFORM,
},
keyPages: {
...KEYPAGES,
},
// ISSUERS
issuerDetails: {
title: '[data-test-pki-issuer-page-title]',
...ISSUERDETAILS,

View File

@@ -0,0 +1,52 @@
import { singularize } from 'ember-inflector';
export const adminPolicy = (mountPath) => {
return `
path "${mountPath}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
},
`;
};
// keys require singularized paths for GET
export const readerPolicy = (mountPath, resource) => {
return `
path "${mountPath}/${resource}" {
capabilities = ["read", "list"]
},
path "${mountPath}/${resource}/*" {
capabilities = ["read", "list"]
},
path "${mountPath}/${singularize(resource)}" {
capabilities = ["read", "list"]
},
path "${mountPath}/${singularize(resource)}/*" {
capabilities = ["read", "list"]
},
`;
};
export const updatePolicy = (mountPath, resource) => {
return `
path "${mountPath}/${resource}" {
capabilities = ["read", "list"]
},
path "${mountPath}/${resource}/*" {
capabilities = ["read", "update"]
},
path "${mountPath}/${singularize(resource)}/*" {
capabilities = ["read", "update"]
},
path "${mountPath}/issue/*" {
capabilities = ["update"]
},
path "${mountPath}/generate/*" {
capabilities = ["update"]
},
path "${mountPath}/import" {
capabilities = ["update"]
},
path "${mountPath}/sign/*" {
capabilities = ["update"]
},
`;
};

View File

@@ -4,7 +4,7 @@ import { click, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupEngine } from 'ember-engines/test-support';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-key-details';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-keys';
module('Integration | Component | pki key details page', function (hooks) {
setupRenderingTest(hooks);

View File

@@ -4,7 +4,7 @@ import { click, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupEngine } from 'ember-engines/test-support';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-key-list';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-keys';
module('Integration | Component | pki key list page', function (hooks) {
setupRenderingTest(hooks);

View File

@@ -122,7 +122,7 @@ module('Integration | Component | pki key form', function (hooks) {
});
test('it should rollback attributes or unload record on cancel', async function (assert) {
assert.expect(2);
assert.expect(5);
this.onCancel = () => assert.ok(true, 'onCancel callback fires');
await render(
hbs`
@@ -138,35 +138,32 @@ module('Integration | Component | pki key form', function (hooks) {
await click(SELECTORS.keyCancelButton);
assert.true(this.model.isDestroyed, 'new model is unloaded on cancel');
/* COMMENT IN WHEN EDIT IS COMPLETE
this.store.pushPayload('pki/key', {
modelName: this.modelName,
keyName: 'test-key',
this.store.pushPayload('pki/key', {
modelName: 'pki/key',
key_name: 'test-key',
type: 'exported',
keyId: 'some-key-id',
keyType: 'rsa',
keyBits: '2048',
};);
this.model = this.store.peekRecord('pki/key', this.data.keyId;
key_id: 'some-key-id',
key_type: 'rsa',
key_bits: '2048',
});
this.model = this.store.peekRecord('pki/key', 'some-key-id');
await render(
hbs`
await render(
hbs`
<PkiKeyForm
@model={{this.model}}
@onCancel={{this.onCancel}}
@onSave={{this.onSave}}
/>
`,
{ owner: this.engine }
);
{ owner: this.engine }
);
await fillIn(SELECTORS.keyNameInput, 'new-name');
await click(SELECTORS.keyCancelButton);
assert.strictEqual(this.model.keyName, undefined, 'Model attributes rolled back on cancel');
*/
await fillIn(SELECTORS.keyNameInput, 'new-name');
await click(SELECTORS.keyCancelButton);
assert.strictEqual(this.model.keyName, 'test-key', 'Model name rolled back on cancel');
await fillIn(SELECTORS.keyNameInput, 'new-name');
await click(SELECTORS.keyCreateButton);
assert.strictEqual(this.model.keyName, 'new-name', 'Model name correctly save on create');
});
/* FUTURE TEST TODO:
* it should update key
*/
});

View File

@@ -1,101 +0,0 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupEngine } from 'ember-engines/test-support';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { SELECTORS } from 'vault/tests/helpers/pki/page/pki-key-list';
module('Integration | Component | pki key list page', function (hooks) {
setupRenderingTest(hooks);
setupEngine(hooks, 'pki');
setupMirage(hooks);
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.secretMountPath = this.owner.lookup('service:secret-mount-path');
this.secretMountPath.currentPath = 'pki-test';
this.store.pushPayload('pki/key', {
modelName: 'pki/key',
key_id: '724862ff-6438-bad0-b598-77a6c7f4e934',
key_type: 'ec',
key_name: 'test-key',
});
this.store.pushPayload('pki/key', {
modelName: 'pki/key',
key_id: '9fdddf12-9ce3-0268-6b34-dc1553b00175',
key_type: 'rsa',
key_name: 'another-key',
});
this.keyModels = this.store.peekAll('pki/key');
});
test('it renders empty state when no keys exist', async function (assert) {
assert.expect(3);
this.keyModels = [];
await render(
hbs`
<Page::PkiKeyList
@keyModels={{this.keyModels}}
@mountPoint="vault.cluster.secrets.backend.pki"
@canImportKey={{true}}
@canGenerateKey={{true}}
/>,
`,
{ owner: this.engine }
);
assert
.dom('[data-test-empty-state-title]')
.hasText('No keys yet', 'renders empty state that no keys exist');
assert.dom(SELECTORS.importKey).exists('renders toolbar with import action');
assert.dom(SELECTORS.generateKey).exists('renders toolbar with generate action');
});
test('it renders list of keys and actions when permission allowed', async function (assert) {
assert.expect(6);
await render(
hbs`
<Page::PkiKeyList
@keyModels={{this.keyModels}}
@mountPoint="vault.cluster.secrets.backend.pki"
@canImportKey={{true}}
@canGenerateKey={{true}}
@canRead={{true}}
@canEdit={{true}}
/>,
`,
{ owner: this.engine }
);
assert
.dom(SELECTORS.keyId)
.hasText('724862ff-6438-bad0-b598-77a6c7f4e934', 'linked block renders key id');
assert.dom(SELECTORS.keyName).hasText('test-key', 'linked block renders key name');
assert.dom(SELECTORS.importKey).exists('renders import action');
assert.dom(SELECTORS.generateKey).exists('renders generate action');
await click(SELECTORS.popupMenuTrigger);
assert.dom(SELECTORS.popupMenuDetails).exists('details link exists');
assert.dom(SELECTORS.popupMenuEdit).exists('edit link exists');
});
test('it hides or disables actions when permission denied', async function (assert) {
assert.expect(4);
await render(
hbs`
<Page::PkiKeyList
@keyModels={{this.keyModels}}
@mountPoint="vault.cluster.secrets.backend.pki"
@canImportKey={{false}}
@canGenerateKey={{false}}
@canRead={{false}}
@canEdit={{false}}
/>,
`,
{ owner: this.engine }
);
assert.dom(SELECTORS.importKey).doesNotExist('renders import action');
assert.dom(SELECTORS.generateKey).doesNotExist('renders generate action');
await click(SELECTORS.popupMenuTrigger);
assert.dom(SELECTORS.popupMenuDetails).hasClass('disabled', 'details link enabled');
assert.dom(SELECTORS.popupMenuEdit).hasClass('disabled', 'edit link enabled');
});
});