mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
UI: PKI Clean up dirty model on leave (#19058)
This commit is contained in:
@@ -57,7 +57,7 @@ export default class PkiIssuerModel extends PkiCertificateBaseModel {
|
||||
|
||||
@attr({
|
||||
label: 'Usage',
|
||||
subText: 'Allowed usages for this issuer. It can always be read',
|
||||
subText: 'Allowed usages for this issuer. It can always be read.',
|
||||
editType: 'yield',
|
||||
valueOptions: [
|
||||
{ label: 'Issuing certificates', value: 'issuing-certificates' },
|
||||
|
||||
@@ -4,11 +4,39 @@ import { inject as service } from '@ember/service';
|
||||
import Ember from 'ember';
|
||||
|
||||
/**
|
||||
* Confirm that the user wants to discard unsaved changes before leaving the page.
|
||||
* This decorator hooks into the willTransition action. If you override setupController,
|
||||
* be sure to set 'model' on the controller to store data or this won't work.
|
||||
* Confirm that the user wants to discard unsaved changes before leaving the page. This decorator hooks into
|
||||
* the willTransition action. If you override setupController, be sure to set 'model' on the controller to
|
||||
* store data or this won't work.
|
||||
*
|
||||
* By default it will check if the route's model is dirty and prompt when leaving. Usage for this is simple:
|
||||
*
|
||||
* @withConfirmLeave()
|
||||
* export default class MyRoute extends Route {
|
||||
* @service store;
|
||||
* model() {
|
||||
* return this.store.createRecord('some-model')
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If the route has ember-data models at multiple paths, you can pass an array of secondary modelPaths which
|
||||
* will rollback on exit after the prompt for the first model is confirmed. In the example below, the window
|
||||
* will only prompt on leave if `model.main` is dirty. Either way, `model.secondary` and `model.optional`
|
||||
* will be cleaned up from the data store.
|
||||
*
|
||||
* @withConfirmLeave('model.main', ['model.secondary', 'model.optional'])
|
||||
* export default class MyRoute extends Route {
|
||||
* @service store;
|
||||
* model() {
|
||||
* return {
|
||||
* main: this.store.peekRecord('some-model', 'abc1')
|
||||
* secondary: this.store.createRecord('some-other')
|
||||
* optional: this.store.createRecord('optional')
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export function withConfirmLeave() {
|
||||
export function withConfirmLeave(modelPath = 'model', silentCleanupPaths) {
|
||||
return function decorator(SuperClass) {
|
||||
if (!Object.prototype.isPrototypeOf.call(Route, SuperClass)) {
|
||||
// eslint-disable-next-line
|
||||
@@ -20,34 +48,42 @@ export function withConfirmLeave() {
|
||||
return class ConfirmLeave extends SuperClass {
|
||||
@service store;
|
||||
|
||||
_rollbackModel(modelPath) {
|
||||
const model = this.controller.get(modelPath);
|
||||
// we only want to complete rollback if the model is dirty and not saving
|
||||
if (model && model.hasDirtyAttributes && !model.isSaving) {
|
||||
const method = model.isNew ? 'unloadRecord' : 'rollbackAttributes';
|
||||
model[method]();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
willTransition(transition) {
|
||||
try {
|
||||
super.willTransition(...arguments);
|
||||
} catch (e) {
|
||||
// if the SuperClass doesn't have willTransition
|
||||
// defined it will throw an error.
|
||||
// defined calling it will throw an error.
|
||||
}
|
||||
const model = this.controller.get('model');
|
||||
if (model && model.hasDirtyAttributes) {
|
||||
const model = this.controller.get(modelPath);
|
||||
|
||||
if (model && model.hasDirtyAttributes && !model.isSaving) {
|
||||
if (
|
||||
Ember.testing ||
|
||||
window.confirm(
|
||||
'You have unsaved changes. Navigating away will discard these changes. Are you sure you want to discard your changes?'
|
||||
)
|
||||
) {
|
||||
// error is thrown when you attempt to unload a record that is inFlight (isSaving)
|
||||
if (!model || !model.unloadRecord || model.isSaving) {
|
||||
return;
|
||||
}
|
||||
model.rollbackAttributes();
|
||||
model.destroy();
|
||||
return true;
|
||||
this._rollbackModel(modelPath);
|
||||
} else {
|
||||
transition.abort();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
silentCleanupPaths?.forEach((pathToModel) => {
|
||||
this._rollbackModel(pathToModel);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,14 +4,9 @@ import RouterService from '@ember/routing/router-service';
|
||||
import FlashMessageService from 'vault/services/flash-messages';
|
||||
import { inject as service } from '@ember/service';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
import PkiKeyModel from 'vault/models/pki/key';
|
||||
interface Args {
|
||||
key: {
|
||||
rollbackAttributes: () => void;
|
||||
destroyRecord: () => void;
|
||||
backend: string;
|
||||
keyName: string;
|
||||
keyId: string;
|
||||
};
|
||||
key: PkiKeyModel;
|
||||
}
|
||||
|
||||
export default class PkiKeyDetails extends Component<Args> {
|
||||
|
||||
@@ -5,14 +5,10 @@ import FlashMessageService from 'vault/services/flash-messages';
|
||||
import SecretMountPath from 'vault/services/secret-mount-path';
|
||||
import { inject as service } from '@ember/service';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
import PkiRoleModel from 'vault/models/pki/role';
|
||||
|
||||
// TODO: pull this in from route model once it's TS
|
||||
interface Args {
|
||||
role: {
|
||||
id: string;
|
||||
rollbackAttributes: () => void;
|
||||
destroyRecord: () => void;
|
||||
};
|
||||
role: PkiRoleModel;
|
||||
}
|
||||
|
||||
export default class DetailsPage extends Component<Args> {
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
<PkiGenerateRoot
|
||||
@model={{@config}}
|
||||
@urls={{@urls}}
|
||||
@onCancel={{this.cancel}}
|
||||
@onCancel={{@onCancel}}
|
||||
@adapterOptions={{hash actionType=@config.actionType useIssuer=@config.canGenerateIssuerRoot}}
|
||||
/>
|
||||
{{else if (eq @config.actionType "generate-csr")}}
|
||||
<PkiGenerateCsr
|
||||
@model={{@config}}
|
||||
@onSave={{transition-to "vault.cluster.secrets.backend.pki.issuers"}}
|
||||
@onCancel={{this.cancel}}
|
||||
@onCancel={{@onCancel}}
|
||||
/>
|
||||
{{else}}
|
||||
<EmptyState
|
||||
@@ -58,7 +58,7 @@
|
||||
<button type="button" class="button is-primary" disabled={{true}} data-test-pki-config-save>
|
||||
Done
|
||||
</button>
|
||||
<button type="button" class="button has-left-margin-s" {{on "click" this.cancel}} data-test-pki-config-cancel>
|
||||
<button type="button" class="button has-left-margin-s" {{on "click" @onCancel}} data-test-pki-config-cancel>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,6 @@ import { inject as service } from '@ember/service';
|
||||
import Store from '@ember-data/store';
|
||||
import Router from '@ember/routing/router';
|
||||
import FlashMessageService from 'vault/services/flash-messages';
|
||||
import { action } from '@ember/object';
|
||||
import PkiActionModel from 'vault/models/pki/action';
|
||||
|
||||
interface Args {
|
||||
@@ -48,9 +47,4 @@ export default class PkiConfigureForm extends Component<Args> {
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@action cancel() {
|
||||
this.args.config.rollbackAttributes();
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.overview');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<button type="submit" class="button is-primary" data-test-pki-generate-root-save>
|
||||
Done
|
||||
</button>
|
||||
<button {{on "click" this.cancel}} type="button" class="button has-left-margin-s" data-test-pki-generate-root-cancel>
|
||||
<button {{on "click" @onCancel}} type="button" class="button has-left-margin-s" data-test-pki-generate-root-cancel>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
type="button"
|
||||
class="button has-left-margin-s"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" this.cancel}}
|
||||
{{on "click" @onCancel}}
|
||||
data-test-pki-key-cancel
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
@@ -12,7 +11,7 @@ import { waitFor } from '@ember/test-waiters';
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <PkiKeyForm @model={{this.model}}/>
|
||||
* <PkiKeyForm @model={{this.model}} @onCancel={{transition-to "vault.cluster"}} @onSave={{transition-to "vault.cluster"}} />
|
||||
* ```
|
||||
*
|
||||
* @param {Object} model - pki/key model.
|
||||
@@ -49,11 +48,4 @@ export default class PkiKeyForm extends Component {
|
||||
this.invalidFormAlert = 'There was an error submitting this form.';
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
cancel() {
|
||||
const method = this.args.model.isNew ? 'unloadRecord' : 'rollbackAttributes';
|
||||
this.args.model[method]();
|
||||
this.args.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
type="button"
|
||||
class="button has-left-margin-s"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" this.cancel}}
|
||||
{{on "click" @onCancel}}
|
||||
data-test-pki-role-cancel
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
@@ -59,11 +58,4 @@ export default class PkiRoleForm extends Component {
|
||||
this.invalidFormAlert = 'There was an error submitting this form.';
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
cancel() {
|
||||
const method = this.args.model.isNew ? 'unloadRecord' : 'rollbackAttributes';
|
||||
this.args.model[method]();
|
||||
this.args.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
import { hash } from 'rsvp';
|
||||
|
||||
@withConfirmLeave('model.config', ['model.urls'])
|
||||
export default class PkiConfigurationCreateRoute extends Route {
|
||||
@service secretMountPath;
|
||||
@service store;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
import PkiIssuersIndexRoute from '.';
|
||||
|
||||
@withConfirmLeave()
|
||||
export default class PkiIssuersGenerateIntermediateRoute extends PkiIssuersIndexRoute {
|
||||
model() {
|
||||
return this.store.createRecord('pki/action', { actionType: 'generate-csr' });
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
@withConfirmLeave()
|
||||
export default class PkiIssuersGenerateRootRoute extends Route {
|
||||
@service secretMountPath;
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
return this.store.createRecord('pki/action');
|
||||
return this.store.createRecord('pki/action', { actionType: 'generate-root' });
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
import PkiIssuerDetailsRoute from './details';
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
export default class PkiIssuerEditRoute extends PkiIssuerDetailsRoute {}
|
||||
@withConfirmLeave()
|
||||
export default class PkiIssuerDetailRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
@service pathHelp;
|
||||
|
||||
beforeModel() {
|
||||
// Must call this promise before the model hook otherwise it doesn't add OpenApi to record.
|
||||
return this.pathHelp.getNewModel('pki/issuer', this.secretMountPath.currentPath);
|
||||
}
|
||||
|
||||
model() {
|
||||
const { issuer_ref } = this.paramsFor('issuers/issuer');
|
||||
return this.store.queryRecord('pki/issuer', {
|
||||
backend: this.secretMountPath.currentPath,
|
||||
id: issuer_ref,
|
||||
});
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
controller.breadcrumbs = [
|
||||
{ label: 'secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: this.secretMountPath.currentPath, route: 'overview' },
|
||||
{ label: 'issuers', route: 'issuers.index' },
|
||||
{ label: resolvedModel.id, route: 'issuers.issuer.details' },
|
||||
{ label: 'update' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
@withConfirmLeave()
|
||||
export default class PkiIssuerSignRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import PkiKeysIndexRoute from '.';
|
||||
import PkiKeyRoute from '../key';
|
||||
|
||||
export default class PkiKeyDetailsRoute extends PkiKeysIndexRoute {
|
||||
export default class PkiKeyDetailsRoute extends PkiKeyRoute {
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
controller.breadcrumbs.push({ label: resolvedModel.id });
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import PkiKeysIndexRoute from '.';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
import PkiKeyRoute from '../key';
|
||||
|
||||
export default class PkiKeyEditRoute extends PkiKeysIndexRoute {
|
||||
@withConfirmLeave()
|
||||
export default class PkiKeyEditRoute extends PkiKeyRoute {
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
controller.breadcrumbs.push({ label: resolvedModel.id, route: 'keys.key.details' }, { label: 'edit' });
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
withConfirmLeave();
|
||||
export default class PkiRoleGenerateRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { withConfirmLeave } from 'core/decorators/confirm-leave';
|
||||
|
||||
withConfirmLeave();
|
||||
export default class PkiRoleSignRoute extends Route {
|
||||
@service store;
|
||||
@service secretMountPath;
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
<PkiConfigureForm @config={{this.model.config}} @urls={{this.model.urls}} />
|
||||
<PkiConfigureForm
|
||||
@config={{this.model.config}}
|
||||
@urls={{this.model.urls}}
|
||||
@onCancel={{transition-to "vault.cluster.secrets.backend.pki.overview"}}
|
||||
/>
|
||||
@@ -10,11 +10,15 @@
|
||||
/>
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink @route="issuers.import">
|
||||
<ToolbarLink @route="issuers.import" data-test-generate-issuer="import">
|
||||
Import
|
||||
</ToolbarLink>
|
||||
<BasicDropdown @class="popup-menu" @horizontalPosition="auto-right" @verticalPosition="below" as |D|>
|
||||
<D.Trigger class={{concat "toolbar-link" (if D.isOpen " is-active")}} @htmlTag="button">
|
||||
<D.Trigger
|
||||
class={{concat "toolbar-link" (if D.isOpen " is-active")}}
|
||||
@htmlTag="button"
|
||||
data-test-issuer-generate-dropdown
|
||||
>
|
||||
Generate
|
||||
<Chevron @direction="down" @isButton={{true}} />
|
||||
</D.Trigger>
|
||||
@@ -22,12 +26,16 @@
|
||||
<nav class="box menu" aria-label="generate options">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<LinkTo @route="issuers.generate-root" {{on "click" (fn this.onLinkClick D)}}>
|
||||
<LinkTo @route="issuers.generate-root" {{on "click" (fn this.onLinkClick D)}} data-test-generate-issuer="root">
|
||||
Root
|
||||
</LinkTo>
|
||||
</li>
|
||||
<li class="action">
|
||||
<LinkTo @route="issuers.generate-intermediate" {{on "click" (fn this.onLinkClick D)}}>
|
||||
<LinkTo
|
||||
@route="issuers.generate-intermediate"
|
||||
{{on "click" (fn this.onLinkClick D)}}
|
||||
data-test-generate-issuer="intermediate"
|
||||
>
|
||||
Intermediate CSR
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
||||
398
ui/tests/acceptance/pki-engine-route-cleanup-test.js-test.js
Normal file
398
ui/tests/acceptance/pki-engine-route-cleanup-test.js-test.js
Normal file
@@ -0,0 +1,398 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
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 { click, currentURL, fillIn, visit } from '@ember/test-helpers';
|
||||
import { runCommands } from 'vault/tests/helpers/pki/pki-run-commands';
|
||||
import { SELECTORS } from '../helpers/pki/workflow';
|
||||
|
||||
/**
|
||||
* This test module should test that dirty route models are cleaned up when the user leaves the page
|
||||
*/
|
||||
module('Acceptance | pki engine route cleanup test', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
await authPage.login();
|
||||
// Setup PKI engine
|
||||
const mountPath = `pki-workflow-${new Date().getTime()}`;
|
||||
await enablePage.enable('pki', mountPath);
|
||||
this.mountPath = mountPath;
|
||||
await logout.visit();
|
||||
});
|
||||
|
||||
hooks.afterEach(async function () {
|
||||
await logout.visit();
|
||||
await authPage.login();
|
||||
// Cleanup engine
|
||||
await runCommands([`delete sys/mounts/${this.mountPath}`]);
|
||||
await logout.visit();
|
||||
});
|
||||
|
||||
module('configuration', function () {
|
||||
test('create config', async function (assert) {
|
||||
let configs, urls, config;
|
||||
await authPage.login(this.pkiAdminToken);
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.emptyStateLink);
|
||||
configs = this.store.peekAll('pki/action');
|
||||
urls = this.store.peekRecord('pki/urls', this.mountPath);
|
||||
config = configs.objectAt(0);
|
||||
assert.strictEqual(configs.length, 1, 'One config model present');
|
||||
assert.false(urls.hasDirtyAttributes, 'URLs is loaded from endpoint');
|
||||
assert.true(config.hasDirtyAttributes, 'Config model is dirty');
|
||||
|
||||
// Cancel button rolls it back
|
||||
await click(SELECTORS.configuration.cancelButton);
|
||||
configs = this.store.peekAll('pki/action');
|
||||
urls = this.store.peekRecord('pki/urls', this.mountPath);
|
||||
assert.strictEqual(configs.length, 0, 'config model is rolled back on cancel');
|
||||
assert.strictEqual(urls.id, this.mountPath, 'Urls still exists on exit');
|
||||
|
||||
await click(SELECTORS.emptyStateLink);
|
||||
configs = this.store.peekAll('pki/action');
|
||||
urls = this.store.peekRecord('pki/urls', this.mountPath);
|
||||
config = configs.objectAt(0);
|
||||
assert.strictEqual(configs.length, 1, 'One config model present');
|
||||
assert.false(urls.hasDirtyAttributes, 'URLs is loaded from endpoint');
|
||||
assert.true(config.hasDirtyAttributes, 'Config model is dirty');
|
||||
|
||||
// Exit page via link rolls it back
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
configs = this.store.peekAll('pki/action');
|
||||
urls = this.store.peekRecord('pki/urls', this.mountPath);
|
||||
assert.strictEqual(configs.length, 0, 'config model is rolled back on cancel');
|
||||
assert.strictEqual(urls.id, this.mountPath, 'Urls still exists on exit');
|
||||
});
|
||||
});
|
||||
|
||||
module('role routes', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
await authPage.login();
|
||||
// Configure PKI
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.emptyStateLink);
|
||||
await click(SELECTORS.configuration.optionByKey('generate-root'));
|
||||
await fillIn(SELECTORS.configuration.typeField, 'internal');
|
||||
await fillIn(SELECTORS.configuration.inputByName('commonName'), 'my-root-cert');
|
||||
await click(SELECTORS.configuration.generateRootSave);
|
||||
await logout.visit();
|
||||
});
|
||||
|
||||
test('create role exit via cancel', async function (assert) {
|
||||
let roles;
|
||||
await authPage.login();
|
||||
// Create PKI
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.rolesTab);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
assert.strictEqual(roles.length, 0, 'No roles exist yet');
|
||||
await click(SELECTORS.createRoleLink);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
const role = roles.objectAt(0);
|
||||
assert.strictEqual(roles.length, 1, 'New role exists');
|
||||
assert.true(role.isNew, 'Role is new model');
|
||||
await click(SELECTORS.roleForm.roleCancelButton);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
assert.strictEqual(roles.length, 0, 'Role is removed from store');
|
||||
});
|
||||
test('create role exit via breadcrumb', async function (assert) {
|
||||
let roles;
|
||||
await authPage.login();
|
||||
// Create PKI
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.rolesTab);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
assert.strictEqual(roles.length, 0, 'No roles exist yet');
|
||||
await click(SELECTORS.createRoleLink);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
const role = roles.objectAt(0);
|
||||
assert.strictEqual(roles.length, 1, 'New role exists');
|
||||
assert.true(role.isNew, 'Role is new model');
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
assert.strictEqual(roles.length, 0, 'Role is removed from store');
|
||||
});
|
||||
test('edit role', async function (assert) {
|
||||
let roles, role;
|
||||
const roleId = 'workflow-edit-role';
|
||||
await authPage.login();
|
||||
// Create PKI
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.rolesTab);
|
||||
roles = this.store.peekAll('pki/role');
|
||||
assert.strictEqual(roles.length, 0, 'No roles exist yet');
|
||||
await click(SELECTORS.createRoleLink);
|
||||
await fillIn(SELECTORS.roleForm.roleName, roleId);
|
||||
await click(SELECTORS.roleForm.roleCreateButton);
|
||||
assert.dom('[data-test-value-div="Role name"]').hasText(roleId, 'Shows correct role after create');
|
||||
roles = this.store.peekAll('pki/role');
|
||||
role = roles.objectAt(0);
|
||||
assert.strictEqual(roles.length, 1, 'Role is created');
|
||||
assert.false(role.hasDirtyAttributes, 'Role no longer has dirty attributes');
|
||||
|
||||
// Edit role
|
||||
await click(SELECTORS.editRoleLink);
|
||||
await fillIn(SELECTORS.roleForm.issuerRef, 'foobar');
|
||||
role = this.store.peekRecord('pki/role', roleId);
|
||||
assert.true(role.hasDirtyAttributes, 'Role has dirty attrs');
|
||||
// Exit page via cancel button
|
||||
await click(SELECTORS.roleForm.roleCancelButton);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/roles/${roleId}/details`);
|
||||
role = this.store.peekRecord('pki/role', roleId);
|
||||
assert.false(role.hasDirtyAttributes, 'Role dirty attrs have been rolled back');
|
||||
|
||||
// Edit again
|
||||
await click(SELECTORS.editRoleLink);
|
||||
await fillIn(SELECTORS.roleForm.issuerRef, 'foobar2');
|
||||
role = this.store.peekRecord('pki/role', roleId);
|
||||
assert.true(role.hasDirtyAttributes, 'Role has dirty attrs');
|
||||
// Exit page via breadcrumbs
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
role = this.store.peekRecord('pki/role', roleId);
|
||||
assert.false(role.hasDirtyAttributes, 'Role dirty attrs have been rolled back');
|
||||
});
|
||||
});
|
||||
|
||||
module('issuer routes', function () {
|
||||
test('import issuer exit via cancel', async function (assert) {
|
||||
let issuers;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 0, 'No issuers exist yet');
|
||||
await click(SELECTORS.importIssuerLink);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer model created');
|
||||
const issuer = issuers.objectAt(0);
|
||||
assert.true(issuer.hasDirtyAttributes, 'Issuer has dirty attrs');
|
||||
assert.true(issuer.isNew, 'Issuer is new');
|
||||
// Exit
|
||||
await click('[data-test-pki-ca-cert-cancel]');
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/issuers`);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 0, 'Issuer is removed from store');
|
||||
});
|
||||
test('import issuer exit via breadcrumb', async function (assert) {
|
||||
let issuers;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 0, 'No issuers exist yet');
|
||||
await click(SELECTORS.importIssuerLink);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer model created');
|
||||
const issuer = issuers.objectAt(0);
|
||||
assert.true(issuer.hasDirtyAttributes, 'Issuer has dirty attrs');
|
||||
assert.true(issuer.isNew, 'Issuer is new');
|
||||
// Exit
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 0, 'Issuer is removed from store');
|
||||
});
|
||||
test('generate root exit via cancel', async function (assert) {
|
||||
let actions;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'No actions exist yet');
|
||||
await click(SELECTORS.generateIssuerDropdown);
|
||||
await click(SELECTORS.generateIssuerRoot);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 1, 'Action model for generate-root created');
|
||||
const action = actions.objectAt(0);
|
||||
assert.true(action.hasDirtyAttributes, 'Action has dirty attrs');
|
||||
assert.true(action.isNew, 'Action is new');
|
||||
assert.strictEqual(action.actionType, 'generate-root', 'Action type is correct');
|
||||
// Exit
|
||||
await click(SELECTORS.configuration.generateRootCancel);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/issuers`);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'Action is removed from store');
|
||||
});
|
||||
test('generate root exit via breadcrumb', async function (assert) {
|
||||
let actions;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'No actions exist yet');
|
||||
await click(SELECTORS.generateIssuerDropdown);
|
||||
await click(SELECTORS.generateIssuerRoot);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 1, 'Action model for generate-root created');
|
||||
const action = actions.objectAt(0);
|
||||
assert.true(action.hasDirtyAttributes, 'Action has dirty attrs');
|
||||
assert.true(action.isNew, 'Action is new');
|
||||
assert.strictEqual(action.actionType, 'generate-root');
|
||||
// Exit
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'Action is removed from store');
|
||||
});
|
||||
test('generate intermediate csr exit via cancel', async function (assert) {
|
||||
let actions;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'No actions exist yet');
|
||||
await await click(SELECTORS.generateIssuerDropdown);
|
||||
await click(SELECTORS.generateIssuerIntermediate);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 1, 'Action model for generate-csr created');
|
||||
const action = actions.objectAt(0);
|
||||
assert.true(action.hasDirtyAttributes, 'Action has dirty attrs');
|
||||
assert.true(action.isNew, 'Action is new');
|
||||
assert.strictEqual(action.actionType, 'generate-csr');
|
||||
// Exit
|
||||
await click('[data-test-cancel]');
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/issuers`);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'Action is removed from store');
|
||||
});
|
||||
test('generate intermediate csr exit via breadcrumb', async function (assert) {
|
||||
let actions;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.issuersTab);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'No actions exist yet');
|
||||
await click(SELECTORS.generateIssuerDropdown);
|
||||
await click(SELECTORS.generateIssuerIntermediate);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 1, 'Action model for generate-csr created');
|
||||
const action = actions.objectAt(0);
|
||||
assert.true(action.hasDirtyAttributes, 'Action has dirty attrs');
|
||||
assert.true(action.isNew, 'Action is new');
|
||||
assert.strictEqual(action.actionType, 'generate-csr');
|
||||
// Exit
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
actions = this.store.peekAll('pki/action');
|
||||
assert.strictEqual(actions.length, 0, 'Action is removed from store');
|
||||
});
|
||||
test('edit issuer exit', async function (assert) {
|
||||
let issuers, issuer;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.emptyStateLink);
|
||||
await click(SELECTORS.configuration.optionByKey('generate-root'));
|
||||
await fillIn(SELECTORS.configuration.typeField, 'internal');
|
||||
await fillIn(SELECTORS.configuration.inputByName('commonName'), 'my-root-cert');
|
||||
await click(SELECTORS.configuration.generateRootSave);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
const issuerId = issuers.objectAt(0).id;
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model');
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/vault/secrets/${this.mountPath}/pki/issuers/${issuerId}/details`,
|
||||
'url is correct'
|
||||
);
|
||||
await click(SELECTORS.issuerDetails.configure);
|
||||
issuer = this.store.peekRecord('pki/issuer', issuerId);
|
||||
assert.false(issuer.hasDirtyAttributes, 'Model not dirty');
|
||||
await fillIn('[data-test-input="issuerName"]', 'foobar');
|
||||
assert.true(issuer.hasDirtyAttributes, 'Model is dirty');
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
issuers = this.store.peekAll('pki/issuer');
|
||||
assert.strictEqual(issuers.length, 1, 'Issuer exists on model');
|
||||
issuer = this.store.peekRecord('pki/issuer', issuerId);
|
||||
assert.false(issuer.hasDirtyAttributes, 'Dirty attrs were rolled back');
|
||||
});
|
||||
});
|
||||
|
||||
module('key routes', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
await authPage.login();
|
||||
// Configure PKI -- key creation not allowed unless configured
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.emptyStateLink);
|
||||
await click(SELECTORS.configuration.optionByKey('generate-root'));
|
||||
await fillIn(SELECTORS.configuration.typeField, 'internal');
|
||||
await fillIn(SELECTORS.configuration.inputByName('commonName'), 'my-root-cert');
|
||||
await click(SELECTORS.configuration.generateRootSave);
|
||||
await logout.visit();
|
||||
});
|
||||
test('create key exit', async function (assert) {
|
||||
let keys, key;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.keysTab);
|
||||
keys = this.store.peekAll('pki/key');
|
||||
const configKeyId = keys.objectAt(0).id;
|
||||
assert.strictEqual(keys.length, 1, 'One key exists from config');
|
||||
// Create key
|
||||
await click(SELECTORS.keyPages.generateKey);
|
||||
keys = this.store.peekAll('pki/key');
|
||||
key = keys.objectAt(1);
|
||||
assert.strictEqual(keys.length, 2, 'New key exists');
|
||||
assert.true(key.isNew, 'Role is new model');
|
||||
// Exit
|
||||
await click(SELECTORS.keyForm.keyCancelButton);
|
||||
keys = this.store.peekAll('pki/key');
|
||||
assert.strictEqual(keys.length, 1, 'Second key is removed from store');
|
||||
assert.strictEqual(keys.objectAt(0).id, configKeyId);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys`, 'url is correct');
|
||||
|
||||
// Create again
|
||||
await click(SELECTORS.keyPages.generateKey);
|
||||
assert.strictEqual(keys.length, 2, 'New key exists');
|
||||
keys = this.store.peekAll('pki/key');
|
||||
key = keys.objectAt(1);
|
||||
assert.true(key.isNew, 'Key is new model');
|
||||
// Exit
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`, 'url is correct');
|
||||
keys = this.store.peekAll('pki/key');
|
||||
assert.strictEqual(keys.length, 1, 'Key is removed from store');
|
||||
});
|
||||
test('edit key exit', async function (assert) {
|
||||
let keys, key;
|
||||
await authPage.login();
|
||||
await visit(`/vault/secrets/${this.mountPath}/pki/overview`);
|
||||
await click(SELECTORS.keysTab);
|
||||
keys = this.store.peekAll('pki/key');
|
||||
assert.strictEqual(keys.length, 1, 'One key from config exists');
|
||||
assert.dom('.list-item-row').exists({ count: 1 }, 'single row for key');
|
||||
await click('.list-item-row');
|
||||
// Edit
|
||||
await click(SELECTORS.keyPages.keyEditLink);
|
||||
await fillIn(SELECTORS.keyForm.keyNameInput, 'foobar');
|
||||
keys = this.store.peekAll('pki/key');
|
||||
key = keys.objectAt(0);
|
||||
assert.true(key.hasDirtyAttributes, 'Key model is dirty');
|
||||
// Exit
|
||||
await click(SELECTORS.keyForm.keyCancelButton);
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
`/vault/secrets/${this.mountPath}/pki/keys/${key.id}/details`,
|
||||
'url is correct'
|
||||
);
|
||||
keys = this.store.peekAll('pki/key');
|
||||
assert.strictEqual(keys.length, 1, 'Key list has 1');
|
||||
assert.false(key.hasDirtyAttributes, 'Key dirty attrs have been rolled back');
|
||||
|
||||
// Edit again
|
||||
await click(SELECTORS.keyPages.keyEditLink);
|
||||
await fillIn(SELECTORS.keyForm.keyNameInput, 'foobar');
|
||||
keys = this.store.peekAll('pki/key');
|
||||
key = keys.objectAt(0);
|
||||
assert.true(key.hasDirtyAttributes, 'Key model is dirty');
|
||||
|
||||
// Exit via breadcrumb
|
||||
await click(SELECTORS.overviewBreadcrumb);
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/overview`, 'url is correct');
|
||||
keys = this.store.peekAll('pki/key');
|
||||
assert.strictEqual(keys.length, 1, 'Key list has 1');
|
||||
assert.false(key.hasDirtyAttributes, 'Key dirty attrs have been rolled back');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,7 @@ import { SELECTORS as CONFIGURATION } from './pki-configure-form';
|
||||
export const SELECTORS = {
|
||||
breadcrumbContainer: '[data-test-breadcrumbs]',
|
||||
breadcrumbs: '[data-test-breadcrumbs] li',
|
||||
overviewBreadcrumb: '[data-test-breadcrumbs] li:nth-of-type(2) > a',
|
||||
pageTitle: '[data-test-pki-role-page-title]',
|
||||
alertBanner: '[data-test-alert-banner="alert"]',
|
||||
emptyStateTitle: '[data-test-empty-state-title]',
|
||||
@@ -40,6 +41,10 @@ export const SELECTORS = {
|
||||
...KEYPAGES,
|
||||
},
|
||||
// ISSUERS
|
||||
importIssuerLink: '[data-test-generate-issuer="import"]',
|
||||
generateIssuerDropdown: '[data-test-issuer-generate-dropdown]',
|
||||
generateIssuerRoot: '[data-test-generate-issuer="root"]',
|
||||
generateIssuerIntermediate: '[data-test-generate-issuer="intermediate"]',
|
||||
issuerDetails: {
|
||||
title: '[data-test-pki-issuer-page-title]',
|
||||
...ISSUERDETAILS,
|
||||
|
||||
@@ -4,13 +4,19 @@ import { click, render } from '@ember/test-helpers';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki/pki-configure-form';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Integration | Component | pki-configure-form', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'pki');
|
||||
hooks.beforeEach(function () {
|
||||
this.cancelSpy = sinon.spy();
|
||||
});
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<PkiConfigureForm @config={{this.config}} />`, { owner: this.engine });
|
||||
await render(hbs`<PkiConfigureForm @onCancel={{this.cancelSpy}} @config={{this.config}} />`, {
|
||||
owner: this.engine,
|
||||
});
|
||||
|
||||
assert.dom(SELECTORS.option).exists({ count: 3 }, 'Three configuration options are shown');
|
||||
assert.dom(SELECTORS.cancelButton).exists('Cancel link is shown');
|
||||
|
||||
@@ -5,6 +5,7 @@ import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki/pki-key-form';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Integration | Component | pki key form', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@@ -17,6 +18,7 @@ module('Integration | Component | pki key form', function (hooks) {
|
||||
this.backend = 'pki-test';
|
||||
this.secretMountPath = this.owner.lookup('service:secret-mount-path');
|
||||
this.secretMountPath.currentPath = this.backend;
|
||||
this.onCancel = sinon.spy();
|
||||
});
|
||||
|
||||
test('it should render fields and show validation messages', async function (assert) {
|
||||
@@ -120,50 +122,4 @@ module('Integration | Component | pki key form', function (hooks) {
|
||||
await fillIn(SELECTORS.keyTypeInput, 'rsa');
|
||||
await click(SELECTORS.keyCreateButton);
|
||||
});
|
||||
|
||||
test('it should rollback attributes or unload record on cancel', async function (assert) {
|
||||
assert.expect(5);
|
||||
this.onCancel = () => assert.ok(true, 'onCancel callback fires');
|
||||
await render(
|
||||
hbs`
|
||||
<PkiKeyForm
|
||||
@model={{this.model}}
|
||||
@onCancel={{this.onCancel}}
|
||||
@onSave={{this.onSave}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
await click(SELECTORS.keyCancelButton);
|
||||
assert.true(this.model.isDestroyed, 'new model is unloaded on cancel');
|
||||
|
||||
this.store.pushPayload('pki/key', {
|
||||
modelName: 'pki/key',
|
||||
key_name: 'test-key',
|
||||
type: 'exported',
|
||||
key_id: 'some-key-id',
|
||||
key_type: 'rsa',
|
||||
key_bits: '2048',
|
||||
});
|
||||
this.model = this.store.peekRecord('pki/key', 'some-key-id');
|
||||
|
||||
await render(
|
||||
hbs`
|
||||
<PkiKeyForm
|
||||
@model={{this.model}}
|
||||
@onCancel={{this.onCancel}}
|
||||
@onSave={{this.onSave}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki/pki-role-form';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Integration | Component | pki-role-form', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@@ -15,6 +16,7 @@ module('Integration | Component | pki-role-form', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.model = this.store.createRecord('pki/role');
|
||||
this.model.backend = 'pki';
|
||||
this.onCancel = sinon.spy();
|
||||
});
|
||||
|
||||
test('it should render default fields and toggle groups', async function (assert) {
|
||||
@@ -111,26 +113,6 @@ module('Integration | Component | pki-role-form', function (hooks) {
|
||||
await click(SELECTORS.roleCreateButton);
|
||||
});
|
||||
|
||||
test('it should unload model on cancel', async function (assert) {
|
||||
assert.expect(3);
|
||||
this.onCancel = () => assert.ok(true, 'onCancel callback fires');
|
||||
await render(
|
||||
hbs`
|
||||
<PkiRoleForm
|
||||
@model={{this.model}}
|
||||
@onCancel={{this.onCancel}}
|
||||
@onSave={{this.onSave}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
await fillIn(SELECTORS.roleName, 'dont-save-me');
|
||||
await click(SELECTORS.roleCancelButton);
|
||||
assert.notEqual(this.model.roleName, 'dont-save-me');
|
||||
assert.true(this.model.isDestroyed, 'new model is unloaded on cancel');
|
||||
});
|
||||
|
||||
test('it should update attributes on the model on update', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.store.pushPayload('pki/role', {
|
||||
|
||||
Reference in New Issue
Block a user