Merge branch 'checkbox-style-updates' of github.com:hashicorp/boundary-ui into checkbox-style-updates

This commit is contained in:
dharini committed 2021-11-12 11:33:04 -08:00
commit 3191197cbf
26 files changed
+796 -28

No files matched your search

@@ -1,3 +1,10 @@
import ApplicationSerializer from './application';
export default class ManagedGroupSerializer extends ApplicationSerializer {}
export default class ManagedGroupSerializer extends ApplicationSerializer {
// =properties
/**
* @type {boolean}
*/
serializeScopeID = false;
}
@@ -10,7 +10,7 @@ import { inject as service } from '@ember/service';
* type: 'oidc',
* status: ['active', 'pending']
* });
* console.log(filter.queryExpression);
* log(filter.queryExpression);
* // ("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending")
*/
export class ResourceFilter {
@@ -0,0 +1 @@
export { default } from 'api/serializers/managed-group';
@@ -15,6 +15,7 @@ export default factory.extend({
authorized_collection_actions: () => {
return {
accounts: ['create', 'list'],
'managed-groups': ['create', 'list'],
};
},
id: (i) => `auth-method-id-${i}`,
@@ -0,0 +1,86 @@
/**
* A resource filter param is a concise way to express a route query parameter.
* Resource filter params abstract away a route's query parameter
* interface, treating a query param as a simple attribute on a route instance.
* Getting the attribute value is equivalent to looking up the associated query
* parameter. Setting the attribute is equivalent to transitioning to the route
* and passing the associated query parameter value.
*
* Additionally, resource filter params may declare `allowedValues`, an array
* containing the set of values the parameter may take. While unenforced,
* `allowedValues` are useful metadata about a resource filter and may be fetch
* via `ResourceFilterParamHelper`. An optional default value may be specified
* via `defaultValue` passed to the decorator.
*
* @see ResourceFilterParamHelper
*
* @example
*
* export default class MyRoute extends Route {
*
* @resourceFilterParam(['active', 'pending'], ['active'])
* status;
*
* @action
* changeStatus(status) {
* // Sets query param `filter-status` on the current route, which occurs
* // via a route transition.
* this.status = status;
* // Log the value of query param `filter-status`.
* log(this.status);
* }
* }
*
*/
export function resourceFilterParam(allowedValues, defaultValue) {
/**
* @param {object} target
* @param {string} name
* @param {object} descriptor
* @return {object{get, set}}
*/
return function (target, name /*, descriptor*/) {
const filterKey = `filter-${name}`;
const filterAllowedValuesKey = `filter-allowed-values-${name}`;
// If the route has no query params specified yet, declare them.
if (!Object.keys(target.queryParams).length) target.queryParams = {};
// Add the resource filter query parameter to the route. Resource filter
// param changes refresh the model but do not contribute to browser history.
// See https://guides.emberjs.com/release/routing/query-params/
target.queryParams[filterKey] = {
refreshModel: true,
replace: true,
};
// Store the allowed values for future lookup
// (via `ResourceFilterParamHelper`).
target[filterAllowedValuesKey] = allowedValues;
// Override the decorated attribute with a getter and setter.
return {
/**
* Returns the JSON-parsed query parameter value OR defaultValue.
*/
get() {
const value =
this.paramsFor(this.routeName)[filterKey] ||
JSON.stringify(defaultValue);
return value ? JSON.parse(value) : null;
},
/**
* Sets the resource filter param to the stringified representation
* of `value`. NOTE: this is equivalent to transitioning into the
* current route, changing the associated query param.
* @param value
*/
set(value) {
const queryParams = {};
queryParams[filterKey] = JSON.stringify(value);
this.transitionTo({ queryParams });
},
};
};
}
@@ -0,0 +1,73 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { action } from '@ember/object';
/**
* Use this helper to fetch the current value of a resource filter param,
* as well as its allowedValues.
*
* @see resourceFilterParam (decorator)
*
* @example
*
* {{#with resource-filter-param 'status' as |param|}}
* {{param.name}} / {{param.value}} / {{param.selectedValue}}
* {{/with}}
*/
export default class ResourceFilterParamHelper extends Helper {
// =services
@service router;
// =methods
// =lifecycle management methods
/**
* Recompute this helper when the route changes.
*/
constructor() {
super(...arguments);
this.router.on('routeDidChange', this.routeDidChange);
}
/**
* Stop listening to route events when this helper is destroyed.
*/
willDestroy() {
this.router.off('routeDidChange', this.routeDidChange);
super.willDestroy();
}
// =compute method
/**
* Returns an object containing `name`, `value`, and `selectedValue`.
* `name` - The name of the resource filter param.
* `value` - The value of the resource filter param
* @return {object}
*/
compute([routeName, name]) {
const owner = getOwner(this);
// Filter options
const route = owner.lookup(`route:${routeName}`);
const filterAllowedValuesKey = `filter-allowed-values-${name}`;
const allowedValues = route[filterAllowedValuesKey];
// Selected filters
const rawValue = route[name];
const selectedValue = rawValue || null;
return { name, allowedValues, selectedValue };
}
// =actions
/**
* Triggers a recompute (see above) when the route changes, especially when
* route query params change.
*/
@action
routeDidChange() {
this.recompute();
}
}
@@ -0,0 +1 @@
export { default } from 'core/decorators/resource-filter-param';
@@ -0,0 +1 @@
export { default } from 'core/helpers/resource-filter-param';
@@ -358,3 +358,7 @@ managed-group:
actions:
delete: Delete Managed Group
create: Create Managed Group
messages:
welcome:
title: Welcome to Managed Groups
description: Create and manage managed groups for an auth method.
@@ -69,11 +69,12 @@
</field.errors>
{{/if}}
</form.input>
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{#if (can 'save model' @model)}}
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{/if}}
</Rose::Form>
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated managed groups breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('resources.managed-group.title_plural');
}
}
@@ -0,0 +1,13 @@
import Controller from '@ember/controller';
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupController extends Controller {
// =attributes
/**
* A managed group breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.model.displayName;
}
}
@@ -0,0 +1,17 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsNewController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated new managed group breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('titles.new');
}
}
@@ -21,7 +21,13 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends
model() {
const authMethod = this.modelFor('scopes.scope.auth-methods.auth-method');
const { id: auth_method_id } = authMethod;
return this.store.query('managed-group', { auth_method_id });
const canListManagedGroups = this.can.can('list collection', authMethod, {
collection: 'managed-groups',
});
if (canListManagedGroups) {
return this.store.query('managed-group', { auth_method_id });
}
}
// =actions
@@ -50,9 +56,16 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends
)
async save(managedGroup) {
await managedGroup.save();
await this.transitionTo(
'scopes.scope.auth-methods.auth-method.managed-groups'
);
if (this.can.can('read model', managedGroup)) {
await this.transitionTo(
'scopes.scope.auth-methods.auth-method.managed-groups.managed-group',
managedGroup
);
} else {
await this.transitionTo(
'scopes.scope.auth-methods.auth-method.managed-groups'
);
}
this.refresh();
}
@@ -65,7 +78,7 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends
@confirm('questions.delete-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.delete-success')
async deleteManagedGroup(managedGroup) {
async delete(managedGroup) {
await managedGroup.destroyRecord();
await this.replaceWith(
'scopes.scope.auth-methods.auth-method.managed-groups'
@@ -68,14 +68,15 @@
{{/if}}
{{#if (feature-flag 'managed-groups')}}
{{! TODO: Add capabilities }}
{{#if @model.isOIDC}}
<dropdown.link
@route='scopes.scope.auth-methods.auth-method.managed-groups.new'
@model={{@model}}
>
{{t 'resources.managed-group.actions.create'}}
</dropdown.link>
{{#if (can 'create collection' @model collection='managed-groups')}}
{{#if @model.isOIDC}}
<dropdown.link
@route='scopes.scope.auth-methods.auth-method.managed-groups.new'
@model={{@model}}
>
{{t 'resources.managed-group.actions.create'}}
</dropdown.link>
{{/if}}
{{/if}}
{{/if}}
@@ -10,10 +10,12 @@
{{/if}}
{{#if (feature-flag 'managed-groups')}}
{{#if @model.isOIDC}}
<nav.link @route='scopes.scope.auth-methods.auth-method.managed-groups'>
{{t 'resources.managed-group.title_plural'}}
</nav.link>
{{#if (can 'navigate collection' @model collection='managed-groups')}}
{{#if @model.isOIDC}}
<nav.link @route='scopes.scope.auth-methods.auth-method.managed-groups'>
{{t 'resources.managed-group.title_plural'}}
</nav.link>
{{/if}}
{{/if}}
{{/if}}
</Rose::Nav::Tabs>
@@ -41,4 +41,46 @@
{{/each}}
</table.body>
</Rose::Table>
{{else}}
<Rose::Layout::Centered>
<Rose::Message
@title={{t 'resources.managed-group.messages.welcome.title'}}
as |message|
>
<message.description>
{{#if
(can 'list collection' this.authMethod collection='managed-groups')
}}
{{! Can list (at least): show default welcome message }}
{{t 'resources.managed-group.description'}}
{{else if (can
'create collection' this.authMethod collection='managed-groups'
)}}
{{! Can create only: show create but not list welcome message }}
{{t
'descriptions.create-but-not-list'
resource=(t 'resources.managed-group.title_plural')
}}
{{else}}
{{! Can neither list nor create }}
{{t
'descriptions.neither-list-nor-create'
resource=(t 'resources.managed-group.title_plural')
}}
{{/if}}
</message.description>
{{#if
(can 'create collection' this.authMethod collection='managed-groups')
}}
<message.link
@route='scopes.scope.auth-methods.auth-method.managed-groups.new'
>
<Rose::Icon @name='flight-icons/svg/plus-circle-16' />
{{t 'resources.managed-group.actions.create'}}
</message.link>
{{/if}}
</Rose::Message>
</Rose::Layout::Centered>
{{/if}}
@@ -7,7 +7,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'deleteManagedGroup' @model)}}
{{on 'click' (route-action 'delete' @model)}}
>
{{t 'resources.managed-group.actions.delete'}}
</dropdown.button>
@@ -1 +1,5 @@
<Form::ManagedGroup @model={{@model}} />
<Form::ManagedGroup
@model={{@model}}
@submit={{route-action 'save' @model}}
@cancel={{route-action 'cancel' @model}}
/>
@@ -0,0 +1,160 @@
import { module, test } from 'qunit';
import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { authenticateSession } from 'ember-simple-auth/test-support';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { Response } from 'miragejs';
module('Acceptance | managed-groups | create', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
const instances = {
scopes: {
global: null,
org: null,
},
authMethod: null,
managedGroup: null,
};
const urls = {
orgScope: null,
authMethods: null,
managedGroups: null,
newManagedGroup: null,
managedGroup: null,
};
hooks.beforeEach(function () {
authenticateSession({});
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
instances.managedGroup = this.server.create('managed-group', {
scope: instances.scopes.org,
authMethod: instances.authMethod,
});
// Generate route URLs for resources
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
urls.managedGroups = `${urls.authMethod}/managed-groups`;
urls.newManagedGroup = `${urls.managedGroups}/new`;
urls.managedGroup = `${urls.managedGroups}/${instances.authMethod.id}`;
});
hooks.afterEach(async function () {
const notification = find('.rose-notification');
if (notification) {
await click('.rose-notification-dismiss');
}
});
test('visiting managed groups', async function (assert) {
assert.expect(1);
await visit(urls.managedGroups);
await a11yAudit();
assert.equal(currentURL(), urls.managedGroups);
});
test('can create a new managed group', async function (assert) {
assert.expect(1);
const managedGroupsCount = this.server.db.managedGroups.length;
await visit(urls.newManagedGroup);
await fillIn('[name="name"]', 'Managed group name');
await fillIn('[name="description"]', 'description');
await click('form [type="submit"]:not(:disabled)');
assert.equal(this.server.db.managedGroups.length, managedGroupsCount + 1);
});
test('User can not create a new managed group without proper authorization', async function (assert) {
assert.expect(2);
instances.authMethod.authorized_collection_actions['managed-groups'] = [];
await visit(urls.authMethod);
assert.notOk(
instances.authMethod.authorized_collection_actions[
'managed-groups'
].includes('create')
);
assert.notOk(
find(`.rose-layout-page-actions [href="${urls.newManagedGroup}"]`)
);
});
test('User can navigate to new managed group route with proper authorization', async function (assert) {
assert.expect(2);
await visit(urls.managedGroups);
assert.ok(
instances.authMethod.authorized_collection_actions[
'managed-groups'
].includes('create')
);
assert.ok(find(`[href="${urls.newManagedGroup}"]`));
});
test('User cannot navigate to new managed group route without proper authorization', async function (assert) {
assert.expect(2);
instances.authMethod.authorized_collection_actions['managed-groups'] = [];
await visit(urls.managedGroups);
assert.notOk(
instances.authMethod.authorized_collection_actions[
'managed-groups'
].includes('create')
);
assert.notOk(find(`[href="${urls.newManagedGroup}"]`));
});
test('User can cancel a new managed group creation', async function (assert) {
assert.expect(2);
const managedGroupsCount = this.server.db.managedGroups.length;
await visit(urls.newManagedGroup);
await fillIn('[name="name"]', 'Managed group name');
await click('form button:not([type="submit"])');
assert.equal(this.server.db.managedGroups.length, managedGroupsCount);
assert.equal(currentURL(), urls.managedGroups);
});
test('When user saving a new managed group with invalid fields displays error message', async function (assert) {
assert.expect(2);
this.server.post('/managed-groups', () => {
return new Response(
400,
{},
{
status: 400,
code: 'invalid_argument',
message: 'The request was invalid.',
details: {
request_fields: [
{
name: 'name',
description: 'Name is required.',
},
],
},
}
);
});
await visit(urls.newManagedGroup);
await fillIn('[name="name"]', 'new managed group');
await click('form [type="submit"]');
await a11yAudit();
assert.equal(
find('.rose-notification-body').textContent.trim(),
'The request was invalid.',
'Displays primary error message.'
);
assert.equal(
find('.rose-form-error-message').textContent.trim(),
'Name is required.',
'Displays field-level errors.'
);
});
});
@@ -0,0 +1,103 @@
import { module, test } from 'qunit';
import { visit, click, find } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { authenticateSession } from 'ember-simple-auth/test-support';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { Response } from 'miragejs';
module('Acceptance | managed-groups | delete', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
const instances = {
scopes: {
global: null,
org: null,
},
authMethod: null,
managedGroup: null,
};
const urls = {
orgScope: null,
authMethods: null,
managedGroups: null,
newManagedGroup: null,
managedGroup: null,
};
hooks.beforeEach(function () {
authenticateSession({});
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
instances.managedGroup = this.server.create('managed-group', {
scope: instances.scopes.org,
authMethod: instances.authMethod,
});
// Generate route URLs for resources
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
urls.managedGroups = `${urls.authMethod}/managed-groups`;
urls.newManagedGroup = `${urls.managedGroups}/new`;
// TODO refactor in the rest of the test this url
urls.managedGroup = `${urls.managedGroups}/${instances.managedGroup.id}`;
});
hooks.afterEach(async function () {
const notification = find('.rose-notification');
if (notification) {
await click('.rose-notification-dismiss');
}
});
test('User can delete a managed group', async function (assert) {
assert.expect(1);
const managedGroupsCount = this.server.db.managedGroups.length;
await visit(urls.managedGroup);
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(this.server.db.managedGroups.length, managedGroupsCount - 1);
});
test('User cannot delete a managed-group without proper authorization', async function (assert) {
assert.expect(1);
instances.managedGroup.authorized_actions =
instances.managedGroup.authorized_actions.filter(
(item) => item !== 'delete'
);
await visit(urls.managedGroup);
assert.notOk(
find('.rose-layout-page-actions .rose-dropdown-button-danger')
);
});
test('Errors are displayed when delete on managed group fails', async function (assert) {
assert.expect(1);
this.server.del('/managed-groups/:id', () => {
return new Response(
490,
{},
{
status: 490,
code: 'error',
message: 'Oops.',
}
);
});
await visit(urls.managedGroup);
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
await a11yAudit();
assert.equal(
find('.rose-notification-body').textContent.trim(),
'Oops.',
'Displays primary error message.'
);
});
});
@@ -0,0 +1,92 @@
import { module, test } from 'qunit';
import { visit, click, find } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { authenticateSession } from 'ember-simple-auth/test-support';
module('Acceptance | managed-groups | list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
const instances = {
scopes: {
global: null,
org: null,
},
authMethod: null,
managedGroup: null,
};
const urls = {
orgScope: null,
authMethods: null,
managedGroups: null,
newManagedGroup: null,
managedGroup: null,
};
hooks.beforeEach(function () {
authenticateSession({});
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
instances.managedGroup = this.server.create('managed-group', {
scope: instances.scopes.org,
authMethod: instances.authMethod,
});
// Generate route URLs for resources
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
urls.managedGroups = `${urls.authMethod}/managed-groups`;
urls.newManagedGroup = `${urls.managedGroups}/new`;
urls.managedGroup = `${urls.managedGroups}/${instances.authMethod.id}`;
});
hooks.afterEach(async function () {
const notification = find('.rose-notification');
if (notification) {
await click('.rose-notification-dismiss');
}
});
test('User can navigate to managed groups with proper authorization', async function (assert) {
assert.expect(2);
await visit(urls.authMethod);
assert.ok(
instances.authMethod.authorized_collection_actions[
'managed-groups'
].includes('list')
);
assert.ok(find(`[href="${urls.managedGroups}"]`));
});
test('User cannot navigate to index without either list or create actions', async function (assert) {
assert.expect(2);
instances.authMethod.authorized_collection_actions['managed-groups'] = [];
await visit(urls.authMethod);
assert.notOk(
instances.authMethod.authorized_collection_actions[
'managed-groups'
].includes('list')
);
assert.notOk(find(`[href="${urls.managedGroups}"]`));
});
test('User can navigate to index with only create action', async function (assert) {
assert.expect(2);
instances.authMethod.authorized_collection_actions['managed-groups'] = [
'create',
];
await visit(urls.authMethod);
assert.ok(find(`[href="${urls.managedGroups}"]`));
assert.ok(
find(`.rose-layout-page-actions [href="${urls.newManagedGroup}"]`)
);
});
});
@@ -0,0 +1,77 @@
import { module, test } from 'qunit';
import { visit, click, find, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { authenticateSession } from 'ember-simple-auth/test-support';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
module('Acceptance | managed-groups | read', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
const instances = {
scopes: {
global: null,
org: null,
},
authMethod: null,
managedGroup: null,
};
const urls = {
orgScope: null,
authMethods: null,
managedGroups: null,
newManagedGroup: null,
managedGroup: null,
};
hooks.beforeEach(function () {
authenticateSession({});
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
instances.managedGroup = this.server.create('managed-group', {
scope: instances.scopes.org,
authMethod: instances.authMethod,
});
// Generate route URLs for resources
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
urls.managedGroups = `${urls.authMethod}/managed-groups`;
urls.newManagedGroup = `${urls.managedGroups}/new`;
// TODO refactor in the rest of the test this url
urls.managedGroup = `${urls.managedGroups}/${instances.managedGroup.id}`;
});
hooks.afterEach(async function () {
const notification = find('.rose-notification');
if (notification) {
await click('.rose-notification-dismiss');
}
});
test('User can navigate to a managed group form', async function (assert) {
assert.expect(1);
await visit(urls.managedGroups);
await click('main tbody .rose-table-header-cell:nth-child(1) a');
await a11yAudit();
assert.equal(currentURL(), urls.managedGroup);
});
test('User cannot navigate to a managed group form without proper authorization', async function (assert) {
assert.expect(1);
instances.managedGroup.authorized_actions =
instances.managedGroup.authorized_actions.filter(
(item) => item !== 'read'
);
await visit(urls.managedGroups);
assert.notOk(find('main tbody .rose-table-header-cell:nth-child(1) a'));
});
});
@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups',
function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:scopes/scope/auth-methods/auth-method/managed-groups'
);
assert.ok(controller);
});
}
);
@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups/managed-group',
function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:scopes/scope/auth-methods/auth-method/managed-groups/managed-group'
);
assert.ok(controller);
});
}
);
@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups/new',
function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:scopes/scope/auth-methods/auth-method/managed-groups/new'
);
assert.ok(controller);
});
}
);