diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index b7d29d2b8..ceb0277ba 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -431,9 +431,17 @@ function routes() { return hostSets.where({ hostCatalogId }); } ); + this.post('/host-sets', function ({ hostCatalogs, hostSets }) { const attrs = this.normalizedRequestAttrs(); const hostCatalog = hostCatalogs.find(attrs.hostCatalogId); + //this needs to be attached while creating a plugin type host set to mimic API + if (hostCatalog.plugin) { + attrs.type = 'plugin'; + attrs.plugin = { + name: hostCatalog.plugin.name, + }; + } attrs.scopeId = hostCatalog.scope.id; return hostSets.create(attrs); }); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 7e3e775e7..7f7ab560b 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -233,7 +233,7 @@ host-set: help: Create a filter to select resources using values such as tag name or tag value. The Azure filter format is:tagName eq ‘’ and tagValue eq ‘'. For example:tagName eq 'application' and tagValue eq 'production' preferred_endpoints: label: Preferred Endpoints - help: Optional. Preferred address at which the host should be accessed when multiple options are present, in cidr: or dns: format. + help: Preferred address at which the host should be accessed when multiple options are present, in cidr: or dns: format. messages: welcome: title: Welcome to Host Sets diff --git a/ui/admin/app/components/form/auth-method/ldap/index.hbs b/ui/admin/app/components/form/auth-method/ldap/index.hbs index 51661cc8a..a7d3557f4 100644 --- a/ui/admin/app/components/form/auth-method/ldap/index.hbs +++ b/ui/admin/app/components/form/auth-method/ldap/index.hbs @@ -83,7 +83,7 @@ }} {{#let (unique-id) as |valueHeaderID|}} - + <:head as |H|> {{t 'titles.value'}} @@ -424,7 +424,7 @@ }} {{#let (unique-id) (unique-id) as |fromHeaderID toHeaderID|}} - + <:head as |H|> {{t diff --git a/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs b/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs index 860e899fe..75a46f0c8 100644 --- a/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs +++ b/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs @@ -236,55 +236,72 @@ {{/if}} - - - {{t 'resources.credential-library.form.critical_options.label'}} - - - {{t 'resources.credential-library.form.critical_options.help'}} - - {{#if @model.errors.critical_options}} - - {{#each @model.errors.critical_options as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - + <:fieldset as |F|> + + {{t 'resources.credential-library.form.critical_options.label'}} + + + {{t 'resources.credential-library.form.critical_options.help'}} + - + {{#each @model.errors.critical_options as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + <:field as |F|> + + + + + + - - {{t 'resources.credential-library.form.extensions.label'}} - - - {{t 'resources.credential-library.form.extensions.help'}} - - {{#if @model.errors.extensions}} - - {{#each @model.errors.extensions as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - + <:fieldset as |F|> + + {{t 'resources.credential-library.form.extensions.label'}} + + + {{t 'resources.credential-library.form.extensions.help'}} + + + {{#if @model.errors.extensions}} + + {{#each @model.errors.extensions as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + <:field as |F|> + + + + {{#if (can 'save model' @model)}} - - {{yield F}} - - {{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}} - - - <:head as |H|> - - {{t 'form.key.label'}} - {{#unless @hasNoValue}} - {{t 'form.value.label'}} - {{/unless}} - - {{#if @removeOptionByIndex}} - {{t 'titles.actions'}} - {{/if}} - - - - <:body as |B|> - - {{#each @options as |option index|}} - - - - - {{#unless @hasNoValue}} - - - - {{/unless}} - - - {{#if @removeOptionByIndex}} - - {{/if}} - - - {{/each}} - - - - - - {{#unless @hasNoValue}} - - - - {{/unless}} - - - - - - - - - {{/let}} - \ No newline at end of file diff --git a/ui/admin/app/components/form/field/list-wrapper/index.hbs b/ui/admin/app/components/form/field/list-wrapper/index.hbs new file mode 100644 index 000000000..e0e63d5ab --- /dev/null +++ b/ui/admin/app/components/form/field/list-wrapper/index.hbs @@ -0,0 +1,29 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + + + + {{yield F to='fieldset'}} + + + {{yield + (hash + KeyValue=(component + 'form/field/list-wrapper/key-value' disabled=@disabled + ) + TextInput=(component + 'form/field/list-wrapper/text-input' disabled=@disabled + ) + ) + to='field' + }} + + + \ No newline at end of file diff --git a/ui/admin/app/components/form/field/list-wrapper/key-value/index.hbs b/ui/admin/app/components/form/field/list-wrapper/key-value/index.hbs new file mode 100644 index 000000000..b62266e17 --- /dev/null +++ b/ui/admin/app/components/form/field/list-wrapper/key-value/index.hbs @@ -0,0 +1,94 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} +{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}} + + + <:head as |H|> + + {{t 'form.key.label'}} + {{t 'form.value.label'}} + + {{#if @removeOptionByIndex}} + {{t 'titles.actions'}} + {{/if}} + + + + <:body as |B|> + + {{#each @options as |option index|}} + + + + + + + + + + + {{#if @removeOptionByIndex}} + + {{/if}} + + + {{/each}} + + + + + + + + + + + + + + + + + + +{{/let}} \ No newline at end of file diff --git a/ui/admin/app/components/form/field/key-value-list/index.js b/ui/admin/app/components/form/field/list-wrapper/key-value/index.js similarity index 92% rename from ui/admin/app/components/form/field/key-value-list/index.js rename to ui/admin/app/components/form/field/list-wrapper/key-value/index.js index 533407b34..e1294ad1f 100644 --- a/ui/admin/app/components/form/field/key-value-list/index.js +++ b/ui/admin/app/components/form/field/list-wrapper/key-value/index.js @@ -30,7 +30,7 @@ export default class MappingListComponent extends Component { @action addOption() { assert( - '[boundary-admin-key-value-list-field] `@addOption` is required.', + '[boundary-admin-list-wrapper] `@addOption` is required.', this.args.addOption ); diff --git a/ui/admin/app/components/form/field/list-wrapper/text-input/index.hbs b/ui/admin/app/components/form/field/list-wrapper/text-input/index.hbs new file mode 100644 index 000000000..95d423941 --- /dev/null +++ b/ui/admin/app/components/form/field/list-wrapper/text-input/index.hbs @@ -0,0 +1,66 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + +{{#let (unique-id) as |valueHeaderID|}} + + + + <:body as |B|> + + {{#each @options as |option index|}} + + + + + + + {{#if @removeOptionByIndex}} + + {{/if}} + + + {{/each}} + + + + + + + + + + + + + + + +{{/let}} \ No newline at end of file diff --git a/ui/admin/app/components/form/field/list-wrapper/text-input/index.js b/ui/admin/app/components/form/field/list-wrapper/text-input/index.js new file mode 100644 index 000000000..c207687b9 --- /dev/null +++ b/ui/admin/app/components/form/field/list-wrapper/text-input/index.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { assert } from '@ember/debug'; + +export default class MappingListTextInputComponent extends Component { + // =attributes + + /** + * @type {string} + */ + @tracked newOptionValue = ''; + + // =actions + + /** + * If a new input is entered and an addOption method was specified, + * calls addOption with the new input. Resets previous value. + */ + + @action + addOption() { + assert( + '[boundary-admin-list-wrapper] `@addOption` is required.', + this.args.addOption + ); + + if (this.newOptionValue) { + this.args.addOption({ + value: this.newOptionValue, + }); + } + + this.newOptionValue = ''; + } +} diff --git a/ui/admin/app/components/form/host-set/aws/index.hbs b/ui/admin/app/components/form/host-set/aws/index.hbs index 52616e421..a1a2238d3 100644 --- a/ui/admin/app/components/form/host-set/aws/index.hbs +++ b/ui/admin/app/components/form/host-set/aws/index.hbs @@ -69,178 +69,65 @@ - - <:title>{{t 'resources.host-set.form.preferred_endpoints.label'}} - <:body> + <:fieldset as |F|> + + {{t 'resources.host-set.form.preferred_endpoints.label'}} + + + {{t 'resources.host-set.form.preferred_endpoints.help'}} + + {{#if @model.errors.preferred_endpoints}} - + {{#each @model.errors.preferred_endpoints as |error|}} - {{error.message}} + {{error.message}} {{/each}} - + {{/if}} - - - - {{t 'titles.value'}} - - - - - {{#each @model.preferred_endpoints as |endpoint i|}} - - - - - - - - - {{t 'actions.remove'}} - - - - {{/each}} + + <:field as |F|> + + + - - - - - - - {{t 'actions.add'}} - - - - - - - + + <:fieldset as |F|> + + {{t 'resources.host-set.form.filter.label'}} + + + {{t 'resources.host-set.form.filter.aws.help'}} + - - <:title>{{t 'resources.host-set.form.filter.label'}} - <:description>{{t 'resources.host-set.form.filter.aws.help'}} - <:body> {{#if @model.errors.filters}} - + {{#each @model.errors.filters as |error|}} - {{error.message}} + {{error.message}} {{/each}} - + {{/if}} - - - - {{t 'titles.value'}} - - - - - {{#each @model.filters as |filter i|}} - - - - - - - - - {{t 'actions.remove'}} - - - - {{/each}} - - - - - - - - {{t 'actions.add'}} - - - - - - - + + <:field as |F|> + + + - - <:title>{{t 'resources.host-set.form.preferred_endpoints.label'}} - <:body> - {{#if @model.errors.preferred_endpoints}} - - {{#each @model.errors.preferred_endpoints as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - - - - {{t 'titles.value'}} - - - - - {{#each @model.preferred_endpoints as |endpoint i|}} - - - - - - - - - {{t 'actions.remove'}} - - - - {{/each}} + <:fieldset as |F|> + + {{t 'resources.host-set.form.preferred_endpoints.label'}} + + + {{t 'resources.host-set.form.preferred_endpoints.help'}} + - - - - - - - {{t 'actions.add'}} - - - - - - - + {{#if @model.errors.preferred_endpoints}} + + {{#each @model.errors.preferred_endpoints as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + <:field as |F|> + + + i !== index); + set(this.args.model, field, newArray); + } +} diff --git a/ui/admin/app/components/form/managed-group/ldap/index.hbs b/ui/admin/app/components/form/managed-group/ldap/index.hbs index 4d90fb5b3..470b98598 100644 --- a/ui/admin/app/components/form/managed-group/ldap/index.hbs +++ b/ui/admin/app/components/form/managed-group/ldap/index.hbs @@ -61,7 +61,7 @@ }} {{#let (unique-id) as |valueHeaderID|}} - + <:head as |H|> {{t 'titles.value'}} diff --git a/ui/admin/app/components/form/storage-bucket/index.hbs b/ui/admin/app/components/form/storage-bucket/index.hbs index 71fe2b064..ae5c7c1b2 100644 --- a/ui/admin/app/components/form/storage-bucket/index.hbs +++ b/ui/admin/app/components/form/storage-bucket/index.hbs @@ -366,30 +366,37 @@ {{/if}} - - - {{t 'resources.storage-bucket.form.role_tags.label'}} - - - {{t 'resources.storage-bucket.form.role_tags.help'}} - - {{#if @model.errors.role_tags}} - - {{#each @model.errors.role_tags as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - + <:fieldset as |F|> + + {{t 'resources.storage-bucket.form.role_tags.label'}} + + + {{t 'resources.storage-bucket.form.role_tags.label'}} + + + {{#if @model.errors.role_tags}} + + {{#each @model.errors.role_tags as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + <:field as |F|> + + + {{/if}} i !== index); - hostSet.set(property, array); - } } diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/new.js b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/new.js index 3c860d036..e2f4d0b1b 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/new.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/host-catalog/host-sets/new.js @@ -4,7 +4,6 @@ */ import Route from '@ember/routing/route'; -import { action } from '@ember/object'; import { inject as service } from '@ember/service'; export default class ScopesScopeHostCatalogsHostCatalogHostSetsNewRoute extends Route { @@ -27,30 +26,4 @@ export default class ScopesScopeHostCatalogsHostCatalogHostSetsNewRoute extends host_catalog_id, }); } - - /** - * Adds a string item to array `property` on the passed `filter`. - - * @param {hostSetModel} hostSet - * @param {string} property - * @param {string} value - */ - @action - async addStringItem(hostSet, property, value) { - const existingArray = hostSet[property] ?? []; - const array = [...existingArray, { value }]; - hostSet.set(property, array); - } - /** - * Removes an item from array `property` at `index` on the - * passed `hostSet`. - * @param {hostSetModel} hostSet - * @param {string} property - * @param {number} index - */ - @action - async removeItemByIndex(hostSet, property, index) { - const array = hostSet.get(property).filter((item, i) => i !== index); - hostSet.set(property, array); - } } diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 6382c981e..6a9eaf6b1 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -687,7 +687,7 @@ } } -.key-value-list { +.list-wrapper-field { border: 0; /* stylelint-disable selector-class-pattern */ diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/index.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/index.hbs index 9bc868f81..0f11a226b 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/index.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/index.hbs @@ -28,8 +28,6 @@ @edit={{route-action 'edit' @model}} @submit={{route-action 'save' @model}} @cancel={{route-action 'cancel' @model}} - @removeItemByIndex={{route-action 'removeItemByIndex' @model}} - @addStringItem={{route-action 'addStringItem' @model}} /> \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/new.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/new.hbs index b4c446d6b..aefc3b8b4 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/new.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/new.hbs @@ -23,8 +23,6 @@ @model={{@model}} @submit={{route-action 'save' @model}} @cancel={{route-action 'cancel' @model}} - @removeItemByIndex={{route-action 'removeItemByIndex' @model}} - @addStringItem={{route-action 'addStringItem' @model}} /> \ No newline at end of file diff --git a/ui/admin/tests/acceptance/host-catalogs/host-sets/create-test.js b/ui/admin/tests/acceptance/host-catalogs/host-sets/create-test.js index fb89138b6..f59237c97 100644 --- a/ui/admin/tests/acceptance/host-catalogs/host-sets/create-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/host-sets/create-test.js @@ -19,6 +19,18 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + const PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR = + '[name="preferred_endpoints"] input'; + const PREFERRED_ENDPOINT_BUTTON_SELECTOR = + '[name="preferred_endpoints"] button'; + const FILTER_TEXT_INPUT_SELECTOR = '[name="filters"] input'; + const FILTER_BUTTON_SELECTOR = '[name="filters"] button'; + const AZURE_FILTER_SELECTOR = '[name="filter"]'; + const SYNC_INTERVAL_SELECTOR = '[name="sync_interval_seconds"]'; + const SUBMIT_BTN_SELECTOR = '.rose-form-actions [type="submit"]'; + const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]'; + const NAME_SELECTOR = '[name="name"]'; + let getHostSetCount; const instances = { @@ -79,8 +91,8 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { assert.expect(1); const count = getHostSetCount(); await visit(urls.newHostSet); - await fillIn('[name="name"]', 'random string'); - await click('[type="submit"]'); + await fillIn(NAME_SELECTOR, 'random string'); + await click(SUBMIT_BTN_SELECTOR); assert.strictEqual(getHostSetCount(), count + 1); }); @@ -100,18 +112,19 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/new` ); const name = 'aws host set'; - await fillIn('[name="name"]', name); - await fillIn('[name="preferred_endpoints"]', 'endpoint'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); - await fillIn('[name="filters"]', 'filter'); - await click('form fieldset:nth-of-type(2) [title="Add"]'); - await fillIn('[name="sync_interval_seconds"]', 10); - await click('[type="submit"]'); + await fillIn(NAME_SELECTOR, name); + await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'endpoint'); + await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR); + await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'filter_test'); + await click(FILTER_BUTTON_SELECTOR); + + await fillIn(SYNC_INTERVAL_SELECTOR, 10); + await click(SUBMIT_BTN_SELECTOR); assert.strictEqual(getHostSetCount(), count + 1); const hostSet = this.server.schema.hostSets.findBy({ name }); assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']); - assert.deepEqual(hostSet.attributes.filters, ['filter']); + assert.deepEqual(hostSet.attributes.filters, ['filter_test']); assert.deepEqual(hostSet.syncIntervalSeconds, 10); }); @@ -131,12 +144,12 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/new` ); const name = 'azure host set'; - await fillIn('[name="name"]', name); - await fillIn('[name="preferred_endpoints"]', 'endpoint'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); - await fillIn('[name="filter"]', 'filter'); - await fillIn('[name="sync_interval_seconds"]', 10); - await click('[type="submit"]'); + await fillIn(NAME_SELECTOR, name); + await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'endpoint'); + await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR); + await fillIn(AZURE_FILTER_SELECTOR, 'filter'); + await fillIn(SYNC_INTERVAL_SELECTOR, 10); + await click(SUBMIT_BTN_SELECTOR); assert.strictEqual(getHostSetCount(), count + 1); const hostSet = this.server.schema.hostSets.findBy({ name }); @@ -173,8 +186,8 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { assert.expect(2); const count = getHostSetCount(); await visit(urls.newHostSet); - await fillIn('[name="name"]', 'random string'); - await click('.rose-form-actions [type="button"]'); + await fillIn(NAME_SELECTOR, 'random string'); + await click(CANCEL_BTN_SELECTOR); assert.strictEqual(currentURL(), urls.hostSets); assert.strictEqual(getHostSetCount(), count); }); @@ -201,7 +214,7 @@ module('Acceptance | host-catalogs | host sets | create', function (hooks) { ); }); await visit(urls.newHostSet); - await click('[type="submit"]'); + await click(SUBMIT_BTN_SELECTOR); assert.ok( find('[role="alert"]').textContent.trim(), 'The request was invalid.' diff --git a/ui/admin/tests/acceptance/host-catalogs/host-sets/update-test.js b/ui/admin/tests/acceptance/host-catalogs/host-sets/update-test.js index 8356bc656..9920b5bb8 100644 --- a/ui/admin/tests/acceptance/host-catalogs/host-sets/update-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/host-sets/update-test.js @@ -26,6 +26,20 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + const PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR = + '[name="preferred_endpoints"] input'; + const PREFERRED_ENDPOINT_BUTTON_SELECTOR = + '[name="preferred_endpoints"] button'; + const PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR = + '[name="preferred_endpoints"] [data-test-remove-button]'; + const FILTER_TEXT_INPUT_SELECTOR = '[name="filters"] input'; + const FILTER_BUTTON_SELECTOR = '[name="filters"] button'; + const FILTER_REMOVE_BUTTON_SELECTOR = + '[name="filters"] [data-test-remove-button]'; + const SYNC_INTERVAL_SELECTOR = '[name="sync_interval_seconds"]'; + const SUBMIT_BTN_SELECTOR = '.rose-form-actions [type="submit"]'; + const AZURE_FILTER_SELECTOR = '[name="filter"]'; + const EDIT_BUTTON_SELECTOR = 'form .rose-form-actions [type="button"]'; const instances = { scopes: { global: null, @@ -33,6 +47,10 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { project: null, hostCatalog: null, host: null, + awsHostCatalog: null, + awsHostSet: null, + azureHostCatalog: null, + azureHostSet: null, }, }; const urls = { @@ -45,6 +63,8 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { hostSet: null, unknownHostSet: null, newHostSet: null, + awshostSet: null, + azureHostSet: null, }; hooks.beforeEach(function () { @@ -61,10 +81,36 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { instances.hostCatalog = this.server.create('host-catalog', { scope: instances.scopes.project, }); + instances.hostSet = this.server.create('host-set', { scope: instances.scopes.project, hostCatalog: instances.hostCatalog, }); + instances.awsHostCatalog = this.server.create('host-catalog', { + scope: instances.scopes.project, + type: 'plugin', + plugin: { + id: `plugin-id-1`, + name: 'aws', + }, + }); + instances.awsHostSet = this.server.create('host-set', { + scope: instances.scopes.project, + hostCatalog: instances.awsHostCatalog, + }); + + instances.azureHostCatalog = this.server.create('host-catalog', { + scope: instances.scopes.project, + type: 'plugin', + plugin: { + id: `plugin-id-1`, + name: 'azure', + }, + }); + instances.azureHostSet = this.server.create('host-set', { + scope: instances.scopes.project, + hostCatalog: instances.azureHostCatalog, + }); // Generate route URLs for resources urls.globalScope = `/scopes/global/scopes`; urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`; @@ -75,6 +121,8 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { urls.hostSet = `${urls.hostSets}/${instances.hostSet.id}`; urls.unknownHostSet = `${urls.hostSets}/foo`; urls.newHostSet = `${urls.hostSets}/new`; + urls.awshostSet = `${urls.hostCatalogs}/${instances.awsHostCatalog.id}/host-sets/${instances.awsHostSet.id}`; + urls.azureHostSet = `${urls.hostCatalogs}/${instances.azureHostCatalog.id}/host-sets/${instances.azureHostSet.id}`; // Generate resource couner authenticateSession({}); }); @@ -101,7 +149,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { ); }); await visit(urls.newHostSet); - await click('[type="submit"]'); + await click(SUBMIT_BTN_SELECTOR); assert.ok( find('[role="alert"]').textContent.trim(), 'The request was invalid.' @@ -116,9 +164,9 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { assert.expect(3); assert.notEqual(instances.hostSet.name, 'random string'); await visit(urls.hostSet); - await click('form [type="button"]', 'Activate edit mode'); + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); await fillIn('[name="name"]', 'random string'); - await click('.rose-form-actions [type="submit"]'); + await click(SUBMIT_BTN_SELECTOR); assert.strictEqual(currentURL(), urls.hostSet); assert.strictEqual( this.server.schema.hostSets.first().name, @@ -127,96 +175,89 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { }); test('can save changes to an existing aws host-set', async function (assert) { - assert.expect(5); - instances.hostCatalog = this.server.create('host-catalog', { - scope: instances.scopes.project, - type: 'plugin', - plugin: { - id: `plugin-id-1`, - name: 'aws', - }, - }); - instances.hostSet = this.server.create('host-set', { - scope: instances.scopes.project, - hostCatalog: instances.hostCatalog, - }); - urls.hostSet = `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/${instances.hostSet.id}`; - await visit(urls.hostSet); + assert.expect(7); - await click( - 'form .rose-form-actions [type="button"]', - 'Activate edit mode' - ); + await visit(urls.awshostSet); + + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); const name = 'aws host set'; await fillIn('[name="name"]', name); - // Remove all the preferred endpoints - await Promise.all( - findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) => - click(element) - ) - ); - await fillIn('[name="preferred_endpoints"]', 'endpoint'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); - // Remove all the filters - await Promise.all( - findAll('form fieldset:nth-of-type(2) [title="Remove"]').map((element) => - click(element) - ) - ); - await fillIn('[name="filters"]', 'filter'); - await click('form fieldset:nth-of-type(2) [title="Add"]'); - await fillIn('[name="sync_interval_seconds"]', 10); - await click('.rose-form-actions [type="submit"]'); - assert.strictEqual(currentURL(), urls.hostSet); + const endpointList = await Promise.all( + findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR) + ); + + for (const element of endpointList) { + await click(element); + } + + assert.strictEqual( + findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR).length, + 0 + ); + + await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'sample endpoint'); + await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR); + + // Remove all the filters + const filterList = await Promise.all( + findAll(FILTER_REMOVE_BUTTON_SELECTOR) + ); + for (const element of filterList) { + await click(element); + } + + assert.strictEqual(findAll(FILTER_REMOVE_BUTTON_SELECTOR).length, 0); + await fillIn(FILTER_TEXT_INPUT_SELECTOR, 'sample filters'); + await click(FILTER_BUTTON_SELECTOR); + + await fillIn(SYNC_INTERVAL_SELECTOR, 10); + + await click(SUBMIT_BTN_SELECTOR); + + assert.strictEqual(currentURL(), urls.awshostSet); const hostSet = this.server.schema.hostSets.findBy({ name }); assert.strictEqual(hostSet.name, name); - assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']); - assert.deepEqual(hostSet.attributes.filters, ['filter']); + assert.deepEqual(hostSet.preferredEndpoints, ['sample endpoint']); + assert.deepEqual(hostSet.attributes.filters, ['sample filters']); assert.deepEqual(hostSet.syncIntervalSeconds, 10); }); test('can save changes to an existing azure host-set', async function (assert) { - assert.expect(5); - instances.hostCatalog = this.server.create('host-catalog', { - scope: instances.scopes.project, - type: 'plugin', - plugin: { - id: `plugin-id-1`, - name: 'azure', - }, - }); - instances.hostSet = this.server.create('host-set', { - scope: instances.scopes.project, - hostCatalog: instances.hostCatalog, - }); - urls.hostSet = `${urls.hostCatalogs}/${instances.hostCatalog.id}/host-sets/${instances.hostSet.id}`; - await visit(urls.hostSet); + assert.expect(6); - await click( - 'form .rose-form-actions [type="button"]', - 'Activate edit mode' - ); + await visit(urls.azureHostSet); + + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); const name = 'azure host set'; await fillIn('[name="name"]', name); // Remove all the preferred endpoints - await Promise.all( - findAll('form fieldset:nth-of-type(1) [title="Remove"]').map((element) => - click(element) - ) - ); - await fillIn('[name="preferred_endpoints"]', 'endpoint'); - await click('form fieldset:nth-of-type(1) [title="Add"]'); - await fillIn('[name="filter"]', 'filter'); - await fillIn('[name="sync_interval_seconds"]', 10); - await click('.rose-form-actions [type="submit"]'); - assert.strictEqual(currentURL(), urls.hostSet); + const endpointList = await Promise.all( + findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR) + ); + + for (const element of endpointList) { + await click(element); + } + + assert.strictEqual( + findAll(PREFERRED_ENDPOINT_REMOVE_BUTTON_SELECTOR).length, + 0 + ); + await fillIn(PREFERRED_ENDPOINT_TEXT_INPUT_SELECTOR, 'sample endpoints'); + await click(PREFERRED_ENDPOINT_BUTTON_SELECTOR); + + await fillIn(AZURE_FILTER_SELECTOR, 'filter'); + await fillIn(SYNC_INTERVAL_SELECTOR, 10); + await click(SUBMIT_BTN_SELECTOR); + + assert.strictEqual(currentURL(), urls.azureHostSet); const hostSet = this.server.schema.hostSets.findBy({ name }); assert.strictEqual(hostSet.name, name); - assert.deepEqual(hostSet.preferredEndpoints, ['endpoint']); + assert.deepEqual(hostSet.preferredEndpoints, ['sample endpoints']); assert.deepEqual(hostSet.attributes.filter, 'filter'); assert.deepEqual(hostSet.syncIntervalSeconds, 10); }); @@ -232,7 +273,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { test('can cancel changes to existing host-set', async function (assert) { assert.expect(2); await visit(urls.hostSet); - await click('form [type="button"]', 'Activate edit mode'); + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="button"]'); assert.notEqual(instances.hostSet.name, 'random string'); @@ -261,9 +302,9 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { ); }); await visit(urls.hostSet); - await click('form [type="button"]', 'Activate edit mode'); + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); await fillIn('[name="name"]', 'random string'); - await click('[type="submit"]'); + await click(SUBMIT_BTN_SELECTOR); assert.ok( find('[role="alert"]').textContent.trim(), 'The request was invalid.' @@ -280,7 +321,7 @@ module('Acceptance | host-catalogs | host sets | update', function (hooks) { confirmService.enabled = true; assert.notEqual(instances.hostSet.name, 'random string'); await visit(urls.hostSet); - await click('form [type="button"]', 'Activate edit mode'); + await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode'); await fillIn('[name="name"]', 'random string'); assert.strictEqual(currentURL(), urls.hostSet); try { diff --git a/ui/admin/tests/integration/components/form/field/key-value-list-test.js b/ui/admin/tests/integration/components/form/field/key-value-list-test.js deleted file mode 100644 index 154eb9efc..000000000 --- a/ui/admin/tests/integration/components/form/field/key-value-list-test.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { setupIntl } from 'ember-intl/test-support'; - -module('Integration | Component | key-value-list', function (hooks) { - setupRenderingTest(hooks); - setupIntl(hooks); - - test('it renders fields', async function (assert) { - await render(hbs` - - Label - Help - - Error! - - - `); - - assert.dom('tbody tr').exists({ count: 1 }); - assert.dom('legend').exists().hasText('Label'); - assert.dom('.hds-form-helper-text').exists().hasText('Help'); - assert.dom('.hds-form-error__message').exists().hasText('Error!'); - assert.dom('button').exists().hasText('Add'); - }); - - test('it renders multiple options', async function (assert) { - this.options = [ - { key: 'one', value: 'two' }, - { key: 'three', value: 'four' }, - ]; - - // Template block usage: - await render(hbs` - - `); - - assert.dom('tbody tr').exists({ count: 3 }); - }); - - test('it renders multiple options with only keys', async function (assert) { - this.options = [{ key: 'one' }, { key: 'three' }]; - - // Template block usage: - await render(hbs` - - `); - - assert.dom('tbody tr:first-child td').exists({ count: 2 }); - assert.dom('tbody tr').exists({ count: 3 }); - }); -}); diff --git a/ui/admin/tests/integration/components/form/field/list-wrapper-test.js b/ui/admin/tests/integration/components/form/field/list-wrapper-test.js new file mode 100644 index 000000000..d51c942e6 --- /dev/null +++ b/ui/admin/tests/integration/components/form/field/list-wrapper-test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { setupIntl } from 'ember-intl/test-support'; + +module('Integration | Component | list-wrapper', function (hooks) { + setupRenderingTest(hooks); + setupIntl(hooks); + + test('it renders fields', async function (assert) { + this.error = [ + { + message: 'Error!', + }, + ]; + await render(hbs` + + <:fieldset as |F|> + + Label + + + Help + + + Error! + + + <:field as |F|> + + + + + `); + + assert.dom('tbody tr').exists({ count: 1 }); + assert.dom('legend').exists().hasText('Label'); + assert.dom('.hds-form-helper-text').exists().hasText('Help'); + + assert.dom('.hds-form-error__message').exists().hasText('Error!'); + assert.dom('button').exists().hasText('Add'); + }); + + test('it renders multiple options with key value pair', async function (assert) { + this.options = [ + { key: 'one', value: 'two' }, + { key: 'three', value: 'four' }, + ]; + + await render(hbs` + + <:field as |F|> + + + + + + `); + + assert.dom('tbody tr').exists({ count: 3 }); + }); + + test('it renders multiple options with a single text field', async function (assert) { + this.options = [{ value: 'one' }, { value: 'three' }]; + this.fn = () => {}; + + await render(hbs` + + + <:field as |F|> + + + + + `); + + assert + .dom('.list-wrapper-field [data-test-remove-button]') + .exists({ count: 2 }); + + assert.dom('tbody tr').exists({ count: 3 }); + }); +});