diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js
index a0ceccdd9..f9d97033e 100644
--- a/ui/core/app/authenticators/password.js
+++ b/ui/core/app/authenticators/password.js
@@ -63,7 +63,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator {
authenticate() {
return super.authenticate(...arguments).then((data) => {
const token = data?.token;
- if (token) this.addTokenToAuthorization(token);
+ this.addTokenToAuthorization(token);
return data;
});
}
@@ -77,7 +77,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator {
*/
restore(data) {
const token = data?.token;
- if (token) this.addTokenToAuthorization(token);
+ this.addTokenToAuthorization(token);
return super.restore(data);
}
@@ -93,6 +93,6 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator {
const headers = adapterPrototype?.headers;
if (!headers) adapterPrototype.headers = {};
adapterPrototype.headers.Authorization = null;
- if (token) adapterPrototype.headers.Authorization = `Bearer ${token}`;
+ adapterPrototype.headers.Authorization = `Bearer ${token}`;
}
}
diff --git a/ui/core/app/components/form/account/password/change-password/index.hbs b/ui/core/app/components/form/account/password/change-password/index.hbs
index 156bc26d1..24548e843 100644
--- a/ui/core/app/components/form/account/password/change-password/index.hbs
+++ b/ui/core/app/components/form/account/password/change-password/index.hbs
@@ -44,7 +44,7 @@
diff --git a/ui/core/app/components/form/account/password/set-password/index.hbs b/ui/core/app/components/form/account/password/set-password/index.hbs
index d3a1f6df2..b77d6b659 100644
--- a/ui/core/app/components/form/account/password/set-password/index.hbs
+++ b/ui/core/app/components/form/account/password/set-password/index.hbs
@@ -1,6 +1,5 @@
-
\ No newline at end of file
+
diff --git a/ui/core/app/components/form/account/password/set-password/index.js b/ui/core/app/components/form/account/password/set-password/index.js
index 5752ea980..2548bca49 100644
--- a/ui/core/app/components/form/account/password/set-password/index.js
+++ b/ui/core/app/components/form/account/password/set-password/index.js
@@ -43,14 +43,4 @@ export default class FormAccountPasswordSetPasswordIndexComponent extends Compon
next(() => this.resetPassword());
fn(password);
}
-
- /**
- * Unset password value before callback.
- * @param {function} fn
- */
- @action
- cancel(fn) {
- this.resetPassword();
- fn();
- }
}
diff --git a/ui/core/app/controllers/scopes/scope/authenticate.js b/ui/core/app/controllers/scopes/scope/authenticate.js
index 139f77c05..5c36ec58e 100644
--- a/ui/core/app/controllers/scopes/scope/authenticate.js
+++ b/ui/core/app/controllers/scopes/scope/authenticate.js
@@ -1,19 +1,17 @@
import Controller from '@ember/controller';
-import { sort } from '@ember/object/computed';
export default class ScopesScopeAuthenticateController extends Controller {
// =attributes
/**
+ * Moves the global scope to index 0.
* @type {Array}
*/
- @sort('model.scopes', (a, b) => {
- if (a.isGlobal) {
- return -1;
- } else if (b.isGlobal) {
- return 1;
- }
- return 0;
- })
- sortedScopes;
+ get sortedScopes() {
+ return [
+ ...this.model.scopes.filter((scope) => scope.id === 'global'),
+ ...this.model.scopes.filter((scope) => scope.id !== 'global'),
+ ]
+ }
+
}
diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js
index 03a582d1b..900c5eaba 100644
--- a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js
+++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js
@@ -47,14 +47,4 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSettingsRout
this.notify.error(error.message, { closeAfter: null });
}
}
-
- /**
- * Redirect to hosts as if nothing ever happened.
- */
- @action
- cancel() {
- this.replaceWith(
- 'scopes.scope.auth-methods.auth-method.accounts.account.set-password'
- );
- }
}
diff --git a/ui/core/app/routes/scopes/scope/orgs.js b/ui/core/app/routes/scopes/scope/orgs.js
index 76ff7b208..626de80fa 100644
--- a/ui/core/app/routes/scopes/scope/orgs.js
+++ b/ui/core/app/routes/scopes/scope/orgs.js
@@ -38,9 +38,8 @@ export default class ScopesScopeOrgsRoute extends Route {
*/
@action
cancel(org) {
- const { isNew } = org;
org.rollbackAttributes();
- if (isNew) this.transitionTo('scopes.scope.orgs');
+ this.transitionTo('scopes.scope.orgs');
}
/**
@@ -51,13 +50,12 @@ export default class ScopesScopeOrgsRoute extends Route {
@action
@loading
async save(org) {
- const { isNew } = org;
try {
await org.save();
- await this.transitionTo('scopes.scope', org);
+ await this.transitionTo('scopes.scope.projects', org);
this.refresh();
this.notify.success(
- this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
+ this.intl.t('notifications.create-success')
);
} catch (error) {
// TODO: replace with translated strings
diff --git a/ui/core/app/routes/scopes/scope/projects/project/sessions.js b/ui/core/app/routes/scopes/scope/projects/project/sessions.js
index d157fe3aa..b537560a7 100644
--- a/ui/core/app/routes/scopes/scope/projects/project/sessions.js
+++ b/ui/core/app/routes/scopes/scope/projects/project/sessions.js
@@ -4,8 +4,9 @@ import { action } from '@ember/object';
import { all, hash } from 'rsvp';
import { task, timeout } from 'ember-concurrency';
import { A } from '@ember/array';
+import config from '../../../../../config/environment';
-const POLL_TIMEOUT_SECONDS = 2.5;
+const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
export default class ScopesScopeProjectsProjectSessionsRoute extends Route {
diff --git a/ui/core/config/environment.js b/ui/core/config/environment.js
index a6f794d02..ffb4c36bf 100644
--- a/ui/core/config/environment.js
+++ b/ui/core/config/environment.js
@@ -34,6 +34,7 @@ module.exports = function (environment) {
appName: APP_NAME,
notifyTimeout: 4000,
+ sessionPollingTimeoutSeconds: 2.5,
enableConfirmService: true,
contentSecurityPolicyHeader: 'Content-Security-Policy',
@@ -99,6 +100,8 @@ module.exports = function (environment) {
// Notification timeout should be 0 for fast tests
ENV.notifyTimeout = 0;
+ // Session polling timeout should be short
+ ENV.sessionPollingTimeoutSeconds = 0.25;
ENV.enableConfirmService = false;
}
diff --git a/ui/core/tests/acceptance/accounts/set-password-test.js b/ui/core/tests/acceptance/accounts/set-password-test.js
index 545f4642c..d03dff3d1 100644
--- a/ui/core/tests/acceptance/accounts/set-password-test.js
+++ b/ui/core/tests/acceptance/accounts/set-password-test.js
@@ -71,13 +71,16 @@ module('Acceptance | accounts | set password', function (hooks) {
await a11yAudit();
});
- test('can cancel setting new password', async function (assert) {
- assert.expect(1);
- await visit(urls.setPassword);
- await fillIn('[name="password"]', 'update password');
- await click('form button:not([type="submit"])');
- assert.notOk(find('[name="password"]').textContent.trim());
- });
+ // NOTE: since set password is a tabbed route with easy navigation away
+ // from the form, and the form does not directly operate on a model instance,
+ // a "cancel" flow doesn't make much sense. Disabling for now.
+ // test('can cancel setting new password', async function (assert) {
+ // assert.expect(1);
+ // await visit(urls.setPassword);
+ // await fillIn('[name="password"]', 'update password');
+ // await click('form button:not([type="submit"])');
+ // assert.notOk(find('[name="password"]').textContent.trim());
+ // });
test('errors are displayed when setting password fails', async function (assert) {
assert.expect(1);
diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js
index 9a6641194..d8bdb6162 100644
--- a/ui/core/tests/acceptance/authentication-test.js
+++ b/ui/core/tests/acceptance/authentication-test.js
@@ -37,8 +37,13 @@ module('Acceptance | authentication', function (hooks) {
let authenticateURL;
let authMethodAuthenticateURL;
let authMethodGlobalAuthenticateURL;
+ let changePasswordURL;
let orgsURL;
let projectsURL;
+ let usersURL;
+ let groupsURL;
+ let rolesURL;
+ let authMethodsURL;
hooks.beforeEach(function () {
invalidateSession();
@@ -66,8 +71,13 @@ module('Acceptance | authentication', function (hooks) {
authenticateURL = `/scopes/${orgScopeID}/authenticate`;
authMethodGlobalAuthenticateURL = `/scopes/global/authenticate/${globalAuthMethodID}`;
authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`;
+ changePasswordURL = `/account/change-password`;
orgsURL = `/scopes/global/orgs`;
projectsURL = `/scopes/${orgScopeID}/projects`;
+ usersURL = `/scopes/${orgScopeID}/users`;
+ groupsURL = `/scopes/${orgScopeID}/groups`;
+ rolesURL = `/scopes/${orgScopeID}/roles`;
+ authMethodsURL = `/scopes/${orgScopeID}/auth-methods`;
});
test('visiting auth methods authenticate route redirects to first auth method', async function (assert) {
@@ -105,6 +115,20 @@ module('Acceptance | authentication', function (hooks) {
assert.notOk(currentSession().isAuthenticated);
});
+ test('visiting change password while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(changePasswordURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
+ test('visiting orgs while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(orgsURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
test('visiting projects while unauthenticated redirects to first global authenticate method', async function (assert) {
assert.expect(2);
await visit(projectsURL);
@@ -112,6 +136,34 @@ module('Acceptance | authentication', function (hooks) {
assert.notOk(currentSession().isAuthenticated);
});
+ test('visiting users while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(usersURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
+ test('visiting roles while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(rolesURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
+ test('visiting groups while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(groupsURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
+ test('visiting auth methods while unauthenticated redirects to first global authenticate method', async function (assert) {
+ assert.expect(2);
+ await visit(authMethodsURL);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ });
+
test('visiting auth method authenticate route', async function (assert) {
assert.expect(1);
await visit(authMethodAuthenticateURL);
@@ -159,7 +211,19 @@ module('Acceptance | authentication', function (hooks) {
assert.notOk(currentSession().isAuthenticated);
});
- test('successful authentication redirects to projects', async function (assert) {
+ test('successful authentication with the global scope redirects to orgs', async function (assert) {
+ assert.expect(4);
+ await visit(authMethodGlobalAuthenticateURL);
+ assert.notOk(currentSession().isAuthenticated);
+ assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
+ await fillIn('[name="identification"]', 'test');
+ await fillIn('[name="password"]', 'test');
+ await click('[type="submit"]');
+ assert.equal(currentURL(), orgsURL);
+ assert.ok(currentSession().isAuthenticated);
+ });
+
+ test('successful authentication with an org scope redirects to projects', async function (assert) {
assert.expect(4);
await visit(authMethodAuthenticateURL);
assert.notOk(currentSession().isAuthenticated);
diff --git a/ui/core/tests/acceptance/groups/members-test.js b/ui/core/tests/acceptance/groups/members-test.js
index 69edf67df..c81154b49 100644
--- a/ui/core/tests/acceptance/groups/members-test.js
+++ b/ui/core/tests/acceptance/groups/members-test.js
@@ -41,7 +41,7 @@ module('Acceptance | groups | members', function (hooks) {
instances.group = this.server.create('group', {
scope: instances.scopes.org,
}, 'withMembers');
- membersCount = instances.group.memberIds.length;
+ membersCount = instances.group.memberIds.length;
urls.groups = `/scopes/${instances.scopes.org.id}/groups`;
urls.group = `${urls.groups}/${instances.group.id}`;
urls.members = `${urls.group}/members`;
@@ -98,6 +98,9 @@ module('Acceptance | groups | members', function (hooks) {
assert.equal(findAll('tbody tr').length, 0);
await click('.rose-layout-page-actions a')
assert.equal(currentURL(), urls.addMembers);
+ // Click three times to select, unselect, then reselect (for coverage)
+ await click('tbody label');
+ await click('tbody label');
await click('tbody label');
await click('form [type="submit"]');
await visit(urls.members);
diff --git a/ui/core/tests/acceptance/host-catalogs/host-sets-test.js b/ui/core/tests/acceptance/host-catalogs/host-sets-test.js
deleted file mode 100644
index d7cd8a32a..000000000
--- a/ui/core/tests/acceptance/host-catalogs/host-sets-test.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import { module, test } from 'qunit';
-import { visit, currentURL, fillIn, click, find, findAll } from '@ember/test-helpers';
-import { setupApplicationTest } from 'ember-qunit';
-import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
-import a11yAudit from 'ember-a11y-testing/test-support/audit';
-import { Response } from 'miragejs';
-import {
- authenticateSession,
- // These are left here intentionally for future reference.
- //currentSession,
- //invalidateSession,
-} from 'ember-simple-auth/test-support';
-
-module('Acceptance | host catalogs | host sets', function (hooks) {
- setupApplicationTest(hooks);
- setupMirage(hooks);
-
- const instances = {
- scopes: {
- global: null,
- org: null,
- project: null,
- },
- hostCatalog: null,
- hostSets: null,
- };
- const urls = {
- orgScope: null,
- projects: null,
- project: null,
- hostCatalogs: null,
- hostCatalog: null,
- hostSets: null,
- newHostSet: null
- };
- let hostSetsCount;
-
- hooks.beforeEach(function () {
- // Setup Mirage mock resources for this test
- 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.scopes.project = this.server.create('scope', {
- type: 'project',
- scope: { id: instances.scopes.org.id, type: 'org' },
- });
- instances.hostCatalog = this.server.create('host-catalog', {
- scope: instances.scopes.project,
- });
- instances.hostSets = this.server.createList('host-set', 3, {
- scope: instances.scopes.project,
- hostCatalog: instances.hostCatalog
- });
- hostSetsCount = instances.hostCatalog.hostSetIds.length;
- // Generate route URLs for resources
- urls.orgScope = `/scopes/${instances.scopes.org.id}`;
- urls.projects = `${urls.orgScope}/projects`;
- urls.project = `${urls.projects}/${instances.hostCatalog.scope.id}`;
- urls.hostCatalogs = `${urls.project}/host-catalogs`;
- urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`;
- urls.hostSets = `${urls.hostCatalog}/host-sets`;
- urls.newHostSet = `${urls.hostSets}/new`;
- });
-
- test('visiting host sets', async function (assert) {
- assert.expect(3);
- await visit(urls.hostSets);
- await a11yAudit();
- assert.equal(currentURL(), urls.hostSets);
- assert.ok(hostSetsCount);
- assert.equal(findAll('tbody tr').length, hostSetsCount);
- });
-
- test('can navigate to a host set form', async function (assert) {
- assert.expect(1);
- await visit(urls.hostSets);
- await click('main tbody .rose-table-header-cell:nth-child(1) a');
- await a11yAudit();
- assert.notEqual(currentURL(), urls.hostSets);
- });
-
- test('can create host set and save changes', async function (assert) {
- assert.expect(1);
- await visit(urls.newHostSet);
- await fillIn('[name="name"]', 'Test Name');
- await fillIn('[name="description"]', 'description');
- await click('form [type="submit"]:not(:disabled)');
- await visit(urls.hostSets);
- assert.equal(findAll('tbody tr').length, hostSetsCount + 1);
- });
-
- test('can create host set and cancel changes', async function (assert) {
- assert.expect(2);
- await visit(urls.newHostSet);
- await fillIn('[name="name"]', 'Test Name');
- await click('form button:not([type="submit"])');
- assert.equal(currentURL(), urls.hostSets);
- assert.equal(findAll('tbody tr').length, hostSetsCount);
- });
-
- test('saving a new host set with invalid fields displays error messages', async function (assert) {
- assert.expect(2);
- this.server.post('/host-sets', () => {
- 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.newHostSet);
- await fillIn('[name="name"]', 'new target');
- await click('form [type="submit"]');
- await a11yAudit();
- assert.ok(find('[role="alert"]'));
- assert.ok(find('.rose-form-error-message'));
- });
-});
diff --git a/ui/core/tests/acceptance/host-catalogs/hosts-test.js b/ui/core/tests/acceptance/host-catalogs/hosts-test.js
deleted file mode 100644
index 565ef41a8..000000000
--- a/ui/core/tests/acceptance/host-catalogs/hosts-test.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import { module, test } from 'qunit';
-import { visit, currentURL, fillIn, click, find, findAll } from '@ember/test-helpers';
-import { setupApplicationTest } from 'ember-qunit';
-import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
-import a11yAudit from 'ember-a11y-testing/test-support/audit';
-import { Response } from 'miragejs';
-import {
- authenticateSession,
- // These are left here intentionally for future reference.
- //currentSession,
- //invalidateSession,
-} from 'ember-simple-auth/test-support';
-
-module('Acceptance | host catalogs | hosts', function (hooks) {
- setupApplicationTest(hooks);
- setupMirage(hooks);
-
- const instances = {
- scopes: {
- global: null,
- org: null,
- project: null,
- },
- hostCatalog: null,
- hosts: null,
- };
- const urls = {
- orgScope: null,
- projects: null,
- project: null,
- hostCatalogs: null,
- hostCatalog: null,
- hosts: null,
- newHost: null
- };
- let hostsCount;
-
- hooks.beforeEach(function () {
- // Setup Mirage mock resources for this test
- 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.scopes.project = this.server.create('scope', {
- type: 'project',
- scope: { id: instances.scopes.org.id, type: 'org' },
- });
- instances.hostCatalog = this.server.create('host-catalog', {
- scope: instances.scopes.project,
- });
- instances.hosts = this.server.createList('host', 3, {
- scope: instances.scopes.project,
- hostCatalog: instances.hostCatalog
- });
- hostsCount = instances.hosts.length;
- // Generate route URLs for resources
- urls.orgScope = `/scopes/${instances.scopes.org.id}`;
- urls.projects = `${urls.orgScope}/projects`;
- urls.project = `${urls.projects}/${instances.hostCatalog.scope.id}`;
- urls.hostCatalogs = `${urls.project}/host-catalogs`;
- urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`;
- urls.hosts = `${urls.hostCatalog}/hosts`;
- urls.newHost = `${urls.hosts}/new`;
- });
-
- test('visiting hosts', async function (assert) {
- assert.expect(3);
- await visit(urls.hosts);
- await a11yAudit();
- assert.equal(currentURL(), urls.hosts);
- assert.ok(hostsCount);
- assert.equal(findAll('tbody tr').length, hostsCount);
- });
-
- test('can navigate to a host form', async function (assert) {
- assert.expect(1);
- await visit(urls.hosts);
- await click('main tbody .rose-table-header-cell:nth-child(1) a');
- await a11yAudit();
- assert.notEqual(currentURL(), urls.hosts);
- });
-
- test('can create host and save changes', async function (assert) {
- assert.expect(1);
- await visit(urls.newHost);
- await fillIn('[name="name"]', 'Test Name');
- await fillIn('[name="description"]', 'description');
- await click('form [type="submit"]:not(:disabled)');
- await visit(urls.hosts);
- assert.equal(findAll('tbody tr').length, hostsCount + 1);
- });
-
- test('can create host and cancel changes', async function (assert) {
- assert.expect(2);
- await visit(urls.newHost);
- await fillIn('[name="name"]', 'Test Name');
- await click('form button:not([type="submit"])');
- assert.equal(currentURL(), urls.hosts);
- assert.equal(findAll('tbody tr').length, hostsCount);
- });
-
- test('saving a new host with invalid fields displays error messages', async function (assert) {
- assert.expect(2);
- this.server.post('/hosts', () => {
- 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.newHost);
- await fillIn('[name="name"]', 'new target');
- await click('form [type="submit"]');
- await a11yAudit();
- assert.ok(find('[role="alert"]'));
- assert.ok(find('.rose-form-error-message'));
- });
-});
diff --git a/ui/core/tests/acceptance/host-sets-test.js b/ui/core/tests/acceptance/host-sets-test.js
index 1fdfc0f91..1b69b06a3 100644
--- a/ui/core/tests/acceptance/host-sets-test.js
+++ b/ui/core/tests/acceptance/host-sets-test.js
@@ -1,5 +1,5 @@
import { module, test } from 'qunit';
-import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers';
+import { visit, currentURL, fillIn, click, find, findAll } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
@@ -32,6 +32,7 @@ module('Acceptance | host sets', function (hooks) {
hostCatalog: null,
hostSets: null,
hostSet: null,
+ newHostSet: null
};
hooks.beforeEach(function () {
@@ -61,6 +62,7 @@ module('Acceptance | host sets', function (hooks) {
urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`;
urls.hostSets = `${urls.hostCatalog}/host-sets`;
urls.hostSet = `${urls.hostSets}/${instances.hostSet.id}`;
+ urls.newHostSet = `${urls.hostSets}/new`;
});
test('visiting a host set', async function (assert) {
@@ -148,4 +150,54 @@ module('Acceptance | host sets', function (hooks) {
await a11yAudit();
assert.ok(find('[role="alert"]'));
});
+
+ test('can create host set and save changes', async function (assert) {
+ assert.expect(1);
+ const hostSetsCount = this.server.db.hostSets.length;
+ await visit(urls.newHostSet);
+ await fillIn('[name="name"]', 'Test Name');
+ await fillIn('[name="description"]', 'description');
+ await click('form [type="submit"]:not(:disabled)');
+ await visit(urls.hostSets);
+ assert.equal(findAll('tbody tr').length, hostSetsCount + 1);
+ });
+
+ test('can create host set and cancel changes', async function (assert) {
+ assert.expect(2);
+ const hostSetsCount = this.server.db.hostSets.length;
+ await visit(urls.newHostSet);
+ await fillIn('[name="name"]', 'Test Name');
+ await click('form button:not([type="submit"])');
+ assert.equal(currentURL(), urls.hostSets);
+ assert.equal(findAll('tbody tr').length, hostSetsCount);
+ });
+
+ test('saving a new host set with invalid fields displays error messages', async function (assert) {
+ assert.expect(2);
+ this.server.post('/host-sets', () => {
+ 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.newHostSet);
+ await fillIn('[name="name"]', 'new target');
+ await click('form [type="submit"]');
+ await a11yAudit();
+ assert.ok(find('[role="alert"]'));
+ assert.ok(find('.rose-form-error-message'));
+ });
});
diff --git a/ui/core/tests/acceptance/hosts-test.js b/ui/core/tests/acceptance/hosts-test.js
index 2514210fb..d04904da9 100644
--- a/ui/core/tests/acceptance/hosts-test.js
+++ b/ui/core/tests/acceptance/hosts-test.js
@@ -1,5 +1,5 @@
import { module, test } from 'qunit';
-import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers';
+import { visit, currentURL, fillIn, click, find, findAll } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
@@ -32,6 +32,7 @@ module('Acceptance | hosts', function (hooks) {
hostCatalog: null,
hosts: null,
host: null,
+ newHost: null
};
hooks.beforeEach(function () {
@@ -61,6 +62,7 @@ module('Acceptance | hosts', function (hooks) {
urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`;
urls.hosts = `${urls.hostCatalog}/hosts`;
urls.host = `${urls.hosts}/${instances.host.id}`;
+ urls.newHost = `${urls.hosts}/new`;
});
test('visiting a host', async function (assert) {
@@ -150,4 +152,54 @@ module('Acceptance | hosts', function (hooks) {
await a11yAudit();
assert.ok(find('[role="alert"]'));
});
+
+ test('can create host and save changes', async function (assert) {
+ assert.expect(1);
+ const hostsCount = this.server.db.hosts.length;
+ await visit(urls.newHost);
+ await fillIn('[name="name"]', 'Test Name');
+ await fillIn('[name="description"]', 'description');
+ await click('form [type="submit"]:not(:disabled)');
+ await visit(urls.hosts);
+ assert.equal(findAll('tbody tr').length, hostsCount + 1);
+ });
+
+ test('can create host and cancel changes', async function (assert) {
+ assert.expect(2);
+ const hostsCount = this.server.db.hosts.length;
+ await visit(urls.newHost);
+ await fillIn('[name="name"]', 'Test Name');
+ await click('form button:not([type="submit"])');
+ assert.equal(currentURL(), urls.hosts);
+ assert.equal(findAll('tbody tr').length, hostsCount);
+ });
+
+ test('saving a new host with invalid fields displays error messages', async function (assert) {
+ assert.expect(2);
+ this.server.post('/hosts', () => {
+ 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.newHost);
+ await fillIn('[name="name"]', 'new target');
+ await click('form [type="submit"]');
+ await a11yAudit();
+ assert.ok(find('[role="alert"]'));
+ assert.ok(find('.rose-form-error-message'));
+ });
});
diff --git a/ui/core/tests/acceptance/orgs-test.js b/ui/core/tests/acceptance/orgs-test.js
index aaeca8356..4215d3db7 100644
--- a/ui/core/tests/acceptance/orgs-test.js
+++ b/ui/core/tests/acceptance/orgs-test.js
@@ -56,6 +56,29 @@ module('Acceptance | org', function (hooks) {
assert.equal(findAll('article').length, orgsCount);
});
+ test('visiting orgs when endpoint 404s shows an error message', async function (assert) {
+ assert.expect(1);
+ this.server.get('/scopes', () => new Response(404));
+ await visit(urls.orgs);
+ await a11yAudit();
+ assert.ok(find('.rose-message'));
+ });
+
+ test('visiting orgs is successful even when the global scope cannot be fetched', async function (assert) {
+ assert.expect(3);
+ this.server.get('/scopes/:id', ({ scopes }, { params: { id } }) => {
+ const scope = scopes.find(id);
+ return (id === 'global')
+ ? new Response(404)
+ : scope;
+ });
+ await visit(urls.orgs);
+ await a11yAudit();
+ assert.ok(orgsCount);
+ assert.equal(currentURL(), urls.orgs);
+ assert.equal(findAll('article').length, orgsCount);
+ });
+
test('visiting scopes redirects to index', async function (assert) {
assert.expect(1);
await visit(urls.scopes);
diff --git a/ui/core/tests/acceptance/roles/principals-test.js b/ui/core/tests/acceptance/roles/principals-test.js
index 493a7a9f4..cebf3031b 100644
--- a/ui/core/tests/acceptance/roles/principals-test.js
+++ b/ui/core/tests/acceptance/roles/principals-test.js
@@ -90,6 +90,9 @@ module('Acceptance | roles | principals', function (hooks) {
assert.equal(findAll('tbody tr').length, 0);
await click('.rose-layout-page-actions a')
assert.equal(currentURL(), urls.addPrincipals);
+ // Click three times to select, unselect, then reselect (for coverage)
+ await click('tbody label');
+ await click('tbody label');
await click('tbody label');
await click('form [type="submit"]');
await visit(urls.rolePrincipals);
diff --git a/ui/core/tests/acceptance/sessions-test.js b/ui/core/tests/acceptance/sessions-test.js
new file mode 100644
index 000000000..03ae3a78b
--- /dev/null
+++ b/ui/core/tests/acceptance/sessions-test.js
@@ -0,0 +1,103 @@
+import { module, test } from 'qunit';
+import { visit, currentURL, findAll, find, click } from '@ember/test-helpers';
+import { setupApplicationTest } from 'ember-qunit';
+import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
+import a11yAudit from 'ember-a11y-testing/test-support/audit';
+import { run, later } from '@ember/runloop';
+import { Response } from 'miragejs';
+import {
+ authenticateSession,
+ // These are left here intentionally for future reference.
+ //currentSession,
+ //invalidateSession,
+} from 'ember-simple-auth/test-support';
+
+module('Acceptance | sessions', function (hooks) {
+ setupApplicationTest(hooks);
+ setupMirage(hooks);
+
+ const instances = {
+ scopes: {
+ global: null,
+ org: null,
+ },
+ sessions: null,
+ };
+ const urls = {
+ orgScope: null,
+ sessions: 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.scopes.project = this.server.create('scope', {
+ type: 'project',
+ scope: { id: instances.scopes.org.id, type: instances.scopes.org.type },
+ });
+ this.server.createList('group', 1, { scope: instances.scopes.org }, 'withMembers');
+ this.server.createList('host-catalog', 1, { scope: instances.scopes.project }, 'withChildren');
+ this.server.createList('target', 1, { scope: instances.scopes.project }, 'withRandomHostSets');
+ instances.sessions = this.server.createList('session', 10, {
+ scope: instances.scopes.project,
+ }, 'withAssociations');
+ urls.sessions = `/scopes/${instances.scopes.org.id}/projects/${instances.scopes.project.id}/sessions`;
+ });
+
+ test('visiting sessions', async function (assert) {
+ assert.expect(2);
+ authenticateSession({});
+ later(async () => {
+ run.cancelTimers();
+ await a11yAudit();
+ assert.equal(currentURL(), urls.sessions);
+ assert.equal(findAll('tbody tr').length, instances.sessions.length);
+ }, 750);
+ await visit(urls.sessions);
+ });
+
+ test('visiting sessions without users or targets is OK', async function (assert) {
+ assert.expect(2);
+ instances.sessions[0].update({
+ userId: null,
+ targetId: null
+ });
+ authenticateSession({});
+ later(async () => {
+ run.cancelTimers();
+ await a11yAudit();
+ assert.equal(currentURL(), urls.sessions);
+ assert.equal(findAll('tbody tr').length, instances.sessions.length);
+ }, 750);
+ await visit(urls.sessions);
+ });
+
+ test('cancelling a session', async function (assert) {
+ assert.expect(2);
+ authenticateSession({});
+ later(async () => {
+ run.cancelTimers();
+ await click('tbody tr:first-child td:last-child button');
+ assert.ok(find('[role="alert"].is-success'));
+ }, 750);
+ await visit(urls.sessions);
+ assert.equal(currentURL(), urls.sessions);
+ });
+
+ test('cancelling a session with error shows notification', async function (assert) {
+ assert.expect(2);
+ this.server.post('/sessions/:id_method', () => new Response(400));
+ authenticateSession({});
+ later(async () => {
+ run.cancelTimers();
+ await click('tbody tr:first-child td:last-child button');
+ assert.ok(find('[role="alert"].is-error'));
+ }, 750);
+ await visit(urls.sessions);
+ assert.equal(currentURL(), urls.sessions);
+ });
+});
diff --git a/ui/core/tests/acceptance/targets-test.js b/ui/core/tests/acceptance/targets-test.js
index a269173b0..76e87a931 100644
--- a/ui/core/tests/acceptance/targets-test.js
+++ b/ui/core/tests/acceptance/targets-test.js
@@ -265,6 +265,9 @@ module('Acceptance | targets', function (hooks) {
assert.equal(findAll('tbody tr').length, 0);
await click('.rose-layout-page-actions a')
assert.equal(currentURL(), urls.targetAddHostSets);
+ // Click three times to select, unselect, then reselect (for coverage)
+ await click('tbody label');
+ await click('tbody label');
await click('tbody label');
await click('form [type="submit"]');
await visit(urls.targetHostSets);
diff --git a/ui/core/tests/acceptance/users/accounts-test.js b/ui/core/tests/acceptance/users/accounts-test.js
index 8bcaa951a..aa941f5ed 100644
--- a/ui/core/tests/acceptance/users/accounts-test.js
+++ b/ui/core/tests/acceptance/users/accounts-test.js
@@ -97,8 +97,11 @@ module('Acceptance | users | accounts', function (hooks) {
instances.user.update({ accountIds: [] });
await visit(urls.accounts);
assert.equal(findAll('tbody tr').length, 0);
- await click('.rose-layout-page-actions a')
+ await click('.rose-layout-page-actions a');
assert.equal(currentURL(), urls.addAccounts);
+ // Click three times to select, unselect, then reselect (for coverage)
+ await click('tbody label');
+ await click('tbody label');
await click('tbody label');
await click('form [type="submit"]');
await visit(urls.accounts);
diff --git a/ui/core/tests/integration/components/error/message-test.js b/ui/core/tests/integration/components/error/message-test.js
index 2671c9b7e..c33db71b8 100644
--- a/ui/core/tests/integration/components/error/message-test.js
+++ b/ui/core/tests/integration/components/error/message-test.js
@@ -20,6 +20,32 @@ module('Integration | Component | error/message', function (hooks) {
find('.rose-message-description').textContent.trim(),
'You are not authenticated. Please authenticate and try again later.'
);
+ await render(hbs``);
+ assert.equal(
+ find('.rose-message-title').textContent.trim(),
+ 'You are not authorized'
+ );
+ assert.equal(
+ find('.rose-message-subtitle').textContent.trim(),
+ 'Error 403'
+ );
+ assert.equal(
+ find('.rose-message-description').textContent.trim(),
+ 'You must be granted permissions to view this data. Ask your administrator if you think you should have access.'
+ );
+ await render(hbs``);
+ assert.equal(
+ find('.rose-message-title').textContent.trim(),
+ 'Resource not found'
+ );
+ assert.equal(
+ find('.rose-message-subtitle').textContent.trim(),
+ 'Error 404'
+ );
+ assert.equal(
+ find('.rose-message-description').textContent.trim(),
+ 'We could not find the requested resource. You can ask your administrator or try again later.'
+ );
});
test('it renders default error for unknown error status', async function (assert) {