refactor: 💡 add custom sort to resources (#2860)

* refactor: 💡 add custom sort

* test: 💍 fix tests

* test: 💍 fix more tests

* refactor: 💡 use reusable sort function for name

* refactor: 💡 use sort util in all resources w/ optional name

* Add missing copyright headers

* test: 💍 fix broken tests & add tests for new util

* refactor: 💡 add name custom sort to users

* test: 💍 fix import in tests

* refactor: 💡 add custom name sort to scopes

* test: 💍 pr feedback on util test cases

* refactor: 💡 move sort function into object (pr feedback)
This commit is contained in:
Lisbet Alvarez authored and GitHub committed 2025-06-05 11:00:25 -07:00
1 parent 4c959ab297
commit 738fa1f15f
27 files changed
+331 -41

No files matched your search

+12 -1
View File
@@ -42,7 +42,7 @@ export const sortResults = (results, { querySort, schema }) => {
// Default sort direction is ascending unless we are sorting by `created_time` (default sort attribute)
const defaultSortDirection =
sortAttribute === SORT_DEFAULT_ATTRIBUTE
sortAttribute === SORT_DEFAULT_ATTRIBUTE && !querySort.sortFunction
? SORT_DIRECTION_DESCENDING
: SORT_DIRECTION_ASCENDING;
const sortDirection = querySort.direction || defaultSortDirection;
@@ -55,6 +55,17 @@ export const sortResults = (results, { querySort, schema }) => {
throw new Error('Invalid sort direction');
}
// Execute custom sort function if one is provided.
if (querySort.sortFunction) {
return results.toSorted((a, b) => {
const sortResult = querySort.sortFunction(a, b);
return sortDirection === SORT_DIRECTION_ASCENDING
? sortResult
: -1 * sortResult;
});
}
const sortAttributeDataType = schema.attributes.get(sortAttribute)?.type;
const sortFunction =
@@ -306,6 +306,54 @@ module('Unit | Handler | indexed-db-handler', function (hooks) {
['Alias 001', 'Alias 002', 'Alias 003', 'Alias 004'],
);
});
test('it sorts by descending, then ascending `name` using custom string sorting', async function (assert) {
const alias1 = this.server.create('alias', { name: 'alias1' });
const alias2 = this.server.create('alias', { name: 'alias2' });
const alias3 = this.server.create('alias', { name: 'alias3' });
const alias4 = this.server.create('alias', { name: 'alias4' });
const sortName = (recordA, recordB) => {
const nameMap = {
alias3: 'Alpha',
alias4: 'Beta',
alias1: 'Delta',
alias2: 'Epsilon',
};
return String(nameMap[recordA.attributes.name]).localeCompare(
String(nameMap[recordB.attributes.name]),
);
};
const shuffledAliases = faker.helpers.shuffle([
alias1,
alias2,
alias3,
alias4,
]);
this.server.get(
'aliases',
createPaginatedResponseHandler(shuffledAliases, {
pageSize: 1,
}),
);
const resultsDesc = await store.query('alias', {
query: { sort: { sortFunction: sortName, direction: 'desc' } },
});
const resultsAsc = await store.query('alias', {
query: { sort: { sortFunction: sortName, direction: 'asc' } },
});
assert.deepEqual(
resultsDesc.map(({ name }) => name),
['alias2', 'alias1', 'alias4', 'alias3'],
);
assert.deepEqual(
resultsAsc.map(({ name }) => name),
['alias3', 'alias4', 'alias1', 'alias2'],
);
});
});
module('option: pushToStore', function (hooks) {
@@ -421,4 +421,68 @@ module('Unit | Utility | sortResults', function (hooks) {
);
});
});
module('custom function sorting', function () {
const result01 = createJsonApiMockRecord('id_01', 'target', {
name: 'target1',
});
const result02 = createJsonApiMockRecord('id_02', 'target', {
name: 'target2',
});
const result03 = createJsonApiMockRecord('id_03', 'target', {
name: 'target3',
});
const result04 = createJsonApiMockRecord('id_04', 'target', {
name: 'target4',
});
const sortFunction = (recordA, recordB) => {
const nameMap = {
target3: 'Alpha',
target4: 'Beta',
target1: 'Delta',
target2: 'Epsilon',
};
return String(nameMap[recordA.attributes.name]).localeCompare(
String(nameMap[recordB.attributes.name]),
);
};
const schema = { attributes: new Map() };
test.each(
'it sorts by `name` using custom sorting function',
{
'ascending (default)': {
querySort: { sortFunction },
expectedResults: ['target3', 'target4', 'target1', 'target2'],
},
descending: {
querySort: { sortFunction, direction: 'desc' },
expectedResults: ['target2', 'target1', 'target4', 'target3'],
},
ascending: {
querySort: { sortFunction, direction: 'asc' },
expectedResults: ['target3', 'target4', 'target1', 'target2'],
},
},
function (assert, input) {
const shuffledResults = faker.helpers.shuffle([
result01,
result02,
result03,
result04,
]);
schema.attributes.set('name', { type: 'string' });
const sortedResults = sortResults(shuffledResults, {
querySort: input.querySort,
schema,
});
assert.deepEqual(
sortedResults.map(({ attributes: { name } }) => name),
input.expectedResults,
);
},
);
});
});
@@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
export default class ScopesScopeCredentialStoresIndexRoute extends Route {
// =services
@@ -77,10 +78,10 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route {
filters.type.push({ equals: type });
});
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const sort =
sortAttribute === 'name'
? { sortFunction: sortNameWithIdFallback, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let credentialStores;
let totalItems = 0;
@@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
export default class ScopesScopeGroupsIndexRoute extends Route {
// =services
@@ -63,15 +64,16 @@ export default class ScopesScopeGroupsIndexRoute extends Route {
const scope = this.modelFor('scopes.scope');
const { id: scope_id } = scope;
const filters = { scope_id: [{ equals: scope_id }] };
const sort =
sortAttribute === 'name'
? { sortFunction: sortNameWithIdFallback, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let groups = [];
let totalItems = 0;
let doGroupsExist = false;
const filters = { scope_id: [{ equals: scope_id }] };
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
if (this.can.can('list model', scope, { collection: 'groups' })) {
groups = await this.store.query('group', {
scope_id,
@@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
export default class ScopesScopeHostCatalogsIndexRoute extends Route {
// =services
@@ -70,10 +71,10 @@ export default class ScopesScopeHostCatalogsIndexRoute extends Route {
'plugin.name': [],
};
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const sort =
sortAttribute === 'name'
? { sortFunction: sortNameWithIdFallback, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let hostCatalogs;
let totalItems = 0;
@@ -6,6 +6,8 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
import { GRANT_SCOPE_THIS } from 'api/models/role';
export default class ScopesScopeRolesIndexRoute extends Route {
// =attributes
@@ -31,6 +33,21 @@ export default class ScopesScopeRolesIndexRoute extends Route {
},
};
customSortFunction = {
name: sortNameWithIdFallback,
grant_scope_ids: (recordA, recordB) => {
const a = recordA.attributes?.grant_scope_ids.includes(GRANT_SCOPE_THIS);
const b = recordB.attributes?.grant_scope_ids.includes(GRANT_SCOPE_THIS);
if (a === b) {
return 0;
} else if (a) {
return 1;
} else {
return -1;
}
},
};
// =services
@service can;
@@ -66,10 +83,11 @@ export default class ScopesScopeRolesIndexRoute extends Route {
const filters = {
scope_id: [{ equals: scope_id }],
};
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const sortFunction = this.customSortFunction[sortAttribute];
const sort = sortFunction
? { sortFunction, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let roles;
let totalItems = 0;
@@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
export default class ScopesScopeScopesIndexRoute extends Route {
// =attributes
@@ -67,10 +68,10 @@ export default class ScopesScopeScopesIndexRoute extends Route {
scope_id: [{ equals: scope_id }],
};
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const sort =
sortAttribute === 'name'
? { sortFunction: sortNameWithIdFallback, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
const subScopes = await this.store.query('scope', {
scope_id,
@@ -7,12 +7,18 @@ import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { action } from '@ember/object';
import { restartableTask, timeout } from 'ember-concurrency';
import {
STATE_SESSION_RECORDING_STARTED,
STATE_SESSION_RECORDING_AVAILABLE,
STATE_SESSION_RECORDING_UNKNOWN,
} from 'api/models/session-recording';
export default class ScopesScopeSessionRecordingsIndexRoute extends Route {
// =services
@service store;
@service router;
@service can;
@service intl;
// =attributes
@@ -65,6 +71,17 @@ export default class ScopesScopeSessionRecordingsIndexRoute extends Route {
return this.retrieveData.perform({ ...params, useDebounce });
}
sortState = (recordA, recordB) => {
const stateMap = {
[STATE_SESSION_RECORDING_AVAILABLE]: this.intl.t('states.completed'),
[STATE_SESSION_RECORDING_STARTED]: this.intl.t('states.recording'),
[STATE_SESSION_RECORDING_UNKNOWN]: this.intl.t('states.failed'),
};
return String(stateMap[recordA.attributes.state]).localeCompare(
String(stateMap[recordB.attributes.state]),
);
};
retrieveData = restartableTask(
async ({
search,
@@ -105,15 +122,16 @@ export default class ScopesScopeSessionRecordingsIndexRoute extends Route {
filters['create_time_values.target.id'].push({ equals: target });
});
const sort =
sortAttribute === 'state'
? { sortFunction: this.sortState, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
if (
this.can.can('list scope', scope, {
collection: 'session-recordings',
})
) {
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const queryOptions = {
scope_id,
recursive: true,
@@ -5,11 +5,12 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import {
STATUS_SESSION_ACTIVE,
STATUS_SESSION_PENDING,
} from 'api/models/session';
import { restartableTask, timeout } from 'ember-concurrency';
import { TYPE_TARGET_SSH, TYPE_TARGET_TCP } from 'api/models/target';
export default class ScopesScopeTargetsIndexRoute extends Route {
// =services
@@ -17,6 +18,7 @@ export default class ScopesScopeTargetsIndexRoute extends Route {
@service can;
@service store;
@service session;
@service intl;
// =attributes
@@ -62,6 +64,16 @@ export default class ScopesScopeTargetsIndexRoute extends Route {
return this.retrieveData.perform({ ...params, useDebounce });
}
sortType = (recordA, recordB) => {
const typeMap = {
[TYPE_TARGET_SSH]: this.intl.t('resources.target.types.ssh'),
[TYPE_TARGET_TCP]: this.intl.t('resources.target.types.tcp'),
};
return String(typeMap[recordA.attributes.type]).localeCompare(
String(typeMap[recordB.attributes.type]),
);
};
retrieveData = restartableTask(
async ({
search,
@@ -105,14 +117,15 @@ export default class ScopesScopeTargetsIndexRoute extends Route {
this.addActiveSessionFilters(filters, availableSessions, sessions);
}
const sort =
sortAttribute === 'type'
? { sortFunction: this.sortType, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let targets;
let totalItems = 0;
let doTargetsExist = false;
if (this.can.can('list model', scope, { collection: 'targets' })) {
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
targets = await this.store.query('target', {
scope_id,
query: { search, filters, sort },
@@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { restartableTask, timeout } from 'ember-concurrency';
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
export default class ScopesScopeUsersIndexRoute extends Route {
// =attributes
@@ -68,10 +69,10 @@ export default class ScopesScopeUsersIndexRoute extends Route {
scope_id: [{ equals: scope_id }],
};
const sort = {
attribute: sortAttribute,
direction: sortDirection,
};
const sort =
sortAttribute === 'name'
? { sortFunction: sortNameWithIdFallback, direction: sortDirection }
: { attribute: sortAttribute, direction: sortDirection };
let users;
let totalItems = 0;
@@ -59,9 +59,10 @@
>
{{t 'form.name.label'}}
</H.ThSort>
<H.Th>
{{t 'resources.role.titles.grants-applied'}}
</H.Th>
<H.ThSort
@onClickSort={{fn H.setSortBy 'grant_scope_ids'}}
@sortOrder={{if (eq 'grant_scope_ids' H.sortBy) H.sortOrder}}
>{{t 'resources.role.titles.grants-applied'}}</H.ThSort>
<H.ThSort
@onClickSort={{fn H.setSortBy 'id'}}
@sortOrder={{if (eq 'id' H.sortBy) H.sortOrder}}
@@ -0,0 +1,12 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export function sortNameWithIdFallback(recordA, recordB) {
let a = recordA.attributes.name;
let b = recordB.attributes.name;
a = a ? a : recordA.id;
b = b ? b : recordB.id;
return String(a).localeCompare(String(b));
}
@@ -14,6 +14,7 @@ import {
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import { Response } from 'miragejs';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import {
@@ -32,6 +33,7 @@ module('Acceptance | authentication', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let indexURL;
let globalScope;
@@ -187,6 +187,14 @@ module('Acceptance | roles | list', function (hooks) {
expectedAscendingSort: ['Alpha', 'Beta', 'Delta', 'Epsilon', 'Gamma'],
column: 1,
},
'on grants applied': {
attribute: {
key: 'grant_scope_ids',
values: [[GRANT_SCOPE_THIS], [GRANT_SCOPE_THIS], [], [], []],
},
expectedAscendingSort: ['No', 'No', 'No', 'Yes', 'Yes'],
column: 2,
},
'on id': {
attribute: {
key: 'id',
@@ -8,6 +8,7 @@ import { visit, currentURL, click, fillIn, waitFor } from '@ember/test-helpers';
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { faker } from '@faker-js/faker';
import * as commonSelectors from 'admin/tests/helpers/selectors';
@@ -22,6 +23,7 @@ module('Acceptance | session recordings | list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
const CREATED_TIME_VALUES_ARRAY = [
'2020-01-01T00:00:00.010Z',
@@ -288,7 +290,7 @@ module('Acceptance | session recordings | list', function (hooks) {
STATE_SESSION_RECORDING_STARTED,
],
},
expectedAscendingSort: ['Completed', 'Recording', 'Failed'],
expectedAscendingSort: ['Completed', 'Failed', 'Recording'],
column: 2,
},
},
@@ -10,6 +10,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
import * as selectors from './selectors';
@@ -17,6 +18,7 @@ module('Acceptance | session-recordings | read', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let featuresService;
@@ -11,6 +11,7 @@ import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { Response } from 'miragejs';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
import * as selectors from '../../selectors';
@@ -20,6 +21,7 @@ module(
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let featuresService;
let getRecordingCount;
@@ -11,6 +11,7 @@ import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { Response } from 'miragejs';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
import { TYPE_TARGET_TCP, TYPE_TARGET_SSH } from 'api/models/target';
@@ -19,6 +20,7 @@ module('Acceptance | targets | create-alias', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let getAliasCount;
let featuresService;
@@ -17,6 +17,7 @@ import { Response } from 'miragejs';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_TARGET_TCP, TYPE_TARGET_SSH } from 'api/models/target';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
import * as selectors from './selectors';
@@ -24,6 +25,7 @@ module('Acceptance | targets | create', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let getTargetCount;
let getTCPTargetCount;
@@ -8,6 +8,7 @@ import { visit, click, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import { Response } from 'miragejs';
import { authenticateSession } from 'ember-simple-auth/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
@@ -17,6 +18,7 @@ module('Acceptance | targets | delete', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let getTargetCount;
@@ -15,6 +15,7 @@ import {
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_TARGET_TCP, TYPE_TARGET_SSH } from 'api/models/target';
import { STATUS_SESSION_ACTIVE } from 'api/models/session';
@@ -26,6 +27,7 @@ module('Acceptance | targets | list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
const NAME_VALUES_ARRAY = ['Alpha', 'Beta', 'Delta', 'Epsilon', 'Gamma'];
const ID_VALUES_ARRAY = ['i_0001', 'i_0010', 'i_0100', 'i_1000', 'i_10000'];
@@ -307,7 +309,7 @@ module('Acceptance | targets | list', function (hooks) {
key: 'type',
values: [TYPE_TARGET_SSH, TYPE_TARGET_TCP, TYPE_TARGET_SSH],
},
expectedAscendingSort: ['SSH', 'SSH', 'Generic TCP'],
expectedAscendingSort: ['Generic TCP', 'SSH', 'SSH'],
column: 2,
},
'on id': {
@@ -8,6 +8,7 @@ import { visit, currentURL, click } from '@ember/test-helpers';
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_TARGET_TCP, TYPE_TARGET_SSH } from 'api/models/target';
@@ -18,6 +19,7 @@ module('Acceptance | targets | read', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let featuresService;
let aliasResource;
@@ -8,6 +8,7 @@ import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
import { setupApplicationTest } from 'admin/tests/helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { setupIntl } from 'ember-intl/test-support';
import { Response } from 'miragejs';
import { authenticateSession } from 'ember-simple-auth/test-support';
import * as commonSelectors from 'admin/tests/helpers/selectors';
@@ -17,6 +18,7 @@ module('Acceptance | targets | update', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
setupIntl(hooks, 'en-us');
let featuresService;
@@ -5,11 +5,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'admin/tests/helpers';
import { setupIntl } from 'ember-intl/test-support';
module(
'Unit | Route | scopes/scope/session-recordings/index',
function (hooks) {
setupTest(hooks);
setupIntl(hooks, 'en-us');
test('it exists', function (assert) {
let route = this.owner.lookup(
@@ -5,9 +5,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupIntl } from 'ember-intl/test-support';
module('Unit | Route | scopes/scope/targets/index', function (hooks) {
setupTest(hooks);
setupIntl(hooks, 'en-us');
test('it exists', function (assert) {
let route = this.owner.lookup('route:scopes/scope/targets/index');
@@ -0,0 +1,67 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { sortNameWithIdFallback } from 'admin/utils/sort-name-with-id-fallback';
import { module, test } from 'qunit';
function createMockRecord(id, attributes = {}) {
return {
id,
attributes,
};
}
module('Unit | Utility | sort-name-with-id-fallback', function () {
test.each(
'sort on name with id fallback',
[
{
recordA: createMockRecord('id_01'),
recordB: createMockRecord('id_01'),
expectedResult: 0,
},
{
recordA: createMockRecord('id_02'),
recordB: createMockRecord('id_01'),
expectedResult: 1,
},
{
recordA: createMockRecord('id_01'),
recordB: createMockRecord('id_02'),
expectedResult: -1,
},
{
recordA: createMockRecord('id_01', { name: 'Alpha' }),
recordB: createMockRecord('id_02'),
expectedResult: -1,
},
{
recordA: createMockRecord('id_01'),
recordB: createMockRecord('id_02', { name: 'Alpha' }),
expectedResult: 1,
},
{
recordA: createMockRecord('id_01', { name: 'Alpha' }),
recordB: createMockRecord('id_02', { name: 'Beta' }),
expectedResult: -1,
},
{
recordA: createMockRecord('id_01', { name: 'Beta' }),
recordB: createMockRecord('id_02', { name: 'Alpha' }),
expectedResult: 1,
},
{
recordA: createMockRecord('id_01', { name: 'Alpha' }),
recordB: createMockRecord('id_02', { name: 'Alpha' }),
expectedResult: 0,
},
],
function (assert, input) {
const result = sortNameWithIdFallback(input.recordA, input.recordB);
assert.strictEqual(result, input.expectedResult);
},
);
});