diff --git a/ui/core/app/components/form/host-set/add-hosts/index.hbs b/ui/core/app/components/form/host-set/add-hosts/index.hbs new file mode 100644 index 000000000..1d467c2ef --- /dev/null +++ b/ui/core/app/components/form/host-set/add-hosts/index.hbs @@ -0,0 +1,56 @@ +{{#if this.hasAvailableHosts}} + + + + + + + + {{t "form.name.label"}} + {{t "form.type.label"}} + {{t "form.id.label"}} + + + + {{#each this.filteredHosts as |host|}} + + + + + {{host.name}} + + {{host.type}} + + + {{/each}} + + + + +{{/if}} + +{{#unless this.hasAvailableHosts}} + + + + {{t "resources.host-set.host.messages.add-none.description"}} + + + + {{t "actions.back"}} + + + +{{/unless}} diff --git a/ui/core/app/components/form/host-set/add-hosts/index.js b/ui/core/app/components/form/host-set/add-hosts/index.js new file mode 100644 index 000000000..4f1a8eb22 --- /dev/null +++ b/ui/core/app/components/form/host-set/add-hosts/index.js @@ -0,0 +1,57 @@ +import Component from '@glimmer/component'; +import { computed, action } from '@ember/object'; +import { A } from '@ember/array'; + +export default class FormHostSetAddHostsIndexComponent extends Component { + // =properties + + /** + * Array of selected host IDs. + * @type {EmberArray} + */ + selectedHostIDs = A(); + + /** + * Checks for unassigned hosts. + * @param {[HostModel]} filteredHosts + * @type {boolean} + */ + @computed('filteredHosts.length') + get hasAvailableHosts() { + return this.filteredHosts.length > 0; + } + + /** + * Hosts currently not assigned to current host set. + * @type {[HostModel]} + */ + @computed('args.{model,model.host_ids,hosts}') + get filteredHosts() { + const currentHostIDs = this.args.model.host_ids.map((host) => host.value); + return this.args.hosts.filter(({ id }) => !currentHostIDs.includes(id)); + } + + // =actions + + /** + * Toggle host selection + * @param {HostModel} host + */ + @action + toggleHost(host) { + if (!this.selectedHostIDs.includes(host.id)) { + this.selectedHostIDs.addObject(host.id); + } else { + this.selectedHostIDs.removeObject(host.id); + } + } + + /** + * Callback submit with selected host ids + * @param {requestCallback} fn + */ + @action + submit(fn) { + fn(this.selectedHostIDs); + } +} diff --git a/ui/core/app/router.js b/ui/core/app/router.js index ebed78060..30e882b22 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -73,7 +73,11 @@ Router.map(function () { }); this.route('host-sets', function() { this.route('new'); - this.route('host-set', { path: ':host_set_id' }, function() {}); + this.route('host-set', { path: ':host_set_id' }, function() { + this.route('hosts'); + this.route('add-hosts'); + this.route('create-and-add-host'); + }); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js new file mode 100644 index 000000000..afe7ce613 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js @@ -0,0 +1,92 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { hash } from 'rsvp'; +import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; + +export default class ScopesScopeHostCatalogsHostCatalogHostSetsHostSetAddHostsRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Empty out any previously loaded hosts. + */ + beforeModel() { + this.store.unloadAll('host'); + } + + /** + * Loads current host set and all hosts under the current host catalog. + * @return {Promise{HostSetModel,[HostModel]}} + */ + async model() { + const { id: host_catalog_id } = this.modelFor( + 'scopes.scope.host-catalogs.host-catalog' + ); + return hash({ + hostSet: this.modelFor( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set' + ), + hosts: this.store.query('host', { host_catalog_id }), + }); + } + + /** + * Renders the add-hosts-specific header template. + * Empties the actions and navigation outlets and renders a custom empty header. + * @override + */ + renderTemplate() { + super.renderTemplate(...arguments); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts/-header', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'header', + } + ); + + this.render('-empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'navigation', + }); + + this.render('-empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'actions', + }); + } + + // =actions + + /** + * Saves host IDs on the host set. + * @param {HostSetModel} hostSet + */ + @action + @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') + async addHosts(hostSet, hostIDs) { + await hostSet.addHosts(hostIDs); + await this.replaceWith( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set.hosts' + ); + } + + /** + * Redirect to hosts as if nothing ever happened. + */ + @action + cancel() { + this.replaceWith( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set.hosts' + ); + } +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.js new file mode 100644 index 000000000..a726302f2 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.js @@ -0,0 +1,85 @@ +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 { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; + +export default class ScopesScopeHostCatalogsHostCatalogHostSetsHostSetCreateAndAddHostRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Creates a new unsaved host in current host catalog scope. + * @return {Promise{HostSetModel,HostModel}} + */ + async 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 the create host-specific header template. + * Empties the actions and navigation outlets and renders a custom empty header. + * @override + */ + renderTemplate() { + super.renderTemplate(...arguments); + + this.render( + 'scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host/-header', + { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'header', + } + ); + + this.render('-empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'navigation', + }); + + this.render('-empty', { + into: 'scopes/scope/host-catalogs/host-catalog', + outlet: 'actions', + }); + } + + // =actions + + /** + * Saves host and add it to current host set. + * @param {HostSetModel,HostModel} model + */ + @action + @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') + async save(host) { + const hostSet = this.modelFor('scopes.scope.host-catalogs.host-catalog.host-sets.host-set'); + await host.save(); + await hostSet.addHost(host.id); + await this.replaceWith( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set.hosts' + ); + } + + /** + * Redirect to hosts in host set as if nothing ever happened. + */ + @action + cancel() { + this.replaceWith( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set.hosts' + ); + } +} diff --git a/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.js b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.js new file mode 100644 index 000000000..32c5b8911 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.js @@ -0,0 +1,55 @@ +import Route from '@ember/routing/route'; +import { hash, all } from 'rsvp'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; +import loading from 'ember-loading/decorator'; +import { confirm } from '../../../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; + +export default class ScopesScopeHostCatalogsHostCatalogHostSetsHostSetHostsRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Loads all hosts under the current host set. + * @return {Promise{HostSetModel,[HostModel]}} + */ + async model() { + const hostSet = this.modelFor( + 'scopes.scope.host-catalogs.host-catalog.host-sets.host-set' + ); + return hash({ + hostSet, + hosts: all( + hostSet.host_ids.map((host) => + this.store.findRecord('host', host.value, { reload: true }) + ) + ), + }); + } + + /** + * Remove a host from the current host set and redirect to hosts index. + * @param {HostSetModel} hostSet + * @param {HostModel} host + */ + @action + @loading + @confirm('questions.remove-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') + async removeHost(hostSet, host) { + const scopeID = this.modelFor('scopes.scope').id; + const hostCatalogID = this.modelFor( + 'scopes.scope.host-catalogs.host-catalog' + ).id; + await hostSet.removeHost(host.id, { + adapterOptions: { scopeID, hostCatalogID }, + }); + this.refresh(); + } +} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs index b49a0dd51..7eccc7b44 100644 --- a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs @@ -1,12 +1,10 @@ - {{!-- - - {{t 'resources.host-set.actions.create-host'}} + + {{t 'resources.host-set.actions.create-and-add-host'}} {{t 'resources.host-set.actions.add-hosts'}} - --}} {{t 'titles.details'}} - {{!-- {{t 'resources.host.title_plural'}} - --}} diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.hbs new file mode 100644 index 000000000..b185f9550 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts.hbs @@ -0,0 +1,8 @@ +{{title (t "resources.host-set.actions.add-hosts")}} + + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts/-header.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts/-header.hbs new file mode 100644 index 000000000..6ff650141 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts/-header.hbs @@ -0,0 +1,5 @@ +

+ {{t "resources.host-set.host.messages.add.title"}} + +

+

{{t "resources.host-set.host.messages.add.description"}}

diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.hbs new file mode 100644 index 000000000..3e7ed3f29 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host.hbs @@ -0,0 +1,7 @@ +{{title (t 'resources.host-set.actions.create-and-add-host')}} + + diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host/-header.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host/-header.hbs new file mode 100644 index 000000000..ee94f8ff0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host/-header.hbs @@ -0,0 +1,4 @@ +

+ {{t "resources.host-set.actions.create-and-add-host"}} + +

diff --git a/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.hbs b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.hbs new file mode 100644 index 000000000..d2ede5ddc --- /dev/null +++ b/ui/core/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts.hbs @@ -0,0 +1,73 @@ +{{#if @model.hosts}} + + + + + {{t "form.name.label"}} + {{t "form.type.label"}} + {{t "form.id.label"}} + {{t "titles.actions"}} + + + + {{#each @model.hosts as |host|}} + + + + {{host.displayName}} + +

{{host.description}}

+
+ + {{host.type}} + + + + {{host.id}} + + + + + + {{t "actions.remove"}} + + + +
+ {{/each}} +
+
+ +{{/if}} + +{{#unless @model.hosts}} + + + + {{t "resources.host-set.host.messages.none.description"}} + + {{!-- + + + {{t "resources.host-set.actions.create-and-add-host"}} + + --}} + + +{{/unless}} diff --git a/ui/core/tests/acceptance/host-catalogs/host-sets-test.js b/ui/core/tests/acceptance/host-catalogs/host-sets-test.js index 7f575a500..aee192343 100644 --- a/ui/core/tests/acceptance/host-catalogs/host-sets-test.js +++ b/ui/core/tests/acceptance/host-catalogs/host-sets-test.js @@ -17,7 +17,7 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - let getHostCount; + let getHostSetCount; const instances = { scopes: { @@ -69,7 +69,7 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { urls.unknownHostSet = `${urls.hostSets}/foo`; urls.newHostSet = `${urls.hostSets}/new`; // Generate resource couner - getHostCount = () => this.server.schema.hostSets.all().models.length; + getHostSetCount = () => this.server.schema.hostSets.all().models.length; authenticateSession({}); }); @@ -92,21 +92,21 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { test('can create new host', async function (assert) { assert.expect(1); - const count = getHostCount(); + const count = getHostSetCount(); await visit(urls.newHostSet); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); - assert.equal(getHostCount(), count + 1); + assert.equal(getHostSetCount(), count + 1); }); test('can cancel create new host', async function (assert) { assert.expect(2); - const count = getHostCount(); + const count = getHostSetCount(); await visit(urls.newHostSet); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="button"]'); assert.equal(currentURL(), urls.hostSets); - assert.equal(getHostCount(), count); + assert.equal(getHostSetCount(), count); }); test('saving a new host set with invalid fields displays error messages', async function (assert) { @@ -232,10 +232,10 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { test('can delete host', async function (assert) { assert.expect(1); - const count = getHostCount(); + const count = getHostSetCount(); await visit(urls.hostSet); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); - assert.equal(getHostCount(), count - 1); + assert.equal(getHostSetCount(), count - 1); }); test('can accept delete host set via dialog', async function (assert) { @@ -243,10 +243,10 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { const confirmService = this.owner.lookup('service:confirm'); confirmService.enabled = true; confirmService.confirm = sinon.fake.returns(resolve()); - const count = getHostCount(); + const count = getHostSetCount(); await visit(urls.hostSet); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); - assert.equal(getHostCount(), count - 1); + assert.equal(getHostSetCount(), count - 1); assert.ok(confirmService.confirm.calledOnce); }); @@ -255,10 +255,10 @@ module('Acceptance | host-catalogs | host sets', function (hooks) { const confirmService = this.owner.lookup('service:confirm'); confirmService.enabled = true; confirmService.confirm = sinon.fake.returns(reject()); - const count = getHostCount(); + const count = getHostSetCount(); await visit(urls.hostSet); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); - assert.equal(getHostCount(), count); + assert.equal(getHostSetCount(), count); assert.ok(confirmService.confirm.calledOnce); }); diff --git a/ui/core/tests/acceptance/host-catalogs/host-sets/hosts-test.js b/ui/core/tests/acceptance/host-catalogs/host-sets/hosts-test.js new file mode 100644 index 000000000..224b479b4 --- /dev/null +++ b/ui/core/tests/acceptance/host-catalogs/host-sets/hosts-test.js @@ -0,0 +1,240 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, click, find, findAll, 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 { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | host-catalogs | host-sets | hosts', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + let getHostSetHostCount; + + 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, + hostSets: null, + hostSet: null, + hostSetHosts: null, + addHosts: 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, + }, 'withChildren'); + instances.hostSet = this.server.schema.hostSets.all().models[0]; + // 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.hostSets = `${urls.hostCatalog}/host-sets`; + urls.hostSet = `${urls.hostSets}/${instances.hostSet.id}`; + urls.hostSetHosts = `${urls.hostSet}/hosts`; + urls.addHosts = `${urls.hostSet}/add-hosts`; + urls.createAndAddHost = `${urls.hostSet}/create-and-add-host`; + // Generate resource couner + getHostSetHostCount = () => + this.server.schema.hostSets.all().models[0].hosts.length; + authenticateSession({}); + }); + + test('visiting host set hosts', async function (assert) { + assert.expect(2); + await visit(urls.hostSetHosts); + await a11yAudit(); + assert.equal(currentURL(), urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, getHostSetHostCount()); + }); + + test('can remove a host', async function (assert) { + assert.expect(2); + const count = getHostSetHostCount(); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, count); + await click('tbody tr .rose-dropdown-button-danger'); + assert.equal(findAll('tbody tr').length, count - 1); + }); + + test('shows error message on host remove error', async function (assert) { + assert.expect(2); + this.server.post('/host-sets/:idMethod', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: {}, + } + ); + }); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, getHostSetHostCount()); + await click('tbody tr .rose-dropdown-button-danger'); + assert.ok(find('[role="alert"]')); + }); + + test('visiting add hosts', async function(assert) { + assert.expect(1); + await visit(urls.addHosts); + await a11yAudit(); + assert.equal(currentURL(), urls.addHosts); + }); + + test('select and save hosts to add', async function (assert) { + assert.expect(3); + instances.hostSet.update({ hostIds: [] }); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, 0); + await click('.rose-layout-page-actions a:nth-child(2)') + assert.equal(currentURL(), urls.addHosts); + // 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.hostSetHosts); + assert.equal(findAll('tbody tr').length, 1); + }); + + test('select and cancel hosts to add', async function (assert) { + assert.expect(4); + const count = getHostSetHostCount(); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, count); + await click('tbody tr .rose-dropdown-button-danger'); + assert.equal(findAll('tbody tr').length, count - 1); + await click('.rose-layout-page-actions a:nth-child(2)') + assert.equal(currentURL(), urls.addHosts); + await click('tbody label'); + await click('form [type="button"]'); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, count - 1); + }); + + test('shows error message on host add error', async function (assert) { + assert.expect(1); + this.server.post('/host-sets/:idMethod', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: {}, + } + ); + }); + instances.hostSet.update({ hostIds: [] }); + await visit(urls.addHosts); + await click('tbody label'); + await click('form [type="submit"]'); + assert.ok(find('[role="alert"]')); + }); + + test('visiting host creation from a host set', async function (assert) { + assert.expect(1); + await visit(urls.createAndAddHost); + await a11yAudit(); + assert.equal(currentURL(), urls.createAndAddHost); + }); + + test('create and add host to host set', async function (assert) { + assert.expect(3); + instances.hostSet.update({ hostIds: [] }); + await visit(urls.hostSet); + assert.equal(findAll('tbody tr').length, 0); + await click('.rose-layout-page-actions a:nth-child(1)') + assert.equal(currentURL(), urls.createAndAddHost); + await fillIn('[name="name"]', 'Test Name'); + await fillIn('[name="description"]', 'description'); + await click('form [type="submit"]:not(:disabled)'); + await visit(urls.hostSetHosts); + assert.equal(findAll('tbody tr').length, 1); + }); + + test('create and cancel host add to host set', async function (assert) { + assert.expect(1); + await visit(urls.createAndAddHost); + await click('form [type="button"]'); + assert.equal(currentURL(), urls.hostSetHosts); + }); + + test('shows error message on host creation error', async function (assert) { + assert.expect(1); + this.server.post('/hosts', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: {}, + } + ); + }); + instances.hostSet.update({ hostIds: [] }); + await visit(urls.createAndAddHost); + await fillIn('[name="name"]', 'New Host'); + await click('form [type="submit"]'); + assert.ok(find('[role="alert"]')); + }); + + test('shows error message on host addition to host set error', async function (assert) { + assert.expect(1); + this.server.post('/host-sets/:idMethod', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: {}, + } + ); + }); + instances.hostSet.update({ hostIds: [] }); + await visit(urls.createAndAddHost); + await fillIn('[name="name"]', 'New Host'); + await click('form [type="submit"]'); + assert.ok(find('[role="alert"]')); + }); + +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts-test.js new file mode 100644 index 000000000..cfcfcffdb --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-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/host-sets/host-set/add-hosts', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/host-sets/host-set/add-hosts'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host-test.js new file mode 100644 index 000000000..9c8d8b120 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-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/host-sets/host-set/create-and-add-host', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/host-sets/host-set/create-and-add-host'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts-test.js b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts-test.js new file mode 100644 index 000000000..728788fe7 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/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/host-sets/host-set/hosts', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/host-catalogs/host-catalog/host-sets/host-set/hosts'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/resources/en-us.yaml b/ui/core/translations/resources/en-us.yaml index b753327bc..e24600300 100644 --- a/ui/core/translations/resources/en-us.yaml +++ b/ui/core/translations/resources/en-us.yaml @@ -67,7 +67,7 @@ host-set: add: Add Host Set create: New Host Set delete: Delete Host Set - create-host: Create and Add Host + create-and-add-host: Create and Add Host add-hosts: Add Existing Host messages: welcome: