From 709a8e39490d86eef3da4c5934fa5b9615345e26 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 28 Oct 2020 19:00:35 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20reintroduce=20hosts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/components/form/host/index.hbs | 77 +++++ ui/core/app/router.js | 7 +- .../scope/host-catalogs/host-catalog/hosts.js | 77 +++++ .../host-catalogs/host-catalog/hosts/host.js | 52 ++++ .../host-catalog/hosts/host/index.js | 4 + .../host-catalogs/host-catalog/hosts/index.js | 4 + .../host-catalogs/host-catalog/hosts/new.js | 51 ++++ .../host-catalogs/host-catalog/-actions.hbs | 2 +- .../host-catalog/-navigation.hbs | 6 +- .../host-catalogs/host-catalog/hosts.hbs | 3 + .../host-catalogs/host-catalog/hosts/host.hbs | 3 + .../host-catalog/hosts/host/-actions.hbs | 9 + .../host-catalog/hosts/host/-header.hbs | 12 + .../host-catalog/hosts/host/-navigation.hbs | 5 + .../host-catalog/hosts/host/index.hbs | 5 + .../host-catalog/hosts/index.hbs | 60 ++++ .../host-catalogs/host-catalog/hosts/new.hbs | 7 + .../host-catalog/hosts/new/-header.hbs | 4 + .../acceptance/host-catalogs/hosts-test.js | 283 ++++++++++++++++++ .../components/form/account-test.js | 23 -- .../components/form/account/password-test.js | 23 -- .../account/password/change-password-test.js | 26 -- .../account/password/set-password-test.js | 26 -- .../components/form/auth-method-test.js | 23 -- .../form/role/add-principals-test.js | 12 - .../components/form/user/add-accounts-test.js | 12 - .../host-catalogs/host-catalog/hosts-test.js | 11 + .../host-catalog/hosts/host-test.js | 11 + .../host-catalog/hosts/host/index-test.js | 11 + .../host-catalog/hosts/index-test.js | 11 + .../host-catalog/hosts/new-test.js | 11 + 31 files changed, 721 insertions(+), 150 deletions(-) create mode 100644 ui/core/app/components/form/host/index.hbs create mode 100644 ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts.js create mode 100644 ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host.js create mode 100644 ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index.js create mode 100644 ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/index.js create mode 100644 ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/new.js create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-actions.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-header.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-navigation.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new.hbs create mode 100644 ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new/-header.hbs create mode 100644 ui/core/tests/acceptance/host-catalogs/hosts-test.js delete mode 100644 ui/core/tests/integration/components/form/account-test.js delete mode 100644 ui/core/tests/integration/components/form/account/password-test.js delete mode 100644 ui/core/tests/integration/components/form/account/password/change-password-test.js delete mode 100644 ui/core/tests/integration/components/form/account/password/set-password-test.js delete mode 100644 ui/core/tests/integration/components/form/auth-method-test.js delete mode 100644 ui/core/tests/integration/components/form/role/add-principals-test.js delete mode 100644 ui/core/tests/integration/components/form/user/add-accounts-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/new-test.js diff --git a/ui/core/app/components/form/host/index.hbs b/ui/core/app/components/form/host/index.hbs new file mode 100644 index 000000000..b5a931880 --- /dev/null +++ b/ui/core/app/components/form/host/index.hbs @@ -0,0 +1,77 @@ + + + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + diff --git a/ui/core/app/router.js b/ui/core/app/router.js index e3419f5f5..cf7accb4a 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -66,7 +66,12 @@ Router.map(function () { this.route('host-catalogs', function() { this.route('new'); - this.route('host-catalog', { path: ':host_catalog_id' }, function() {}); + this.route('host-catalog', { path: ':host_catalog_id' }, function() { + this.route('hosts', function() { + this.route('new'); + this.route('host', { path: ':host_id' }, function() {}); + }); + }); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts.js new file mode 100644 index 000000000..912381a39 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts.js @@ -0,0 +1,77 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; + +export default class ScopesScopeHostCatalogsHostCatalogHostsRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Loads all hosts under the current host catalog and it's parent scope. + * @return {Promise{[HostModel]}} + */ + model() { + const { id: host_catalog_id } = this.modelFor( + 'scopes.scope.host-catalogs.host-catalog' + ); + return this.store.query('host', { host_catalog_id }); + } + + // =actions + + /** + * Rollback changes on a host. + * @param {HostModel} host + */ + @action + cancel(host) { + const { isNew } = host; + host.rollbackAttributes(); + if (isNew) + this.transitionTo( + 'scopes.scope.host-catalogs.host-catalog.hosts' + ); + } + + /** + * Handle save of a host. + * @param {HostModel} host + * @param {Event} e + */ + @action + @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') + async save(host) { + await host.save(); + await this.transitionTo( + 'scopes.scope.host-catalogs.host-catalog.hosts.host', + host + ); + this.refresh(); + } + + /** + * Delete host in current scope and redirect to index. + * @param {HostModel} host + */ + @action + @loading + @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') + async deleteHost(host) { + await host.destroyRecord(); + await this.replaceWith( + 'scopes.scope.host-catalogs.host-catalog.hosts' + ); + this.refresh(); + } +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host.js new file mode 100644 index 000000000..8cbd978a2 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host.js @@ -0,0 +1,52 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeHostCatalogsHostCatalogHostsHostRoute extends Route { + // =methods + + /** + * Load a host using current host-catalog and it's parent scope. + * @param {object} params + * @param {string} params.host_id + * @return {HostModel} + */ + async model({ host_id }) { + return this.store.findRecord('host', host_id, { reload: true }); + } + + /** + * Renders the host specific templates for header, navigation, and action page sections. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + super.renderTemplate(...arguments); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/hosts/host/-header', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'header', + model: model, + } + ); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/hosts/host/-navigation', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'navigation', + model: model, + } + ); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/hosts/host/-actions', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'actions', + model: model, + } + ); + } +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index.js new file mode 100644 index 000000000..8d1674617 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeHostCatalogsHostCatalogHostsHostIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/index.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/index.js new file mode 100644 index 000000000..931deeb2e --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeHostCatalogsHostCatalogHostsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/new.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/new.js new file mode 100644 index 000000000..e82bfe0e4 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/hosts/new.js @@ -0,0 +1,51 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeHostCatalogsHostCatalogHostsNewRoute extends Route { + // =methods + + /** + * Creates a new unsaved host in current host catalog scope. + * @return {HostModel} + */ + model() { + const { id: host_catalog_id } = this.modelFor( + 'scopes.scope.host-catalogs.host-catalog' + ); + return this.store.createRecord('host', { + type: 'static', + host_catalog_id, + }); + } + + /** + * Renders new host specific templates for header, navigation, + * and action page sections. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + super.renderTemplate(...arguments); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/hosts/new/-header', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'header', + model: model, + } + ); + + this.render('_empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'navigation', + model: model, + }); + + this.render('_empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'actions', + model: model, + }); + } +} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/-actions.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/-actions.hbs index 14538798b..2cfb73b07 100644 --- a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/-actions.hbs @@ -1,11 +1,11 @@ - {{!-- {{t "resources.host.actions.create"}} + {{!-- {{t 'titles.details'}} + + {{t 'resources.host.title_plural'}} + {{!-- {{t 'resources.host-set.title_plural'}} - - {{t 'resources.host.title_plural'}} - --}} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts.hbs new file mode 100644 index 000000000..5e74dd528 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.host.title_plural")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host.hbs new file mode 100644 index 000000000..d66e83390 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host.hbs @@ -0,0 +1,3 @@ +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-actions.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-actions.hbs new file mode 100644 index 000000000..4c9a91aa3 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-actions.hbs @@ -0,0 +1,9 @@ + + + {{t 'resources.host.actions.delete'}} + + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-header.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-header.hbs new file mode 100644 index 000000000..7b15e6f0f --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-header.hbs @@ -0,0 +1,12 @@ +

+ {{t "resources.host.title"}} + +

+

{{t "resources.host.description"}}

+ + {{@model.id}} + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-navigation.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-navigation.hbs new file mode 100644 index 000000000..157f7472f --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/-navigation.hbs @@ -0,0 +1,5 @@ + + + {{t 'titles.details'}} + + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/index.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/index.hbs new file mode 100644 index 000000000..6cc780607 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/host/index.hbs @@ -0,0 +1,5 @@ + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/index.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/index.hbs new file mode 100644 index 000000000..dcf9b3b63 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/index.hbs @@ -0,0 +1,60 @@ +{{#if @model}} + + + + + {{t "form.name.label"}} + {{t "form.type.label"}} + {{t "form.id.label"}} + {{t "form.address.label"}} + + + + {{#each @model as |host|}} + + + + {{host.displayName}} + +

{{host.description}}

+
+ + + {{t (concat 'resources.host.types.' host.type)}} + + + + + {{host.id}} + + + + {{host.attributes.address}} + +
+ {{/each}} +
+
+ +{{else}} + + + + + {{t "resources.host.description"}} + + + + {{t "titles.new"}} + + + + +{{/if}} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new.hbs new file mode 100644 index 000000000..2e3ec8149 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new.hbs @@ -0,0 +1,7 @@ +{{title (t 'titles.new')}} + + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new/-header.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new/-header.hbs new file mode 100644 index 000000000..3122f3b43 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/hosts/new/-header.hbs @@ -0,0 +1,4 @@ +

+ {{t "titles.new"}} + +

diff --git a/ui/core/tests/acceptance/host-catalogs/hosts-test.js b/ui/core/tests/acceptance/host-catalogs/hosts-test.js new file mode 100644 index 000000000..ba1acae76 --- /dev/null +++ b/ui/core/tests/acceptance/host-catalogs/hosts-test.js @@ -0,0 +1,283 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, find, click, fillIn } 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 { resolve, reject } from 'rsvp'; +import sinon from 'sinon'; +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); + + let getHostCount; + + const instances = { + scopes: { + global: null, + org: null, + project: null, + hostCatalog: null, + host: null + } + }; + const urls = { + globalScope: null, + orgScope: null, + projectScope: null, + hostCatalogs: null, + hostCatalog: null, + hosts: null, + host: null, + unknownHost: null, + newHost: null, + }; + + hooks.beforeEach(function () { + // Generate resources + 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.host = this.server.create('host', { + scope: instances.scopes.project, + hostCatalog: instances.hostCatalog + }); + // Generate route URLs for resources + urls.globalScope = `/scopes/global/scopes`; + urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`; + urls.projectScope = `/scopes/${instances.scopes.project.id}`; + urls.hostCatalogs = `${urls.projectScope}/host-catalogs`; + urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`; + urls.hosts = `${urls.hostCatalog}/hosts`; + urls.host = `${urls.hosts}/${instances.host.id}`; + urls.unknownHost = `${urls.hosts}/foo`; + urls.newHost = `${urls.hosts}/new`; + // Generate resource couner + getHostCount = () => this.server.schema.hosts.all().models.length; + authenticateSession({}); + }); + + test('visiting hosts', async function (assert) { + assert.expect(2); + await visit(urls.hosts); + await a11yAudit(); + assert.equal(currentURL(), urls.hosts); + await visit(urls.host); + await a11yAudit(); + assert.equal(currentURL(), urls.host); + }); + + test('visiting an unknown host displays 404 message', async function (assert) { + assert.expect(1); + await visit(urls.unknownHost); + await a11yAudit(); + assert.ok(find('.rose-message-subtitle').textContent.trim(), 'Error 404'); + }); + + test('can create new host', async function (assert) { + assert.expect(1); + const count = getHostCount(); + await visit(urls.newHost); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.equal(getHostCount(), count + 1); + }); + + test('can cancel create new host', async function (assert) { + assert.expect(2); + const count = getHostCount(); + await visit(urls.newHost); + await fillIn('[name="name"]', 'random string'); + await click('.rose-form-actions [type="button"]'); + assert.equal(currentURL(), urls.hosts); + assert.equal(getHostCount(), count); + }); + + 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 click('[type="submit"]'); + assert.ok(find('[role="alert"]').textContent.trim(), 'The request was invalid.'); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.' + ); + }); + + test('can save changes to existing host', async function (assert) { + assert.expect(3); + assert.notEqual(instances.host.name, 'random string'); + await visit(urls.host); + await click('form [type="button"]', 'Activate edit mode'); + await fillIn('[name="name"]', 'random string'); + await click('.rose-form-actions [type="submit"]'); + assert.equal(currentURL(), urls.host); + assert.equal(this.server.schema.hosts.all().models[0].name, 'random string'); + }); + + test('can cancel changes to existing host', async function (assert) { + assert.expect(2); + await visit(urls.host); + await click('form [type="button"]', 'Activate edit mode'); + await fillIn('[name="name"]', 'random string'); + await click('.rose-form-actions [type="button"]'); + assert.notEqual(instances.host.name, 'random string'); + assert.equal(find('[name="name"]').value, instances.host.name); + }); + + test('saving an existing host with invalid fields displays error messages', async function (assert) { + assert.expect(2); + this.server.patch('/hosts/:id', () => { + 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.host); + await click('form [type="button"]', 'Activate edit mode'); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.ok(find('[role="alert"]').textContent.trim(), 'The request was invalid.'); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.' + ); + }); + + test('can discard unsaved host changes via dialog', async function (assert) { + assert.expect(5); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + assert.notEqual(instances.host.name, 'random string'); + await visit(urls.host); + await click('form [type="button"]', 'Activate edit mode'); + await fillIn('[name="name"]', 'random string'); + assert.equal(currentURL(), urls.host); + try { + await visit(urls.hosts); + } catch (e) { + assert.ok(find('.rose-dialog')); + await click('.rose-dialog-footer button:first-child'); + assert.equal(currentURL(), urls.hosts); + assert.notEqual(this.server.schema.hosts.all().models[0].name, 'random string'); + } + }); + + test('can cancel discard unsaved host changes via dialog', async function (assert) { + assert.expect(5); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + assert.notEqual(instances.host.name, 'random string'); + await visit(urls.host); + await click('form [type="button"]', 'Activate edit mode'); + await fillIn('[name="name"]', 'random string'); + assert.equal(currentURL(), urls.host); + try { + await visit(urls.hosts); + } catch (e) { + assert.ok(find('.rose-dialog')); + await click('.rose-dialog-footer button:last-child'); + assert.equal(currentURL(), urls.host); + assert.notEqual(this.server.schema.hosts.all().models[0].name, 'random string'); + } + }); + + test('can delete host', async function (assert) { + assert.expect(1); + const count = getHostCount(); + await visit(urls.host); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(getHostCount(), count - 1); + }); + + test('can accept delete host via dialog', async function (assert) { + assert.expect(2); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + confirmService.confirm = sinon.fake.returns(resolve()); + const count = getHostCount(); + await visit(urls.host); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(getHostCount(), count - 1); + assert.ok(confirmService.confirm.calledOnce); + }); + + test('cannot cancel delete host via dialog', async function (assert) { + assert.expect(2); + const confirmService = this.owner.lookup('service:confirm'); + confirmService.enabled = true; + confirmService.confirm = sinon.fake.returns(reject()); + const count = getHostCount(); + await visit(urls.host); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(getHostCount(), count); + assert.ok(confirmService.confirm.calledOnce); + }); + + test('deleting a host which errors displays error messages', async function (assert) { + assert.expect(1); + this.server.del('/hosts/:id', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit(urls.host); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.ok(find('[role="alert"]').textContent.trim(), 'Oops.'); + }); + +}); diff --git a/ui/core/tests/integration/components/form/account-test.js b/ui/core/tests/integration/components/form/account-test.js deleted file mode 100644 index 5fc184035..000000000 --- a/ui/core/tests/integration/components/form/account-test.js +++ /dev/null @@ -1,23 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/account/index', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - this.submit = () => {}; - this.cancel = () => {}; - this.model = { type: 'password' }; - - await render(hbs` - `); - - assert.ok(find('form')); - }); -}); diff --git a/ui/core/tests/integration/components/form/account/password-test.js b/ui/core/tests/integration/components/form/account/password-test.js deleted file mode 100644 index 3ac6bf4ed..000000000 --- a/ui/core/tests/integration/components/form/account/password-test.js +++ /dev/null @@ -1,23 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/account/password', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - this.submit = () => {}; - this.cancel = () => {}; - this.model = { type: 'password' }; - - await render(hbs` - `); - - assert.ok(find('form')); - }); -}); diff --git a/ui/core/tests/integration/components/form/account/password/change-password-test.js b/ui/core/tests/integration/components/form/account/password/change-password-test.js deleted file mode 100644 index 54a6f61dd..000000000 --- a/ui/core/tests/integration/components/form/account/password/change-password-test.js +++ /dev/null @@ -1,26 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module( - 'Integration | Component | form/account/password/change-password', - function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - this.submit = () => {}; - this.cancel = () => {}; - this.model = { type: 'password' }; - - await render(hbs` - `); - - assert.ok(find('form')); - }); - } -); diff --git a/ui/core/tests/integration/components/form/account/password/set-password-test.js b/ui/core/tests/integration/components/form/account/password/set-password-test.js deleted file mode 100644 index ae4e1d355..000000000 --- a/ui/core/tests/integration/components/form/account/password/set-password-test.js +++ /dev/null @@ -1,26 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module( - 'Integration | Component | form/account/password/set-password', - function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - this.submit = () => {}; - this.cancel = () => {}; - this.model = { type: 'password' }; - - await render(hbs` - `); - - assert.ok(find('form')); - }); - } -); diff --git a/ui/core/tests/integration/components/form/auth-method-test.js b/ui/core/tests/integration/components/form/auth-method-test.js deleted file mode 100644 index 267e516b5..000000000 --- a/ui/core/tests/integration/components/form/auth-method-test.js +++ /dev/null @@ -1,23 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/auth-method', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - this.submit = () => {}; - this.cancel = () => {}; - this.model = { type: 'password' }; - - await render(hbs` - `); - - assert.ok(find('form')); - }); -}); diff --git a/ui/core/tests/integration/components/form/role/add-principals-test.js b/ui/core/tests/integration/components/form/role/add-principals-test.js deleted file mode 100644 index 4b6ed6a3d..000000000 --- a/ui/core/tests/integration/components/form/role/add-principals-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -// import { render } from '@ember/test-helpers'; -// import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/role/add-principals', function (hooks) { - setupRenderingTest(hooks); - - test('it renders', async function (assert) { - assert.expect(0); - }); -}); diff --git a/ui/core/tests/integration/components/form/user/add-accounts-test.js b/ui/core/tests/integration/components/form/user/add-accounts-test.js deleted file mode 100644 index ba96e4dba..000000000 --- a/ui/core/tests/integration/components/form/user/add-accounts-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -// import { render } from '@ember/test-helpers'; -// import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/user/add-accounts', function(hooks) { - setupRenderingTest(hooks); - - test('it renders', async function(assert) { - assert.expect(0); - }); -}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts-test.js new file mode 100644 index 000000000..7c2891d51 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/host-catalogs/host-catalog/hosts', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/hosts'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host-test.js new file mode 100644 index 000000000..d40fb98a9 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/host-catalogs/host-catalog/hosts/host', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/hosts/host'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index-test.js new file mode 100644 index 000000000..1f6b2379b --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/host/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/host-catalogs/host-catalog/hosts/host/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/hosts/host/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/index-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/index-test.js new file mode 100644 index 000000000..5bccae028 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/host-catalogs/host-catalog/hosts/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/hosts/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/new-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/new-test.js new file mode 100644 index 000000000..e7d31cca4 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/hosts/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/host-catalogs/host-catalog/hosts/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/hosts/new'); + assert.ok(route); + }); +});