diff --git a/ui/core/app/components/form/project/index.hbs b/ui/core/app/components/form/project/index.hbs index 7541f8ed5..9db695baa 100644 --- a/ui/core/app/components/form/project/index.hbs +++ b/ui/core/app/components/form/project/index.hbs @@ -2,6 +2,7 @@ @onSubmit={{@submit}} @cancel={{@cancel}} @disabled={{@model.isSaving}} + @error={{true}} as |form| > @@ -10,17 +11,35 @@ @type="name" @value={{@model.name}} @label={{t "form.label.name"}} - @error={{false}} - readonly={{false}} /> + @error={{@model.errors.name}} + readonly={{false}} + as |field|> + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + readonly={{false}} + as |field|> + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + { + // return new Response(400, {}, { + // "status": 400, + // "code": "invalid_argument", + // "message": "The request contained invalid fields.", + // "details": { + // "request_id": "abc123", + // "TraceId": "0123-4567", + // "fields": [ + // { + // "name": "name", + // "message": "name is required" + // }, + // { + // "name": "description", + // "message": "The description must not be more than 1 trillion characters long." + // } + // ] + // } + // }); + + return new Response(409, {}, { + "status": 409, + "code": "invalid_argument", + "message": "The request was awful." + }); + }); this.del('/orgs/:org_id/projects/:id'); } diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js new file mode 100644 index 000000000..a98601b19 --- /dev/null +++ b/ui/core/tests/acceptance/projects-test.js @@ -0,0 +1,138 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, click, fillIn, find } 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'; + +module('Acceptance | projects', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + test('visiting projects', async function (assert) { + assert.expect(1); + await visit('/orgs/1/projects'); + await a11yAudit(); + assert.equal(currentURL(), '/orgs/1/projects'); + }); + + test('saving a new project with invalid fields displays error messages', async function (assert) { + assert.expect(2); + this.server.post('/orgs/:org_id/projects', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: { + fields: [ + { + name: 'name', + message: 'Name is required.', + }, + ], + }, + } + ); + }); + await visit('/orgs/1/projects/new'); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'The request was invalid.', + 'Displays primary error message.' + ); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.', + 'Displays field-level errors.' + ); + }); + + test('saving an existing project with invalid fields displays error messages', async function (assert) { + assert.expect(2); + this.server.createList('project', 1); + this.server.patch('/orgs/:org_id/projects/:id', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: { + fields: [ + { + name: 'name', + message: 'Name is required.', + }, + ], + }, + } + ); + }); + await visit('/orgs/1/projects/1'); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'The request was invalid.', + 'Displays primary error message.' + ); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.', + 'Displays field-level errors.' + ); + }); + + test('errors are displayed when save project fails', async function (assert) { + assert.expect(1); + this.server.createList('project', 1); + this.server.patch('/orgs/:org_id/projects/:id', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/projects/1'); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); + + test('errors are displayed when delete project fails', async function (assert) { + assert.expect(1); + this.server.createList('project', 1); + this.server.del('/orgs/:org_id/projects/:id', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/projects/1'); + await click('.rose-button-warning'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); +});