UI: OIDC config cleanup (#17105)
* cleanup infotableitemarray, add render name option to component * wait until items fetched before rendering child component * update test * finish tests for info table item array * remove unused capability checks * remove unnecessary path alias * fix info table row arg * fix wildcards getting info tooltip
This commit is contained in:
parent
ed0a9feb7f
commit
fcf6467cbf
|
@ -29,11 +29,6 @@ export default class OidcAssignmentModel extends Model {
|
|||
|
||||
// CAPABILITIES
|
||||
@lazyCapabilities(apiPath`identity/oidc/assignment/${'name'}`, 'name') assignmentPath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/assignment`) assignmentsPath;
|
||||
|
||||
get canCreate() {
|
||||
return this.assignmentPath.get('canCreate');
|
||||
}
|
||||
get canRead() {
|
||||
return this.assignmentPath.get('canRead');
|
||||
}
|
||||
|
@ -43,17 +38,4 @@ export default class OidcAssignmentModel extends Model {
|
|||
get canDelete() {
|
||||
return this.assignmentPath.get('canDelete');
|
||||
}
|
||||
get canList() {
|
||||
return this.assignmentsPath.get('canList');
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/entity`) entitiesPath;
|
||||
get canListEntities() {
|
||||
return this.entitiesPath.get('canList');
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/group`) groupsPath;
|
||||
get canListGroups() {
|
||||
return this.groupsPath.get('canList');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,45 +73,6 @@ export default class OidcClientModel extends Model {
|
|||
@attr('string', { label: 'Client ID' }) clientId;
|
||||
@attr('string') clientSecret;
|
||||
|
||||
// CAPABILITIES //
|
||||
@lazyCapabilities(apiPath`identity/oidc/client/${'name'}`, 'name') clientPath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/client`) clientsPath;
|
||||
get canCreate() {
|
||||
return this.clientPath.get('canCreate');
|
||||
}
|
||||
get canRead() {
|
||||
return this.clientPath.get('canRead');
|
||||
}
|
||||
get canEdit() {
|
||||
return this.clientPath.get('canUpdate');
|
||||
}
|
||||
get canDelete() {
|
||||
return this.clientPath.get('canDelete');
|
||||
}
|
||||
get canList() {
|
||||
return this.clientsPath.get('canList');
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/oidc/key`) keysPath;
|
||||
get canListKeys() {
|
||||
return this.keysPath.get('canList');
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/oidc/assignment/${'name'}`, 'name') assignmentPath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/assignment`) assignmentsPath;
|
||||
get canCreateAssignments() {
|
||||
return this.assignmentPath.get('canCreate');
|
||||
}
|
||||
get canListAssignments() {
|
||||
return this.assignmentsPath.get('canList');
|
||||
}
|
||||
|
||||
// API WIP
|
||||
@lazyCapabilities(apiPath`identity/oidc/${'name'}/provider`, 'backend', 'name') clientProvidersPath;
|
||||
get canListProviders() {
|
||||
return this.clientProvidersPath.get('canList');
|
||||
}
|
||||
|
||||
// TODO refactor when field-to-attrs util is refactored as decorator
|
||||
_attributeMeta = null; // cache initial result of expandAttributeMeta in getter and return
|
||||
get formFields() {
|
||||
|
@ -131,4 +92,16 @@ export default class OidcClientModel extends Model {
|
|||
}
|
||||
return this._fieldToAttrsGroups;
|
||||
}
|
||||
|
||||
// CAPABILITIES //
|
||||
@lazyCapabilities(apiPath`identity/oidc/client/${'name'}`, 'name') clientPath;
|
||||
get canRead() {
|
||||
return this.clientPath.get('canRead');
|
||||
}
|
||||
get canEdit() {
|
||||
return this.clientPath.get('canUpdate');
|
||||
}
|
||||
get canDelete() {
|
||||
return this.clientPath.get('canDelete');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,6 @@ export default class OidcKeyModel extends Model {
|
|||
|
||||
@lazyCapabilities(apiPath`identity/oidc/key/${'name'}`, 'name') keyPath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/key/${'name'}/rotate`, 'name') rotatePath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/key`) keysPath;
|
||||
get canCreate() {
|
||||
return this.keyPath.get('canCreate');
|
||||
}
|
||||
get canRead() {
|
||||
return this.keyPath.get('canRead');
|
||||
}
|
||||
|
@ -58,7 +54,4 @@ export default class OidcKeyModel extends Model {
|
|||
get canDelete() {
|
||||
return this.keyPath.get('canDelete');
|
||||
}
|
||||
get canList() {
|
||||
return this.keysPath.get('canList');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,8 @@ export default class OidcProviderModel extends Model {
|
|||
}
|
||||
return this._attributeMeta;
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/oidc/provider/${'name'}`, 'name') providerPath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/provider`) providersPath;
|
||||
get canCreate() {
|
||||
return this.providerPath.get('canCreate');
|
||||
}
|
||||
get canRead() {
|
||||
return this.providerPath.get('canRead');
|
||||
}
|
||||
|
@ -59,16 +56,4 @@ export default class OidcProviderModel extends Model {
|
|||
get canDelete() {
|
||||
return this.providerPath.get('canDelete');
|
||||
}
|
||||
get canList() {
|
||||
return this.providersPath.get('canList');
|
||||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/oidc/client`) clientsPath;
|
||||
get canListClients() {
|
||||
return this.clientsPath.get('canList');
|
||||
}
|
||||
@lazyCapabilities(apiPath`identity/oidc/scope`) scopesPath;
|
||||
get canListScopes() {
|
||||
return this.scopesPath.get('canList');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,6 @@ export default class OidcScopeModel extends Model {
|
|||
}
|
||||
|
||||
@lazyCapabilities(apiPath`identity/oidc/scope/${'name'}`, 'name') scopePath;
|
||||
@lazyCapabilities(apiPath`identity/oidc/scope`) scopesPath;
|
||||
get canCreate() {
|
||||
return this.scopePath.get('canCreate');
|
||||
}
|
||||
get canRead() {
|
||||
return this.scopePath.get('canRead');
|
||||
}
|
||||
|
@ -36,7 +32,4 @@ export default class OidcScopeModel extends Model {
|
|||
get canDelete() {
|
||||
return this.scopePath.get('canDelete');
|
||||
}
|
||||
get canList() {
|
||||
return this.scopesPath.get('canList');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<LinkTo
|
||||
@route="vault.cluster.access.oidc.providers.provider.details"
|
||||
@model={{provider.name}}
|
||||
@disabled={{not provider.canRead}}
|
||||
@disabled={{eq provider.canRead false}}
|
||||
data-test-oidc-provider-menu-link="details"
|
||||
>
|
||||
Details
|
||||
|
|
|
@ -66,7 +66,8 @@
|
|||
@value={{@model.entityIds}}
|
||||
@model={{@model}}
|
||||
@isLink={{true}}
|
||||
@modelType="oidc/assignment"
|
||||
@renderItemName={{true}}
|
||||
@modelType="identity/entity"
|
||||
@itemRoute={{(array "vault.cluster.access.identity.show" "entities" "details")}}
|
||||
@alwaysRender={{true}}
|
||||
@toggleViewAll={{true}}
|
||||
|
@ -77,7 +78,8 @@
|
|||
@value={{@model.groupIds}}
|
||||
@model={{@model}}
|
||||
@isLink={{true}}
|
||||
@modelType="oidc/assignment"
|
||||
@renderItemName={{true}}
|
||||
@modelType="identity/group"
|
||||
@itemRoute={{(array "vault.cluster.access.identity.show" "groups" "details")}}
|
||||
@alwaysRender={{true}}
|
||||
@doNotTruncate={{true}}
|
||||
|
|
|
@ -1,72 +1,68 @@
|
|||
{{! the class linkable-item is needed for the read-more component }}
|
||||
<div data-test-info-table-item-array {{did-insert (perform this.fetchOptions)}} class="linkable-item">
|
||||
<div data-test-info-table-item-array {{did-insert this.fetchOptions}} class="linkable-item">
|
||||
{{#if @isLink}}
|
||||
<div data-test-row-value={{@label}}>
|
||||
<ReadMore>
|
||||
{{#each this.displayArrayTruncated as |name|}}
|
||||
{{#if (is-wildcard-string name)}}
|
||||
{{#let (filter-wildcard name this.allOptions) as |wildcardCount|}}
|
||||
<span>{{name}}</span>
|
||||
<span class="tag is-light has-text-grey-dark" data-test-count="{{if wildcardCount wildcardCount 0}}">
|
||||
includes
|
||||
{{if wildcardCount wildcardCount 0}}
|
||||
{{if (eq wildcardCount 1) @wildcardLabel (pluralize @wildcardLabel)}}
|
||||
</span>
|
||||
{{#if (eq this.displayArrayTruncated.lastObject name)}}
|
||||
<LinkTo @route={{this.rootRoute}} @query={{hash tab=@queryParam}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{else}}
|
||||
{{#if (is-array this.itemRoute)}}
|
||||
<LinkTo
|
||||
@route={{(get this.itemRoute "0")}}
|
||||
@models={{array (get this.itemRoute "1") name (get this.itemRoute "2")}}
|
||||
>
|
||||
{{name}}
|
||||
</LinkTo>
|
||||
{{#if this.fetchComplete}}
|
||||
<ReadMore>
|
||||
{{#each this.displayArrayTruncated as |item|}}
|
||||
{{#if (is-wildcard-string item)}}
|
||||
{{#let (filter-wildcard item this.allOptions) as |wildcardCount|}}
|
||||
<span>{{item}}</span>
|
||||
<span class="tag is-light has-text-grey-dark" data-test-count={{wildcardCount}}>
|
||||
{{if (not-eq wildcardCount undefined) (concat "includes " wildcardCount)}}
|
||||
{{if (eq wildcardCount 1) @wildcardLabel (pluralize @wildcardLabel)}}
|
||||
</span>
|
||||
{{#if (eq this.displayArrayTruncated.lastObject item)}}
|
||||
<LinkTo @route={{this.rootRoute}} @query={{hash tab=@queryParam}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route={{this.itemRoute}}
|
||||
@model={{if @queryParam (concat @queryParam "/" name) name}}
|
||||
data-test-item={{name}}
|
||||
>
|
||||
<span>{{name}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if
|
||||
(or
|
||||
(and (not-eq name this.displayArrayTruncated.lastObject) this.wildcardInDisplayArray)
|
||||
(not-eq name this.displayArrayTruncated.lastObject)
|
||||
)
|
||||
}}
|
||||
,
|
||||
{{/if}}
|
||||
{{#unless this.doNotTruncate}}
|
||||
{{#if (and (eq name this.displayArrayTruncated.lastObject) (gte @displayArray.length 10))}}
|
||||
{{! dec is a math helper that decrements by 5 the length of the array ex: 11-5 = "and 6 others."}}
|
||||
<span data-test-and={{dec 5 @displayArray.length}}>
|
||||
and
|
||||
{{dec 5 @displayArray.length}}
|
||||
others.
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if (and (eq name this.displayArrayTruncated.lastObject) (gte @displayArray.length 10))}}
|
||||
{{#if (is-array @rootRoute)}}
|
||||
<LinkTo @route={{(get @rootRoute "0")}} @model={{(get @rootRoute "1")}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
{{#if (is-array this.itemRoute)}}
|
||||
<LinkTo
|
||||
@route={{(get this.itemRoute "0")}}
|
||||
@models={{array (get this.itemRoute "1") item (get this.itemRoute "2")}}
|
||||
>
|
||||
{{or (get this.itemNameById item) item}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route={{this.rootRoute}} @query={{hash tab=@queryParam}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
<LinkTo
|
||||
@route={{this.itemRoute}}
|
||||
@model={{if @queryParam (concat @queryParam "/" item) item}}
|
||||
data-test-item={{item}}
|
||||
>
|
||||
<span>{{or (get this.itemNameById item) item}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</ReadMore>
|
||||
{{#if (not-eq item this.displayArrayTruncated.lastObject)}}
|
||||
,
|
||||
{{/if}}
|
||||
{{#unless this.doNotTruncate}}
|
||||
{{#if (and (eq item this.displayArrayTruncated.lastObject) (gte @displayArray.length 10))}}
|
||||
{{! dec is a math helper that decrements by 5 the length of the array ex: 11-5 = "and 6 others."}}
|
||||
<span data-test-and={{dec 5 @displayArray.length}}>
|
||||
and
|
||||
{{dec 5 @displayArray.length}}
|
||||
others.
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if (and (eq item this.displayArrayTruncated.lastObject) (gte @displayArray.length 10))}}
|
||||
{{#if (is-array @rootRoute)}}
|
||||
<LinkTo @route={{(get @rootRoute "0")}} @model={{(get @rootRoute "1")}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route={{this.rootRoute}} @query={{hash tab=@queryParam}}>
|
||||
<span data-test-view-all={{lowercase @label}}>View all {{lowercase @label}}.</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</ReadMore>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
<code class="is-word-break has-text-black" data-test-row-value={{@label}}>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { isWildcardString } from 'vault/helpers/is-wildcard-string';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
/**
|
||||
* @module InfoTableItemArray
|
||||
|
@ -30,15 +29,17 @@ import { isWildcardString } from 'vault/helpers/is-wildcard-string';
|
|||
* @param {boolean} [isLink] - Indicates if the item should contain a link-to component. Only setup for arrays, but this could be changed if needed.
|
||||
* @param {string || array} [rootRoute="vault.cluster.secrets.backend.list-root"] - Tells what route the link should go to when selecting "view all". If the route requires more than one dynamic param, insert an array.
|
||||
* @param {string || array} [itemRoute=vault.cluster.secrets.backend.show] - Tells what route the link should go to when selecting the individual item. If the route requires more than one dynamic param, insert an array.
|
||||
* @param {string} [modelType] - Tells which model you want data for the allOptions to be returned from. Used in conjunction with the the isLink.
|
||||
* @param {string} [modelType] - Tells which model you want to query and set allOptions. Used in conjunction with the the isLink.
|
||||
* @param {string} [wildcardLabel] - when you want the component to return a count on the model for options returned when using a wildcard you must provide a label of the count e.g. role. Should be singular.
|
||||
* @param {string} [backend] - To specify which backend to point the link to.
|
||||
* @param {boolean} [doNotTruncate=false] - Determines whether to show the View all "roles" link.
|
||||
* @param {boolean} [doNotTruncate=false] - Determines whether to show the View all "roles" link. Otherwise uses the ReadMore component's "See More" toggle
|
||||
* @param {boolean} [renderItemName=false] - If true renders the item name instead of its id
|
||||
*/
|
||||
export default class InfoTableItemArray extends Component {
|
||||
@tracked allOptions = null;
|
||||
@tracked wildcardInDisplayArray = false;
|
||||
@service store;
|
||||
@tracked allOptions = null;
|
||||
@tracked itemNameById; // object is only created if renderItemName=true
|
||||
@tracked fetchComplete = false;
|
||||
|
||||
get rootRoute() {
|
||||
return this.args.rootRoute || 'vault.cluster.secrets.backend.list-root';
|
||||
|
@ -62,29 +63,26 @@ export default class InfoTableItemArray extends Component {
|
|||
return displayArray;
|
||||
}
|
||||
|
||||
async checkWildcardInArray() {
|
||||
if (!this.args.displayArray) {
|
||||
return;
|
||||
}
|
||||
let filteredArray = await this.args.displayArray.filter((item) => isWildcardString([item]));
|
||||
this.wildcardInDisplayArray = filteredArray.length > 0 ? true : false;
|
||||
}
|
||||
|
||||
@task *fetchOptions() {
|
||||
@action async fetchOptions() {
|
||||
if (this.args.isLink && this.args.modelType) {
|
||||
let queryOptions = {};
|
||||
let queryOptions = this.args.backend ? { backend: this.args.backend } : {};
|
||||
|
||||
if (this.args.backend) {
|
||||
queryOptions = { backend: this.args.backend };
|
||||
let modelRecords = await this.store.query(this.args.modelType, queryOptions).catch((err) => {
|
||||
if (err.httpStatus === 404) {
|
||||
return [];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
this.allOptions = modelRecords ? modelRecords.mapBy('id') : null;
|
||||
if (this.args.renderItemName && modelRecords) {
|
||||
modelRecords.forEach(({ id, name }) => {
|
||||
// create key/value pair { item-id: item-name } for each record
|
||||
this.itemNameById = { ...this.itemNameById, [id]: name };
|
||||
});
|
||||
}
|
||||
|
||||
let options = yield this.store.query(this.args.modelType, queryOptions);
|
||||
this.formatOptions(options);
|
||||
}
|
||||
this.checkWildcardInArray();
|
||||
}
|
||||
|
||||
formatOptions(options) {
|
||||
this.allOptions = options.mapBy('id');
|
||||
this.fetchComplete = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
@rootRoute={{@rootRoute}}
|
||||
@itemRoute={{@itemRoute}}
|
||||
@doNotTruncate={{@doNotTruncate}}
|
||||
@renderItemName={{@renderItemName}}
|
||||
/>
|
||||
{{else}}
|
||||
{{#if @tooltipText}}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { singularize } from 'ember-inflector';
|
|||
import { resolve } from 'rsvp';
|
||||
import { filterOptions, defaultMatcher } from 'ember-power-select/utils/group-utils';
|
||||
import layout from '../templates/components/search-select';
|
||||
import { isWildcardString } from 'vault/helpers/is-wildcard-string';
|
||||
|
||||
/**
|
||||
* @module SearchSelect
|
||||
|
@ -87,8 +88,9 @@ export default Component.extend({
|
|||
this.set('allOptions', allOptions); // used by filter-wildcard helper
|
||||
let formattedOptions = this.selectedOptions.map((option) => {
|
||||
let matchingOption = options.findBy(this.idKey, option);
|
||||
// an undefined matchingOption means a selectedOption, on edit, didn't match a model returned from the query and therefore doesn't exist
|
||||
let addTooltip = matchingOption ? false : true; // add tooltip to let user know the selection can be discarded
|
||||
// an undefined matchingOption means a selectedOption, on edit, didn't match a model returned from the query
|
||||
// this means it is a wildcard string or no longer exists
|
||||
let addTooltip = matchingOption || isWildcardString([option]) ? false : true; // add tooltip to let user know the selection can be discarded
|
||||
options.removeObject(matchingOption);
|
||||
return {
|
||||
id: option,
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<InfoTooltip>
|
||||
The item with this
|
||||
{{to-label this.idKey}}
|
||||
no longer exists and can safely be removed.
|
||||
no longer exists.
|
||||
</InfoTooltip>
|
||||
{{/if}}
|
||||
<button
|
||||
|
|
|
@ -233,7 +233,7 @@ module('Acceptance | oidc-config clients and assignments', function (hooks) {
|
|||
|
||||
// assert default values in assignment details view are correct
|
||||
assert.dom('[data-test-value-div="Name"]').hasText('test-assignment');
|
||||
assert.dom('[data-test-value-div="Entities"]').hasText('1234-12345', 'shows the entity id.');
|
||||
assert.dom('[data-test-value-div="Entities"]').hasText('test-entity', 'shows the entity name.');
|
||||
|
||||
// edit assignment
|
||||
await click(SELECTORS.assignmentEditButton);
|
||||
|
@ -252,8 +252,8 @@ module('Acceptance | oidc-config clients and assignments', function (hooks) {
|
|||
'renders success flash upon updating the assignment'
|
||||
);
|
||||
|
||||
assert.dom('[data-test-value-div="Entities"]').hasText('1234-12345', 'it still shows the entity id.');
|
||||
assert.dom('[data-test-value-div="Groups"]').hasText('abcdef-123', 'shows updated group name id.');
|
||||
assert.dom('[data-test-value-div="Entities"]').hasText('test-entity', 'it still shows the entity name.');
|
||||
assert.dom('[data-test-value-div="Groups"]').hasText('test-group', 'shows updated group name id.');
|
||||
|
||||
// delete the assignment
|
||||
await click(SELECTORS.assignmentDeleteButton);
|
||||
|
|
|
@ -1,23 +1,42 @@
|
|||
import { module, test } from 'qunit';
|
||||
import Service from '@ember/service';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { findAll, render } from '@ember/test-helpers';
|
||||
import { run } from '@ember/runloop';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
const DISPLAY_ARRAY = ['role-1', 'role-2', 'role-3', 'role-4', 'role-5'];
|
||||
|
||||
const storeService = Service.extend({
|
||||
query() {
|
||||
return new Promise((resolve) => {
|
||||
resolve([
|
||||
{ id: 'role-1' },
|
||||
{ id: 'role-2' },
|
||||
{ id: 'role-3' },
|
||||
{ id: 'role-4' },
|
||||
{ id: 'role-5' },
|
||||
{ id: 'role-6' },
|
||||
]);
|
||||
query(modelType) {
|
||||
return new Promise((resolve, reject) => {
|
||||
switch (modelType) {
|
||||
case 'transform/role':
|
||||
resolve([
|
||||
{ id: 'role-1' },
|
||||
{ id: 'role-2' },
|
||||
{ id: 'role-3' },
|
||||
{ id: 'role-4' },
|
||||
{ id: 'role-5' },
|
||||
{ id: 'role-6' },
|
||||
]);
|
||||
break;
|
||||
case 'model/no-permission':
|
||||
reject({ httpStatus: 403, message: 'permission denied' });
|
||||
break;
|
||||
case 'identity/entity':
|
||||
resolve([
|
||||
{ id: '1', name: 'one' },
|
||||
{ id: '6', name: 'six' },
|
||||
{ id: '7', name: 'seven' },
|
||||
{ id: '8', name: 'eight' },
|
||||
{ id: '9', name: 'nine' },
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
reject({ httpStatus: 404, message: 'not found' });
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -128,4 +147,123 @@ module('Integration | Component | InfoTableItemArray', function (hooks) {
|
|||
.exists('correctly counts with wildcard filter and shows the count');
|
||||
assert.dom('[data-test-view-all="roles"]').hasText('View all roles.', 'renders correct view all text');
|
||||
});
|
||||
|
||||
test('it fails gracefully if query returns 403 and display array contains wildcard', async function (assert) {
|
||||
const displayArrayWithWildcard = [
|
||||
'role-1',
|
||||
'role-2',
|
||||
'role-3',
|
||||
'r*',
|
||||
'role-4',
|
||||
'role-5',
|
||||
'role-6',
|
||||
'role-7',
|
||||
'role-8',
|
||||
'role-9',
|
||||
'role-10',
|
||||
];
|
||||
this.set('displayArrayWithWildcard', displayArrayWithWildcard);
|
||||
this.set('modelType', 'model/no-permission');
|
||||
await render(hbs`
|
||||
<InfoTableItemArray
|
||||
@label={{this.label}}
|
||||
@displayArray={{this.displayArrayWithWildcard}}
|
||||
@isLink={{this.isLink}}
|
||||
@modelType={{this.modelType}}
|
||||
@queryParam={{this.queryParam}}
|
||||
@backend={{this.backend}}
|
||||
/>`);
|
||||
assert.equal(findAll('[data-test-item]').length, 4, 'lists 4 roles');
|
||||
assert.dom('[data-test-readmore-content]').hasTextContaining('r*', 'renders wildcard');
|
||||
assert.dom('[data-test-count="0"]').doesNotExist('does not render badge');
|
||||
assert.dom('[data-test-view-all="roles"]').hasText('View all roles.', 'renders correct view all text');
|
||||
assert.dom('[data-test-and="6"]').exists(`renders correct 'and 6 others' text`);
|
||||
});
|
||||
|
||||
test('it fails gracefully if query returns 404 and display array contains wildcard', async function (assert) {
|
||||
const displayArrayWithWildcard = [
|
||||
'role-1',
|
||||
'role-2',
|
||||
'role-3',
|
||||
'r*',
|
||||
'role-4',
|
||||
'role-5',
|
||||
'role-6',
|
||||
'role-7',
|
||||
'role-8',
|
||||
'role-9',
|
||||
'role-10',
|
||||
];
|
||||
this.set('displayArrayWithWildcard', displayArrayWithWildcard);
|
||||
this.set('modelType', 'model-not-found');
|
||||
await render(hbs`
|
||||
<InfoTableItemArray
|
||||
@label={{this.label}}
|
||||
@displayArray={{this.displayArrayWithWildcard}}
|
||||
@isLink={{this.isLink}}
|
||||
@modelType={{this.modelType}}
|
||||
@queryParam={{this.queryParam}}
|
||||
@backend={{this.backend}}
|
||||
/>`);
|
||||
assert.dom('[data-test-count="0"]').hasText('includes 0', 'renders badge');
|
||||
assert.equal(findAll('[data-test-item]').length, 4, 'renders list of 4 roles');
|
||||
assert.dom('[data-test-view-all="roles"]').hasText('View all roles.', 'renders view all text');
|
||||
});
|
||||
|
||||
test('it renders name if renderItemName=true or id if name not found', async function (assert) {
|
||||
const value = ['6', '8', '123-id'];
|
||||
this.set('value', value);
|
||||
this.set('modelType', 'identity/entity');
|
||||
await render(hbs`
|
||||
<InfoTableItemArray
|
||||
@label={{this.label}}
|
||||
@displayArray={{this.value}}
|
||||
@isLink={{this.isLink}}
|
||||
@modelType={{this.modelType}}
|
||||
@renderItemName={{true}}
|
||||
/>`);
|
||||
assert.dom('[data-test-item="6"]').hasText('six', `renders name of 'six' instead of id`);
|
||||
assert.dom('[data-test-item="8"]').hasText('eight', `renders 'eight' instead of id`);
|
||||
assert.equal(findAll('[data-test-item]').length, 3, 'renders all entities');
|
||||
assert
|
||||
.dom('[data-test-item="123-id"]')
|
||||
.hasText('123-id', 'renders id instead of name if no record for name');
|
||||
});
|
||||
|
||||
test('it truncates and renders name when renderItemName=true', async function (assert) {
|
||||
const value = ['1', '2', '3-id', '4', '5', '6', '7', '8', '9', '10'];
|
||||
this.set('value', value);
|
||||
this.set('modelType', 'identity/entity');
|
||||
await render(hbs`
|
||||
<InfoTableItemArray
|
||||
@label="Entities"
|
||||
@displayArray={{this.value}}
|
||||
@isLink={{this.isLink}}
|
||||
@modelType={{this.modelType}}
|
||||
@renderItemName={{true}}
|
||||
/>`);
|
||||
assert.dom('[data-test-item="1"]').hasText('one', `renders name of 'one' instead of id`);
|
||||
assert.dom('[data-test-item="3-id"]').hasText('3-id', 'renders id instead of name if no record for name');
|
||||
assert.equal(findAll('[data-test-item]').length, 5, 'only lists 5 entities');
|
||||
});
|
||||
|
||||
test('it truncates using read more component when overflows div', async function (assert) {
|
||||
const value = ['1', '2', '3-id', '4', '5', '6', '7', '8', '9', '10'];
|
||||
this.set('value', value);
|
||||
this.set('modelType', 'identity/entity');
|
||||
await render(hbs`
|
||||
<div style="width: 200px">
|
||||
<InfoTableItemArray
|
||||
@label="Entities"
|
||||
@displayArray={{this.value}}
|
||||
@isLink={{this.isLink}}
|
||||
@modelType={{this.modelType}}
|
||||
@renderItemName={{true}}
|
||||
@doNotTruncate={{true}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
assert.dom('[data-test-readmore-toggle]').exists('renders see more toggle');
|
||||
assert.dom('[data-test-view-all]').doesNotExist('Does not render view all text');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -687,7 +687,7 @@ module('Integration | Component | search select', function (hooks) {
|
|||
test('it renders an info tooltip beside selection if does not match a record returned from query when passObject=false and idKey=id', async function (assert) {
|
||||
const models = ['some/model'];
|
||||
const spy = sinon.spy();
|
||||
const inputValue = ['model-a-id', 'non-existent-model'];
|
||||
const inputValue = ['model-a-id', 'non-existent-model', 'wildcard*'];
|
||||
this.set('models', models);
|
||||
this.set('onChange', spy);
|
||||
this.set('inputValue', inputValue);
|
||||
|
@ -700,16 +700,18 @@ module('Integration | Component | search select', function (hooks) {
|
|||
@passObject={{false}}
|
||||
/>
|
||||
`);
|
||||
|
||||
assert.equal(component.selectedOptions.length, 2, 'there are two selected options');
|
||||
assert.equal(component.selectedOptions.length, 3, 'there are three selected options');
|
||||
assert.dom('[data-test-selected-option="0"]').hasText('model-a-id');
|
||||
assert.dom('[data-test-selected-option="1"]').hasText('non-existent-model');
|
||||
assert.dom('[data-test-selected-option="2"]').hasText('wildcard*');
|
||||
assert
|
||||
.dom('[data-test-selected-option="0"] [data-test-component="info-tooltip"]')
|
||||
.doesNotExist('does not render info tooltip for model that exists');
|
||||
|
||||
assert
|
||||
.dom('[data-test-selected-option="1"] [data-test-component="info-tooltip"]')
|
||||
.exists('renders info tooltip for model not returned from query');
|
||||
assert
|
||||
.dom('[data-test-selected-option="2"] [data-test-component="info-tooltip"]')
|
||||
.doesNotExist('does not render info tooltip for wildcard option');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue