mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-31 18:48:08 +00:00
Ui/transform templates (#9981)
Add CRUD capabilities on transform templates. Disallow read or edit for built-ins.
This commit is contained in:
@@ -55,12 +55,11 @@ export default ApplicationAdapter.extend({
|
|||||||
|
|
||||||
queryRecord(store, type, query) {
|
queryRecord(store, type, query) {
|
||||||
return this.ajax(this.url(query.backend, type.modelName, query.id), 'GET').then(result => {
|
return this.ajax(this.url(query.backend, type.modelName, query.id), 'GET').then(result => {
|
||||||
|
// CBS TODO: Add name to response and unmap name <> id on models
|
||||||
return {
|
return {
|
||||||
id: query.id,
|
id: query.id,
|
||||||
...result,
|
...result,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// buildUrl(modelName, id, snapshot, requestType, query, returns) {},
|
|
||||||
});
|
});
|
||||||
|
|||||||
32
ui/app/components/transform-list-item.js
Normal file
32
ui/app/components/transform-list-item.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* @module TransformListItem
|
||||||
|
* TransformListItem components are used for the list items for the Transform Secret Engines for all but Transformations.
|
||||||
|
* This component automatically handles read-only list items if capabilities are not granted or the item is internal only.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
* <TransformListItem @item={item} @itemPath="role/my-item" @itemType="role" />
|
||||||
|
* ```
|
||||||
|
* @param {object} item - item refers to the model item used on the list item partial
|
||||||
|
* @param {string} itemPath - usually the id of the item, but can be prefixed with the model type (see transform/role)
|
||||||
|
* @param {string} [itemType] - itemType is used to calculate whether an item is readable or
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
item: null,
|
||||||
|
itemPath: '',
|
||||||
|
itemType: '',
|
||||||
|
|
||||||
|
itemViewable: computed('item', 'itemType', function() {
|
||||||
|
const item = this.get('item');
|
||||||
|
if (this.itemType === 'alphabet' || this.itemType === 'template') {
|
||||||
|
return !item.get('id').startsWith('builtin/');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
|
||||||
|
backendType: 'transform',
|
||||||
|
});
|
||||||
3
ui/app/components/transform-template-edit.js
Normal file
3
ui/app/components/transform-template-edit.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import TransformBase from './transform-edit-base';
|
||||||
|
|
||||||
|
export default TransformBase.extend({});
|
||||||
@@ -80,15 +80,14 @@ const SECRET_BACKENDS = {
|
|||||||
editComponent: 'transform-role-edit',
|
editComponent: 'transform-role-edit',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'templates',
|
name: 'template',
|
||||||
modelPrefix: 'template/',
|
modelPrefix: 'template/',
|
||||||
label: 'Templates',
|
label: 'Templates',
|
||||||
searchPlaceholder: 'Filter templates',
|
searchPlaceholder: 'Filter templates',
|
||||||
item: 'templates',
|
item: 'template',
|
||||||
create: 'Create template',
|
create: 'Create template',
|
||||||
tab: 'templates',
|
tab: 'template',
|
||||||
editComponent: 'transform-template-edit',
|
editComponent: 'transform-template-edit',
|
||||||
hideCreate: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'alphabets',
|
name: 'alphabets',
|
||||||
|
|||||||
@@ -1,7 +1,52 @@
|
|||||||
|
import { computed } from '@ember/object';
|
||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
|
import { apiPath } from 'vault/macros/lazy-capabilities';
|
||||||
|
import attachCapabilities from 'vault/lib/attach-capabilities';
|
||||||
|
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||||
|
|
||||||
export default DS.Model.extend({
|
const { attr } = DS;
|
||||||
name: DS.attr('string'),
|
|
||||||
alphabet: DS.belongsTo('transform/alphabet'),
|
const Model = DS.Model.extend({
|
||||||
transformations: DS.hasMany('transformation'),
|
idPrefix: 'template/',
|
||||||
|
idForNav: computed('id', 'idPrefix', function() {
|
||||||
|
let modelId = this.id || '';
|
||||||
|
return `${this.idPrefix}${modelId}`;
|
||||||
|
}),
|
||||||
|
|
||||||
|
name: attr('string', {
|
||||||
|
label: 'Name',
|
||||||
|
fieldValue: 'id',
|
||||||
|
readOnly: true,
|
||||||
|
subText:
|
||||||
|
'Templates allow Vault to determine what and how to capture the value to be transformed. This cannot be edited later.',
|
||||||
|
}),
|
||||||
|
type: attr('string', { defaultValue: 'regex' }),
|
||||||
|
pattern: attr('string', {
|
||||||
|
subText: 'The template’s pattern defines the data format. Expressed in regex.',
|
||||||
|
}),
|
||||||
|
alphabet: attr('array', {
|
||||||
|
subText:
|
||||||
|
'Alphabet defines a set of characters (UTF-8) that is used for FPE to determine the validity of plaintext and ciphertext values. You can choose a built-in one, or create your own.',
|
||||||
|
editType: 'searchSelect',
|
||||||
|
fallbackComponent: 'string-list',
|
||||||
|
label: 'Alphabet',
|
||||||
|
models: ['transform/alphabet'],
|
||||||
|
selectLimit: 1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
attrs: computed('pattern', 'alphabet', function() {
|
||||||
|
let keys = ['name', 'pattern', 'alphabet'];
|
||||||
|
return expandAttributeMeta(this, keys);
|
||||||
|
}),
|
||||||
|
|
||||||
|
editableAttrs: computed('pattern', 'alphabet', function() {
|
||||||
|
let keys = ['pattern', 'alphabet'];
|
||||||
|
return expandAttributeMeta(this, keys);
|
||||||
|
}),
|
||||||
|
|
||||||
|
backend: attr('string', { readOnly: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default attachCapabilities(Model, {
|
||||||
|
updatePath: apiPath`${'backend'}/template/${'id'}`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default Route.extend({
|
|||||||
case 'role':
|
case 'role':
|
||||||
modelType = 'transform/role';
|
modelType = 'transform/role';
|
||||||
break;
|
break;
|
||||||
case 'templates':
|
case 'template':
|
||||||
modelType = 'transform/template';
|
modelType = 'transform/template';
|
||||||
break;
|
break;
|
||||||
case 'alphabets':
|
case 'alphabets':
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import ApplicationSerializer from '../application';
|
import ApplicationSerializer from '../application';
|
||||||
|
|
||||||
export default ApplicationSerializer.extend({
|
export default ApplicationSerializer.extend({
|
||||||
extractLazyPaginatedData(payload) {
|
extractLazyPaginatedData(payload) {
|
||||||
let ret;
|
let ret;
|
||||||
|
|||||||
34
ui/app/serializers/transform/template.js
Normal file
34
ui/app/serializers/transform/template.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import ApplicationSerializer from '../application';
|
||||||
|
|
||||||
|
export default ApplicationSerializer.extend({
|
||||||
|
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||||
|
payload.data.name = payload.id;
|
||||||
|
if (payload.data.alphabet) {
|
||||||
|
payload.data.alphabet = [payload.data.alphabet];
|
||||||
|
}
|
||||||
|
return this._super(store, primaryModelClass, payload, id, requestType);
|
||||||
|
},
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
let json = this._super(...arguments);
|
||||||
|
if (json.alphabet && Array.isArray(json.alphabet)) {
|
||||||
|
// Templates should only ever have one alphabet
|
||||||
|
json.alphabet = json.alphabet[0];
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
|
||||||
|
extractLazyPaginatedData(payload) {
|
||||||
|
let ret;
|
||||||
|
ret = payload.data.keys.map(key => {
|
||||||
|
let model = {
|
||||||
|
id: key,
|
||||||
|
};
|
||||||
|
if (payload.backend) {
|
||||||
|
model.backend = payload.backend;
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
});
|
||||||
66
ui/app/templates/components/transform-list-item.hbs
Normal file
66
ui/app/templates/components/transform-list-item.hbs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{{#if (and itemViewable item.updatePath.canRead)}}
|
||||||
|
{{#linked-block
|
||||||
|
"vault.cluster.secrets.backend.show"
|
||||||
|
itemPath
|
||||||
|
class="list-item-row"
|
||||||
|
data-test-secret-link=itemPath
|
||||||
|
encode=true
|
||||||
|
queryParams=(secret-query-params backendType)
|
||||||
|
}}
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-10">
|
||||||
|
<SecretLink
|
||||||
|
@mode="show"
|
||||||
|
@secret={{item.id}}
|
||||||
|
@queryParams={{query-params type=modelType}}
|
||||||
|
@class="has-text-black has-text-weight-semibold">
|
||||||
|
<Icon
|
||||||
|
@glyph='file-outline'
|
||||||
|
@class="has-text-grey-light"/>
|
||||||
|
{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
|
||||||
|
</SecretLink>
|
||||||
|
</div>
|
||||||
|
<div class="column has-text-right">
|
||||||
|
{{#if (or item.updatePath.canRead item.updatePath.canUpdate)}}
|
||||||
|
<PopupMenu name="secret-menu">
|
||||||
|
<nav class="menu">
|
||||||
|
<ul class="menu-list">
|
||||||
|
{{#if item.updatePath.canRead}}
|
||||||
|
<li class="action">
|
||||||
|
<SecretLink
|
||||||
|
@mode="show"
|
||||||
|
@secret={{itemPath}}
|
||||||
|
@class="has-text-black has-text-weight-semibold">
|
||||||
|
Details
|
||||||
|
</SecretLink>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if item.updatePath.canUpdate}}
|
||||||
|
<li class="action">
|
||||||
|
<SecretLink
|
||||||
|
@mode="edit"
|
||||||
|
@secret={{itemPath}}
|
||||||
|
@class="has-text-black has-text-weight-semibold">
|
||||||
|
Edit
|
||||||
|
</SecretLink>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</PopupMenu>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/linked-block}}
|
||||||
|
{{else}}
|
||||||
|
<div class="list-item-row" data-test-view-only-list-item>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-12 has-text-grey has-text-weight-semibold">
|
||||||
|
<Icon
|
||||||
|
@glyph='file-outline'
|
||||||
|
@class="has-text-grey-light"/>
|
||||||
|
{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
123
ui/app/templates/components/transform-template-edit.hbs
Normal file
123
ui/app/templates/components/transform-template-edit.hbs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<PageHeader as |p|>
|
||||||
|
<p.top>
|
||||||
|
{{key-value-header
|
||||||
|
baseKey=(hash display=model.id id=model.idForNav)
|
||||||
|
path="vault.cluster.secrets.backend.list"
|
||||||
|
mode=mode
|
||||||
|
root=root
|
||||||
|
showCurrent=true
|
||||||
|
}}
|
||||||
|
</p.top>
|
||||||
|
<p.levelLeft>
|
||||||
|
<h1 class="title is-3" data-test-secret-header="true">
|
||||||
|
{{#if (eq mode "create") }}
|
||||||
|
Create Template
|
||||||
|
{{else if (eq mode "edit")}}
|
||||||
|
Edit Template
|
||||||
|
{{else}}
|
||||||
|
Template <code>{{model.id}}</code>
|
||||||
|
{{/if}}
|
||||||
|
</h1>
|
||||||
|
</p.levelLeft>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
|
{{#if (eq mode "show")}}
|
||||||
|
<Toolbar>
|
||||||
|
<ToolbarActions>
|
||||||
|
{{#if capabilities.canDelete}}
|
||||||
|
<button
|
||||||
|
class="toolbar-link"
|
||||||
|
onclick={{action "delete"}}
|
||||||
|
data-test-transformation-template-delete
|
||||||
|
>
|
||||||
|
Delete template
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
{{#if capabilities.canUpdate }}
|
||||||
|
<ToolbarSecretLink
|
||||||
|
@secret={{concat model.idPrefix model.id}}
|
||||||
|
@mode="edit"
|
||||||
|
@data-test-edit-link=true
|
||||||
|
@replace=true
|
||||||
|
>
|
||||||
|
Edit template
|
||||||
|
</ToolbarSecretLink>
|
||||||
|
{{/if}}
|
||||||
|
</ToolbarActions>
|
||||||
|
</Toolbar>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (or (eq mode 'edit') (eq mode 'create'))}}
|
||||||
|
<form onsubmit={{action "createOrUpdate" mode}}>
|
||||||
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
|
{{message-error model=model}}
|
||||||
|
<NamespaceReminder @mode={{mode}} @noun="Transform role" />
|
||||||
|
{{#each model.attrs as |attr|}}
|
||||||
|
{{#if (and (eq attr.name 'name') (eq mode 'edit')) }}
|
||||||
|
<label for="{{attr.name}}" class="is-label">
|
||||||
|
{{attr.options.label}}
|
||||||
|
</label>
|
||||||
|
{{#if attr.options.subText}}
|
||||||
|
<p class="sub-text">{{attr.options.subText}}</p>
|
||||||
|
{{/if}}
|
||||||
|
<input
|
||||||
|
data-test-input={{attr.name}}
|
||||||
|
id={{attr.name}}
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
value={{or (get model attr.name) attr.options.defaultValue}}
|
||||||
|
readonly
|
||||||
|
class="field input is-readOnly"
|
||||||
|
type={{attr.type}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<FormField
|
||||||
|
data-test-field
|
||||||
|
@attr={{attr}}
|
||||||
|
@model={{model}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={{buttonDisabled}}
|
||||||
|
class="button is-primary"
|
||||||
|
data-test-role-transform-create=true
|
||||||
|
>
|
||||||
|
{{#if (eq mode 'create')}}
|
||||||
|
Create role
|
||||||
|
{{else if (eq mode 'edit')}}
|
||||||
|
Save
|
||||||
|
{{/if}}
|
||||||
|
</button>
|
||||||
|
{{#secret-link
|
||||||
|
mode=(if (eq mode "create") "list" "show")
|
||||||
|
class="button"
|
||||||
|
secret=(concat model.idPrefix model.id)
|
||||||
|
}}
|
||||||
|
Cancel
|
||||||
|
{{/secret-link}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{else}}
|
||||||
|
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||||
|
{{#each model.attrs as |attr|}}
|
||||||
|
{{#if (eq attr.type "object")}}
|
||||||
|
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(stringify (get model attr.name))}}
|
||||||
|
{{else if (eq attr.type "array")}}
|
||||||
|
{{info-table-row
|
||||||
|
label=(capitalize (or attr.options.label (humanize (dasherize attr.name))))
|
||||||
|
value=(get model attr.name)
|
||||||
|
type=attr.type
|
||||||
|
viewAll=attr.name
|
||||||
|
}}
|
||||||
|
{{else}}
|
||||||
|
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name)}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
@@ -1,77 +1,5 @@
|
|||||||
{{!-- TODO do not let click if !canRead --}}
|
<TransformListItem
|
||||||
{{#if (eq options.item "role")}}
|
@item={{item}}
|
||||||
{{#let (concat options.modelPrefix item.id) as |itemPath|}}
|
@itemPath={{concat options.modelPrefix item.id}}
|
||||||
{{#linked-block
|
@itemType={{options.item}}
|
||||||
"vault.cluster.secrets.backend.show"
|
/>
|
||||||
itemPath
|
|
||||||
class="list-item-row"
|
|
||||||
data-test-secret-link=itemPath
|
|
||||||
encode=true
|
|
||||||
queryParams=(secret-query-params backendModel.type)
|
|
||||||
}}
|
|
||||||
<div class="columns is-mobile">
|
|
||||||
<div class="column is-10">
|
|
||||||
<SecretLink
|
|
||||||
@mode="show"
|
|
||||||
@secret={{item.id}}
|
|
||||||
@queryParams={{if (eq backendModel.type "transform") (query-params tab="actions") ""}}
|
|
||||||
@class="has-text-black has-text-weight-semibold">
|
|
||||||
<Icon
|
|
||||||
@glyph='file-outline'
|
|
||||||
@class="has-text-grey-light"/>
|
|
||||||
{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
|
|
||||||
</SecretLink>
|
|
||||||
</div>
|
|
||||||
<div class="column has-text-right">
|
|
||||||
{{#if (or item.updatePath.canRead item.updatePath.canUpdate)}}
|
|
||||||
<PopupMenu name="secret-menu">
|
|
||||||
<nav class="menu">
|
|
||||||
<ul class="menu-list">
|
|
||||||
{{#if (or item.versionPath.isLoading item.secretPath.isLoading)}}
|
|
||||||
<li class="action">
|
|
||||||
<button disabled type="button" class="link button is-loading is-transparent">
|
|
||||||
loading
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
{{else}}
|
|
||||||
{{#if item.updatePath.canRead}}
|
|
||||||
<li class="action">
|
|
||||||
<SecretLink
|
|
||||||
@mode="show"
|
|
||||||
@secret={{itemPath}}
|
|
||||||
@class="has-text-black has-text-weight-semibold">
|
|
||||||
Details
|
|
||||||
</SecretLink>
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
{{#if item.updatePath.canUpdate}}
|
|
||||||
<li class="action">
|
|
||||||
<SecretLink
|
|
||||||
@mode="edit"
|
|
||||||
@secret={{itemPath}}
|
|
||||||
@class="has-text-black has-text-weight-semibold">
|
|
||||||
Edit
|
|
||||||
</SecretLink>
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</PopupMenu>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/linked-block}}
|
|
||||||
{{/let}}
|
|
||||||
{{else}}
|
|
||||||
<div class="list-item-row">
|
|
||||||
<div class="columns is-mobile">
|
|
||||||
<div class="column is-12 has-text-grey has-text-weight-semibold">
|
|
||||||
<Icon
|
|
||||||
@glyph='file-outline'
|
|
||||||
@class="has-text-grey-light"/>
|
|
||||||
{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|||||||
121
ui/tests/integration/components/transform-list-item-test.js
Normal file
121
ui/tests/integration/components/transform-list-item-test.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import EmberObject from '@ember/object';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
|
import { render, findAll, click } from '@ember/test-helpers';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
module('Integration | Component | transform-list-item', function(hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
test('it renders un-clickable item if no read capability', async function(assert) {
|
||||||
|
let item = EmberObject.create({
|
||||||
|
id: 'foo',
|
||||||
|
updatePath: {
|
||||||
|
canRead: false,
|
||||||
|
canDelete: true,
|
||||||
|
canUpdate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.set('itemPath', 'role/foo');
|
||||||
|
this.set('itemType', 'role');
|
||||||
|
this.set('item', item);
|
||||||
|
await render(hbs`<TransformListItem
|
||||||
|
@item={{item}}
|
||||||
|
@itemPath={{itemPath}}
|
||||||
|
@itemType={{itemType}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-view-only-list-item]').exists('shows view only list item');
|
||||||
|
assert.dom('[data-test-view-only-list-item]').hasText(item.id, 'has correct label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it is clickable with details menu item if read capability', async function(assert) {
|
||||||
|
let item = EmberObject.create({
|
||||||
|
id: 'foo',
|
||||||
|
updatePath: {
|
||||||
|
canRead: true,
|
||||||
|
canDelete: false,
|
||||||
|
canUpdate: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.set('itemPath', 'template/foo');
|
||||||
|
this.set('itemType', 'template');
|
||||||
|
this.set('item', item);
|
||||||
|
await render(hbs`<TransformListItem
|
||||||
|
@item={{item}}
|
||||||
|
@itemPath={{itemPath}}
|
||||||
|
@itemType={{itemType}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-secret-link="template/foo"]').exists('shows clickable list item');
|
||||||
|
await click('button.popup-menu-trigger');
|
||||||
|
assert.equal(findAll('.popup-menu-content li').length, 1, 'has one option');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it has details and edit menu item if read & edit capabilities', async function(assert) {
|
||||||
|
let item = EmberObject.create({
|
||||||
|
id: 'foo',
|
||||||
|
updatePath: {
|
||||||
|
canRead: true,
|
||||||
|
canDelete: true,
|
||||||
|
canUpdate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.set('itemPath', 'alphabet/foo');
|
||||||
|
this.set('itemType', 'alphabet');
|
||||||
|
this.set('item', item);
|
||||||
|
await render(hbs`<TransformListItem
|
||||||
|
@item={{item}}
|
||||||
|
@itemPath={{itemPath}}
|
||||||
|
@itemType={{itemType}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-secret-link="alphabet/foo"]').exists('shows clickable list item');
|
||||||
|
await click('button.popup-menu-trigger');
|
||||||
|
assert.equal(findAll('.popup-menu-content li').length, 2, 'has both options');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it is not clickable if built-in template with all capabilities', async function(assert) {
|
||||||
|
let item = EmberObject.create({
|
||||||
|
id: 'builtin/foo',
|
||||||
|
updatePath: {
|
||||||
|
canRead: true,
|
||||||
|
canDelete: true,
|
||||||
|
canUpdate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.set('itemPath', 'template/builtin/foo');
|
||||||
|
this.set('itemType', 'template');
|
||||||
|
this.set('item', item);
|
||||||
|
await render(hbs`<TransformListItem
|
||||||
|
@item={{item}}
|
||||||
|
@itemPath={{itemPath}}
|
||||||
|
@itemType={{itemType}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-view-only-list-item]').exists('shows view only list item');
|
||||||
|
assert.dom('[data-test-view-only-list-item]').hasText(item.id, 'has correct label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it is not clickable if built-in alphabet', async function(assert) {
|
||||||
|
let item = EmberObject.create({
|
||||||
|
id: 'builtin/foo',
|
||||||
|
updatePath: {
|
||||||
|
canRead: true,
|
||||||
|
canDelete: true,
|
||||||
|
canUpdate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.set('itemPath', 'alphabet/builtin/foo');
|
||||||
|
this.set('itemType', 'alphabet');
|
||||||
|
this.set('item', item);
|
||||||
|
await render(hbs`<TransformListItem
|
||||||
|
@item={{item}}
|
||||||
|
@itemPath={{itemPath}}
|
||||||
|
@itemType={{itemType}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-view-only-list-item]').exists('shows view only list item');
|
||||||
|
assert.dom('[data-test-view-only-list-item]').hasText(item.id, 'has correct label');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { module, skip } from 'qunit';
|
||||||
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
|
import { render } from '@ember/test-helpers';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
module('Integration | Component | transform-template-edit', function(hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
skip('it renders', async function(assert) {
|
||||||
|
// Set any properties with this.set('myProperty', 'value');
|
||||||
|
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||||
|
|
||||||
|
await render(hbs`{{transform-template-edit}}`);
|
||||||
|
|
||||||
|
assert.equal(this.element.textContent.trim(), '');
|
||||||
|
|
||||||
|
// Template block usage:
|
||||||
|
await render(hbs`
|
||||||
|
{{#transform-template-edit}}
|
||||||
|
template block text
|
||||||
|
{{/transform-template-edit}}
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.equal(this.element.textContent.trim(), 'template block text');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user