Files
threshold-ui/ui/admin/tests/integration/components/grant-actions/index-test.js
T
Lisbet Alvarez ecefa39b98 fix: 🐛 actions not displaying for templates in grants editor (#3296)
* fix: 🐛 actions not displaying for templates in grants editor

* fix: 🐛 invalid ids check

* fix: 🐛 update check for ids type

* test: 💍 fix broken strings

* test: 💍 fix test case

* test: 💍 remove log
2026-06-03 12:46:34 -07:00

281 lines
8.3 KiB
JavaScript

/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'admin/tests/helpers';
import { setupIntl } from 'ember-intl/test-support';
import { setupMirage } from 'admin/tests/helpers/mirage';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import * as commonSelectors from 'admin/tests/helpers/selectors';
const GRANT_ACTIONS_TABLE = '[data-test-grant-actions-table]';
const GRANT_ACTIONS_EMPTY_STATE = '[data-test-grant-actions-empty-state]';
const GRANT_ACTIONS_NO_TYPE_DETECTED =
'[data-test-grant-actions-no-type-detected]';
const GRANT_ACTIONS_INVALID_ID_OR_TYPE =
'[data-test-grant-actions-invalid-id-or-type]';
module('Integration | Component | grant-actions/index', function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
setupIntl(hooks, 'en-us');
hooks.beforeEach(async function () {
const response = await fetch('/grants-schema.json');
if (!response.ok) {
throw new Error(`Failed to fetch grants schema: ${response.status}`);
}
const grantsSchemaData = await response.json();
this.grantsSchema = grantsSchemaData;
});
test('it renders the actions relevant to the current grant line', async function (assert) {
this.grantString = 'ids=hcst_1234567890;type=host-set';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).isVisible();
assert.dom(commonSelectors.TABLE_ROWS).isVisible({ count: 8 });
const rowData = [
...this.element.querySelectorAll(commonSelectors.TABLE_ROWS),
].map((row) => {
const [name, description] = [...row.querySelectorAll('td')].map((cell) =>
cell.textContent.trim(),
);
return { name, description };
});
assert.deepEqual(
rowData.map(({ name }) => name),
[
'add-hosts',
'create',
'delete',
'list',
'read',
'remove-hosts',
'set-hosts',
'update',
],
);
assert.deepEqual(rowData[0], {
name: 'add-hosts',
description: 'Add hosts to a host set',
});
assert.deepEqual(rowData[1], {
name: 'create',
description: 'Create a new host-set',
});
assert.deepEqual(rowData.at(-1), {
name: 'update',
description: 'Update a host-set',
});
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
});
test('it renders relevant actions for template IDs', async function (assert) {
this.grantString = 'ids={{.User.Id}}';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).isVisible();
assert.dom(commonSelectors.TABLE_ROWS).isVisible({ count: 7 });
const rowData = [
...this.element.querySelectorAll(commonSelectors.TABLE_ROWS),
].map((row) => {
const [name, description] = [...row.querySelectorAll('td')].map((cell) =>
cell.textContent.trim(),
);
return { name, description };
});
assert.deepEqual(
rowData.map(({ name }) => name),
[
'add-accounts',
'delete',
'list-resolvable-aliases',
'read',
'remove-accounts',
'set-accounts',
'update',
],
);
assert.deepEqual(rowData[0], {
name: 'add-accounts',
description: 'Add accounts to a user',
});
assert.deepEqual(rowData[1], {
name: 'delete',
description: 'Delete a user',
});
assert.deepEqual(rowData.at(-1), {
name: 'update',
description: 'Update a user',
});
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
});
test('it renders the no resource type detected state when the current line has no detectable type', async function (assert) {
this.grantString = 'output_fields=*';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE).doesNotExist();
assert
.dom(GRANT_ACTIONS_NO_TYPE_DETECTED)
.hasText('No resource type detected.');
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
});
test('it renders the invalid ID and type state for an unknown type', async function (assert) {
this.grantString = 'type=not-a-real-type;actions=';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
assert
.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE)
.hasText('Invalid ID and type. No actions available.');
});
test('it renders the invalid ID and type state for an unknown ID', async function (assert) {
this.grantString = 'ids=bad_123;actions=';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
assert
.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE)
.hasText('Invalid ID and type. No actions available.');
});
test('it keeps the no suggestions state when a type is known but no actions apply', async function (assert) {
this.grantString = 'type=host;actions=';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
assert.dom(GRANT_ACTIONS_EMPTY_STATE).hasText('No suggestions');
});
test('it renders the no-type-detected state for an empty grant string', async function (assert) {
this.grantString = '';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE).doesNotExist();
assert
.dom(GRANT_ACTIONS_NO_TYPE_DETECTED)
.hasText('No resource type detected.');
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
});
test('it renders the invalid state for a pinned id paired with its own type instead of a child type', async function (assert) {
this.grantString = 'ids=hcst_1234567890;type=host-catalog';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
assert
.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE)
.hasText('Invalid ID and type. No actions available.');
});
test('it renders only CRUDL actions with fallback descriptions when type is wildcard', async function (assert) {
this.grantString = 'ids=*;type=*';
await render(hbs`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
assert.dom(GRANT_ACTIONS_TABLE).isVisible();
const rows = [...this.element.querySelectorAll(commonSelectors.TABLE_ROWS)];
const rowNames = rows.map((row) =>
row.querySelector('td')?.textContent.trim(),
);
assert.deepEqual(
rowNames,
['create', 'delete', 'list', 'read', 'update'],
'only CRUDL actions are shown for wildcard grants',
);
const createRow = rows.find(
(row) => row.querySelector('td')?.textContent.trim() === 'create',
);
assert.strictEqual(
createRow.querySelectorAll('td')[1].textContent.trim(),
'Creates resources',
'description falls back to generic when no resource type is detected',
);
});
});