Icu 17197 update logic in global roles to only add project scopes when using children (#2874)
* refactor: 💡 hide manage custom scopes button when using keyword * refactor: 💡 update to only allow projects for global roles * refactor: 💡 clean-up ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17197
This commit is contained in:
1 parent
57e614e8db
commit
eb365f0d39
9 files changed
+405
-100
No files matched your search
@@ -631,8 +631,11 @@ role:
|
||||
link: You can read more about it here
|
||||
manage-custom-scopes:
|
||||
title: Manage Custom Scopes
|
||||
description: Select which specific orgs you want to apply to this role's grant sets.
|
||||
description:
|
||||
0: Select which specific orgs you want to apply to this role's grant sets.
|
||||
1: Select specific projects from each org to apply to this role's grant sets. You cannot add specific orgs because applying "children" has already added them to this role.
|
||||
success: Custom scope selection saved
|
||||
tooltip: The org this project is under.
|
||||
manage-org-projects:
|
||||
title: Manage Projects in {orgDisplayName}
|
||||
description: Select which projects in this org you want to apply to this role's grant sets.
|
||||
|
||||
@@ -19,68 +19,153 @@
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' @handleSearchInput}}
|
||||
/>
|
||||
{{#unless @model.canSelectOrgs}}
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
name='org'
|
||||
@toggleText={{t 'resources.org.title'}}
|
||||
@itemOptions={{@orgScopes}}
|
||||
@checkedItems={{@orgs}}
|
||||
@applyFilter={{fn @applyFilter 'orgs'}}
|
||||
@isSearchable={{true}}
|
||||
/>
|
||||
</S.Generic>
|
||||
{{/unless}}
|
||||
</Hds::SegmentedGroup>
|
||||
{{#if @model.orgScopes}}
|
||||
<div class='table-selected-items-text'>
|
||||
{{#if @model.role.grantScopeOrgIDs.length}}
|
||||
<Hds::Text::Body @tag='p' @color='faint'>
|
||||
{{t
|
||||
'resources.role.scope.titles.selected'
|
||||
items=@model.role.grantScopeOrgIDs.length
|
||||
total=@model.totalItemsCount
|
||||
}}
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
</div>
|
||||
<Hds::Table
|
||||
@columns={{array
|
||||
(hash label=(t 'resources.role.scope.title'))
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label=(t 'resources.role.scope.titles.projects_selected'))
|
||||
}}
|
||||
@model={{@model.orgScopes}}
|
||||
@isSelectable={{true}}
|
||||
@onSelectionChange={{this.selectionChange}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr
|
||||
@selectionKey={{B.data.id}}
|
||||
@isSelected={{includes B.data.id @model.role.grantScopeOrgIDs}}
|
||||
@selectionAriaLabelSuffix='row {{B.data.id}}'
|
||||
data-test-org-scopes-table-row={{B.data.id}}
|
||||
>
|
||||
<B.Td>
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{B.data.displayName}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.id}}
|
||||
@color='secondary'
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#let (get @model.projectTotals B.data.id) as |projectTotals|}}
|
||||
{{#if projectTotals}}
|
||||
<Hds::Link::Standalone
|
||||
@icon='arrow-right'
|
||||
@iconPosition='trailing'
|
||||
@text={{t
|
||||
'resources.role.scope.actions.view-projects'
|
||||
selected=projectTotals.selected.length
|
||||
total=projectTotals.total
|
||||
}}
|
||||
@route='scopes.scope.roles.role.manage-scopes.manage-org-projects'
|
||||
@model={{B.data.id}}
|
||||
/>
|
||||
|
||||
{{#if @model.scopes}}
|
||||
{{#if @model.canSelectOrgs}}
|
||||
<div class='table-selected-items-text'>
|
||||
{{#if @model.role.grantScopeOrgIDs.length}}
|
||||
<Hds::Text::Body @tag='p' @color='faint'>
|
||||
{{t
|
||||
'resources.role.scope.titles.selected'
|
||||
items=@model.role.grantScopeOrgIDs.length
|
||||
total=@model.totalItemsCount
|
||||
}}
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<Hds::Table
|
||||
@columns={{array
|
||||
(hash label=(t 'resources.role.scope.title'))
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label=(t 'resources.role.scope.titles.projects_selected'))
|
||||
}}
|
||||
@model={{@model.scopes}}
|
||||
@isSelectable={{true}}
|
||||
@onSelectionChange={{this.orgSelectionChange}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr
|
||||
@selectionKey={{B.data.id}}
|
||||
@isSelected={{includes B.data.id @model.role.grantScopeOrgIDs}}
|
||||
@selectionAriaLabelSuffix='row {{B.data.id}}'
|
||||
data-test-org-scopes-table-row={{B.data.id}}
|
||||
>
|
||||
<B.Td>
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{B.data.displayName}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.id}}
|
||||
@color='secondary'
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#let (get @model.projectTotals B.data.id) as |projectTotals|}}
|
||||
{{#if projectTotals}}
|
||||
<Hds::Link::Standalone
|
||||
@icon='arrow-right'
|
||||
@iconPosition='trailing'
|
||||
@text={{t
|
||||
'resources.role.scope.actions.view-projects'
|
||||
selected=projectTotals.selected.length
|
||||
total=projectTotals.total
|
||||
}}
|
||||
@route='scopes.scope.roles.role.manage-scopes.manage-org-projects'
|
||||
@model={{B.data.id}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{else}}
|
||||
<FilterTags @filters={{@filters}} />
|
||||
|
||||
<div class='table-selected-items-text'>
|
||||
{{#if @model.role.grantScopeProjectIDs.length}}
|
||||
<Hds::Text::Body @tag='p' @color='faint'>
|
||||
{{t
|
||||
'resources.role.scope.titles.selected'
|
||||
items=@model.role.grantScopeProjectIDs.length
|
||||
total=@model.totalItemsCount
|
||||
}}
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<Hds::Table
|
||||
@columns={{array
|
||||
(hash label=(t 'resources.project.title'))
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash
|
||||
label=(t 'resources.org.title')
|
||||
tooltip=(t
|
||||
'resources.role.scope.messages.manage-custom-scopes.tooltip'
|
||||
)
|
||||
)
|
||||
}}
|
||||
@model={{@model.scopes}}
|
||||
@isSelectable={{true}}
|
||||
@onSelectionChange={{this.projectSelectionChange}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr
|
||||
@selectionKey={{B.data.id}}
|
||||
@isSelected={{includes
|
||||
B.data.id
|
||||
@model.role.grantScopeProjectIDs
|
||||
}}
|
||||
@selectionAriaLabelSuffix='row {{B.data.id}}'
|
||||
data-test-project-scopes-table-row={{B.data.id}}
|
||||
>
|
||||
<B.Td>
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{B.data.displayName}}
|
||||
</Hds::Text::Body>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.id}}
|
||||
@color='secondary'
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if (can 'read model' B.data)}}
|
||||
<Hds::Link::Inline
|
||||
@route='scopes.scope'
|
||||
@model={{B.data.scope.id}}
|
||||
@icon='org'
|
||||
@iconPosition='leading'
|
||||
>
|
||||
{{~or B.data.scope.name B.data.scope.id}}
|
||||
</Hds::Link::Inline>
|
||||
{{else}}
|
||||
{{or B.data.scope.name B.data.scope.id}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{/if}}
|
||||
<Rose::Pagination
|
||||
@model={{@model.role.id}}
|
||||
@totalItems={{@model.totalItems}}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class FormRoleManageCustomScopesIndexComponent extends Component
|
||||
* @type {string}
|
||||
*/
|
||||
get orgDisplayName() {
|
||||
const org = this.args.model.orgScopes.find(
|
||||
const org = this.args.model.scopes.find(
|
||||
({ id }) => id === this.selectedOrg,
|
||||
);
|
||||
return org.displayName;
|
||||
@@ -27,12 +27,12 @@ export default class FormRoleManageCustomScopesIndexComponent extends Component
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handles the selection changes for the paginated table.
|
||||
* Handles the org selection changes for the paginated table.
|
||||
* @param {object} selectableRowsStates
|
||||
* @param {object} selectionKey
|
||||
*/
|
||||
@action
|
||||
selectionChange({ selectableRowsStates, selectionKey }) {
|
||||
orgSelectionChange({ selectableRowsStates, selectionKey }) {
|
||||
const { role, projectTotals } = this.args.model;
|
||||
|
||||
const addOrRemoveValues = (add, remove, orgId) => {
|
||||
@@ -109,4 +109,24 @@ export default class FormRoleManageCustomScopesIndexComponent extends Component
|
||||
toggleRemoveAllModal() {
|
||||
this.selectedOrgs = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the project selection changes for the paginated table.
|
||||
* @param {object} selectableRowsStates
|
||||
*/
|
||||
@action
|
||||
projectSelectionChange({ selectableRowsStates }) {
|
||||
const { role } = this.args.model;
|
||||
selectableRowsStates.forEach((row) => {
|
||||
const { isSelected, selectionKey: key } = row;
|
||||
const includesId = role.grant_scope_ids.includes(key);
|
||||
if (isSelected && !includesId) {
|
||||
role.grant_scope_ids = [...role.grant_scope_ids, key];
|
||||
} else if (!isSelected && includesId) {
|
||||
role.grant_scope_ids = role.grant_scope_ids.filter(
|
||||
(item) => item !== key,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+44
-1
@@ -19,12 +19,43 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesControlle
|
||||
|
||||
// =attributes
|
||||
|
||||
queryParams = ['search', 'page', 'pageSize'];
|
||||
queryParams = ['search', { orgs: { type: 'array' } }, 'page', 'pageSize'];
|
||||
|
||||
@tracked search;
|
||||
@tracked orgs = [];
|
||||
@tracked page = 1;
|
||||
@tracked pageSize = 10;
|
||||
|
||||
/**
|
||||
* Returns unique org scopes from projects.
|
||||
* @returns {[object]}
|
||||
*/
|
||||
get orgScopes() {
|
||||
const uniqueMap = new Map();
|
||||
this.model.allProjects.forEach(({ scope: { id, name } }) => {
|
||||
if (!uniqueMap.has(id)) {
|
||||
const orgName = name || id;
|
||||
uniqueMap.set(id, { id, name: orgName });
|
||||
}
|
||||
});
|
||||
return Array.from(uniqueMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filters object used for displaying filter tags.
|
||||
* @type {object}
|
||||
*/
|
||||
get filters() {
|
||||
return {
|
||||
allFilters: {
|
||||
orgs: this.orgScopes,
|
||||
},
|
||||
selectedFilters: {
|
||||
orgs: this.orgs,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
@@ -38,6 +69,18 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesControlle
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query param to the value of selectedItems
|
||||
* and resets the page to 1.
|
||||
* @param {string} paramKey
|
||||
* @param {[string]} selectedItems
|
||||
*/
|
||||
@action
|
||||
applyFilter(paramKey, selectedItems) {
|
||||
this[paramKey] = [...selectedItems];
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save grant scope IDs to current role via the API.
|
||||
* @param {RoleModel} role
|
||||
|
||||
@@ -9,6 +9,7 @@ import { service } from '@ember/service';
|
||||
import { TrackedObject } from 'tracked-built-ins';
|
||||
import { TYPE_SCOPE_PROJECT } from 'api/models/scope';
|
||||
import { restartableTask, timeout } from 'ember-concurrency';
|
||||
import { GRANT_SCOPE_CHILDREN } from 'api/models/role';
|
||||
|
||||
export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute extends Route {
|
||||
// =attributes
|
||||
@@ -18,6 +19,10 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
orgs: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
@@ -36,7 +41,7 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
|
||||
|
||||
/**
|
||||
* Loads sub scopes for the current scope.
|
||||
* @returns {Promise<{role: RoleModel, orgScopes: [ScopeModel], projectTotals: object, totalItems: number, totalItemsCount: number}> }
|
||||
* @returns {Promise<{role: RoleModel, orgScopes: [ScopeModel], projectTotals: object, totalItems: number, totalItemsCount: number, canSelectOrgs: boolean}> }
|
||||
*/
|
||||
async model(params) {
|
||||
const useDebounce =
|
||||
@@ -45,46 +50,75 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
|
||||
}
|
||||
|
||||
retrieveData = restartableTask(
|
||||
async ({ search, page, pageSize, useDebounce }) => {
|
||||
async ({ search, orgs, page, pageSize, useDebounce }) => {
|
||||
if (useDebounce) {
|
||||
await timeout(250);
|
||||
}
|
||||
|
||||
const role = this.modelFor('scopes.scope.roles.role');
|
||||
const currentScope = this.modelFor('scopes.scope');
|
||||
const { id: scope_id } = currentScope;
|
||||
const filters = {
|
||||
scope_id: [{ equals: scope_id }],
|
||||
};
|
||||
const canSelectOrgs =
|
||||
!role.grant_scope_ids.includes(GRANT_SCOPE_CHILDREN);
|
||||
|
||||
const orgScopes = await this.store.query('scope', {
|
||||
scope_id,
|
||||
let filters;
|
||||
if (canSelectOrgs) {
|
||||
filters = { scope_id: [{ equals: 'global' }] };
|
||||
} else {
|
||||
filters = { type: [{ equals: TYPE_SCOPE_PROJECT }], scope_id: [] };
|
||||
orgs.forEach((org) => {
|
||||
filters.scope_id.push({ equals: org });
|
||||
});
|
||||
}
|
||||
const scopes = await this.store.query('scope', {
|
||||
scope_id: 'global',
|
||||
query: { search, filters },
|
||||
page,
|
||||
pageSize,
|
||||
recursive: true,
|
||||
});
|
||||
const totalItems = orgScopes.meta?.totalItems;
|
||||
|
||||
let projectTotals;
|
||||
let allProjects;
|
||||
if (canSelectOrgs) {
|
||||
projectTotals = await this.getProjectTotals(role.grantScopeProjectIDs);
|
||||
} else {
|
||||
allProjects = await this.getAllProjects();
|
||||
}
|
||||
|
||||
const totalItems = scopes.meta?.totalItems;
|
||||
const totalItemsCount = await this.getTotalItemsCount(
|
||||
scope_id,
|
||||
search,
|
||||
totalItems,
|
||||
);
|
||||
|
||||
const projectTotals = await this.getProjectTotals(
|
||||
role.grantScopeProjectIDs,
|
||||
canSelectOrgs,
|
||||
);
|
||||
|
||||
return {
|
||||
role,
|
||||
orgScopes,
|
||||
scopes,
|
||||
projectTotals,
|
||||
allProjects,
|
||||
totalItems,
|
||||
totalItemsCount,
|
||||
canSelectOrgs,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves
|
||||
* @returns {Promise<[ScopeModel]>}
|
||||
*/
|
||||
async getAllProjects() {
|
||||
const options = { pushToStore: false, peekIndexedDB: true };
|
||||
return this.store.query(
|
||||
'scope',
|
||||
{
|
||||
scope_id: 'global',
|
||||
query: { filters: { type: [{ equals: TYPE_SCOPE_PROJECT }] } },
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that contains the number of selected projects
|
||||
* and the total number of projects for each org scope.
|
||||
@@ -92,15 +126,7 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
|
||||
* @returns {object}
|
||||
*/
|
||||
async getProjectTotals(projectIDs) {
|
||||
const options = { pushToStore: false, peekIndexedDB: true };
|
||||
const projects = await this.store.query(
|
||||
'scope',
|
||||
{
|
||||
scope_id: 'global',
|
||||
query: { filters: { type: [{ equals: TYPE_SCOPE_PROJECT }] } },
|
||||
},
|
||||
options,
|
||||
);
|
||||
const projects = await this.getAllProjects();
|
||||
|
||||
// We want this object to be tracked so that changes to this object
|
||||
// cause the "Projects selected" column to re-render with updates.
|
||||
@@ -121,25 +147,24 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
|
||||
/**
|
||||
* Sets scopesExist to true if there exists any scopes and
|
||||
* sets totalItemsCount to the number of scopes that exist.
|
||||
* @param {string} scope_id
|
||||
* @param {string} search
|
||||
* @param {number} totalItems
|
||||
* @param {boolean} canSelectOrgs
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
async getTotalItemsCount(scope_id, search, totalItems) {
|
||||
async getTotalItemsCount(search, totalItems, canSelectOrgs) {
|
||||
if (!search) {
|
||||
return totalItems;
|
||||
}
|
||||
const options = { pushToStore: false, peekIndexedDB: true };
|
||||
const filters = canSelectOrgs
|
||||
? { scope_id: [{ equals: 'global' }] }
|
||||
: { type: [{ equals: TYPE_SCOPE_PROJECT }] };
|
||||
const scopes = await this.store.query(
|
||||
'scope',
|
||||
{
|
||||
scope_id,
|
||||
query: {
|
||||
filters: {
|
||||
scope_id: [{ equals: scope_id }],
|
||||
},
|
||||
},
|
||||
scope_id: 'global',
|
||||
query: { filters },
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
},
|
||||
|
||||
@@ -904,6 +904,10 @@
|
||||
}
|
||||
|
||||
.role-manage-custom-scopes-form {
|
||||
.filters-applied {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.table-selected-items-text {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
+13
-1
@@ -21,7 +21,15 @@
|
||||
<DocLink @doc='role.add-grant-scopes' />
|
||||
</PH.Title>
|
||||
<PH.Description>
|
||||
{{t 'resources.role.scope.messages.manage-custom-scopes.description'}}
|
||||
{{#if @model.canSelectOrgs}}
|
||||
{{t
|
||||
'resources.role.scope.messages.manage-custom-scopes.description.0'
|
||||
}}
|
||||
{{else}}
|
||||
{{t
|
||||
'resources.role.scope.messages.manage-custom-scopes.description.1'
|
||||
}}
|
||||
{{/if}}
|
||||
</PH.Description>
|
||||
</Hds::PageHeader>
|
||||
</page.header>
|
||||
@@ -30,9 +38,13 @@
|
||||
<Form::Role::ManageCustomScopes
|
||||
@model={{@model}}
|
||||
@search={{this.search}}
|
||||
@filters={{this.filters}}
|
||||
@orgs={{this.orgs}}
|
||||
@orgScopes={{this.orgScopes}}
|
||||
@page={{this.page}}
|
||||
@pageSize={{this.pageSize}}
|
||||
@handleSearchInput={{this.handleSearchInput}}
|
||||
@applyFilter={{this.applyFilter}}
|
||||
@submit={{fn this.setGrantScopes @model.role}}
|
||||
@cancel={{fn this.cancel @model.role}}
|
||||
/>
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
@icon='org'
|
||||
@iconPosition='leading'
|
||||
>
|
||||
{{~if B.data.scope.name B.data.scope.name B.data.scope.id}}
|
||||
{{~or B.data.scope.name B.data.scope.id}}
|
||||
</Hds::Link::Inline>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
findAll,
|
||||
fillIn,
|
||||
waitUntil,
|
||||
waitFor,
|
||||
} from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
@@ -714,6 +715,118 @@ module('Acceptance | roles | global-scope', function (hooks) {
|
||||
.includesText('No results found');
|
||||
});
|
||||
|
||||
test('user can select projects when `children` is applied on manage custom scopes page', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [GRANT_SCOPE_CHILDREN] });
|
||||
await visit(urls.role);
|
||||
|
||||
await click(commonSelectors.HREF(urls.roleScopes));
|
||||
|
||||
assert.dom(selectors.GRANT_SCOPE_ROW(GRANT_SCOPE_CHILDREN)).isVisible();
|
||||
|
||||
await click(selectors.MANAGE_DROPDOWN_ROLES);
|
||||
await click(selectors.MANAGE_DROPDOWN_ROLES_SCOPES);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.manageScopes);
|
||||
|
||||
await click(commonSelectors.HREF(urls.manageCustomScopes));
|
||||
|
||||
assert.strictEqual(currentURL(), urls.manageCustomScopes);
|
||||
|
||||
// Click three times to select, unselect, then reselect (for coverage)
|
||||
const projectID = instances.scopes.project.id;
|
||||
await click(selectors.SCOPE_CHECKBOX('project', projectID));
|
||||
await click(selectors.SCOPE_CHECKBOX('project', projectID));
|
||||
await click(selectors.SCOPE_CHECKBOX('project', projectID));
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.manageScopes);
|
||||
assert.dom(selectors.MANAGE_CUSTOM_SCOPES_BUTTON_ICON).isVisible();
|
||||
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.roleScopes);
|
||||
assert.dom(commonSelectors.TABLE_ROWS).exists({ count: 2 });
|
||||
assert.dom(selectors.GRANT_SCOPE_ROW(GRANT_SCOPE_CHILDREN)).isVisible();
|
||||
assert.dom(selectors.GRANT_SCOPE_ROW(projectID)).isVisible();
|
||||
});
|
||||
|
||||
test('user can search for a specific project scope by id on manage custom scopes page', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [GRANT_SCOPE_CHILDREN] });
|
||||
const anotherProject = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: { id: instances.scopes.org.id, type: 'org' },
|
||||
});
|
||||
await visit(urls.manageScopes);
|
||||
|
||||
await click(commonSelectors.HREF(urls.manageCustomScopes));
|
||||
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', instances.scopes.project.id))
|
||||
.isVisible();
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', anotherProject.id))
|
||||
.isVisible();
|
||||
|
||||
await fillIn(commonSelectors.SEARCH_INPUT, instances.scopes.project.id);
|
||||
await waitFor(selectors.SCOPE_CHECKBOX('project', anotherProject.id), {
|
||||
count: 0,
|
||||
});
|
||||
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', instances.scopes.project.id))
|
||||
.isVisible();
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', anotherProject.id))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('user can search for a specific project scope by id and get no results on manage custom scopes page', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [GRANT_SCOPE_CHILDREN] });
|
||||
await visit(urls.manageScopes);
|
||||
|
||||
await click(commonSelectors.HREF(urls.manageCustomScopes));
|
||||
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', instances.scopes.project.id))
|
||||
.isVisible();
|
||||
|
||||
await fillIn(
|
||||
commonSelectors.SEARCH_INPUT,
|
||||
'fake project scope that does not exist',
|
||||
);
|
||||
await waitFor(selectors.NO_RESULTS_GRANT_SCOPE_MSG, { count: 1 });
|
||||
|
||||
assert
|
||||
.dom(selectors.NO_RESULTS_GRANT_SCOPE_MSG)
|
||||
.includesText('No results found');
|
||||
});
|
||||
|
||||
test('user can filter a project scope by parent scope on manage custom scopes page', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [GRANT_SCOPE_CHILDREN] });
|
||||
const anotherOrg = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: { id: 'global', type: 'global' },
|
||||
});
|
||||
const anotherProject = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: { id: anotherOrg.id, type: 'org' },
|
||||
});
|
||||
await visit(urls.manageScopes);
|
||||
|
||||
await click(commonSelectors.HREF(urls.manageCustomScopes));
|
||||
|
||||
assert.dom(commonSelectors.TABLE_ROWS).exists({ count: 2 });
|
||||
|
||||
await click(commonSelectors.FILTER_DROPDOWN('org'));
|
||||
await click(commonSelectors.FILTER_DROPDOWN_ITEM(anotherOrg.id));
|
||||
await click(commonSelectors.FILTER_DROPDOWN_ITEM_APPLY_BTN('org'));
|
||||
|
||||
assert.dom(commonSelectors.TABLE_ROWS).exists({ count: 1 });
|
||||
assert
|
||||
.dom(selectors.SCOPE_CHECKBOX('project', anotherProject.id))
|
||||
.isVisible();
|
||||
});
|
||||
|
||||
test('user can save custom scopes to add on manage org projects page', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [] });
|
||||
await visit(urls.role);
|
||||
|
||||
Reference in new issue
Block a user