import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { click, fillIn, find, render, settled, waitUntil, } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import { WorkflowSession } from '@cardstack/web-client/models/workflow'; import { Response as MirageResponse } from 'ember-cli-mirage'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { MirageTestContext } from 'ember-cli-mirage/test-support'; import { OPTIONS } from '@cardstack/web-client/components/card-space/edit-details/category'; interface Context extends MirageTestContext {} module( 'Integration | Component | card-space/edit-details/category', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); hooks.beforeEach(async function (this: Context) { this.server.post('/card-spaces/validate-profile-category', function () { return new MirageResponse(200, {}, { errors: [] }); }); }); test('it lists the allowed categories and persists the choice to the workflow session', async function (assert) { let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); assert.dom('radio-option__input--checked').doesNotExist(); OPTIONS.forEach(function (buttonText, index) { assert .dom(`[data-test-category-option]:nth-child(${index + 1})`) .hasText(buttonText); }); await click(`[data-test-category-option]:nth-child(2)`); assert.equal( workflowSession.getValue('profileCategory'), OPTIONS[1] ); assert.dom('[data-test-category-option]:nth-child(2) input').isChecked(); }); test('it allows a custom category to be entered', async function (assert) { let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); assert .dom(`[data-test-category-option]:nth-child(${OPTIONS.length + 1})`) .containsText('Other'); await click( `[data-test-category-option]:nth-child(${OPTIONS.length + 1})` ); assert .dom( `[data-test-category-option]:nth-child(${ OPTIONS.length + 1 }) [data-test-category-option-other]` ) .exists(); await fillIn('[data-test-category-option-other] input', 'Something'); await waitUntil( () => (find('[data-test-validation-state-input]') as HTMLElement).dataset .testValidationStateInput !== 'initial' ); assert .dom( `[data-test-category-option]:nth-child(${OPTIONS.length + 1}) input` ) .hasClass('radio-option__input--checked'); assert.equal( workflowSession.getValue('profileCategory'), 'Something' ); await click('[data-test-category-option]:nth-child(2)'); assert.dom('[data-test-category-option-other] input').doesNotExist(); assert.notEqual( workflowSession.getValue('profileCategory'), 'Something' ); await click( `[data-test-category-option]:nth-child(${OPTIONS.length + 1})` ); assert .dom('[data-test-category-option-other] input') .hasValue('Something'); await waitUntil( () => (find('[data-test-validation-state-input]') as HTMLElement).dataset .testValidationStateInput !== 'loading' ); assert.equal( workflowSession.getValue('profileCategory'), 'Something' ); }); test('it focuses the other field when the option is clicked', async function (assert) { let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); await click('[data-test-category-option-other-container]'); assert.dom('[data-test-category-option-other] input').isFocused(); }); test('it ignores the validated other value if a preset value has been chosen in the meantime', async function (this: Context, assert) { let validationResolver: Function | undefined; let validationPromise = new Promise(function (resolve) { validationResolver = resolve; }); this.server.post( '/card-spaces/validate-profile-category', async function () { await validationPromise; return new MirageResponse(200, {}, { errors: [] }); } ); let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); await click('[data-test-category-option-other-container]'); await fillIn('[data-test-category-option-other] input', 'Something'); await click(`[data-test-category-option]:nth-child(2)`); assert.equal( workflowSession.getValue('profileCategory'), OPTIONS[1] ); assert.dom('[data-test-category-option]:nth-child(2) input').isChecked(); if (validationResolver) { validationResolver(true); } // Hideous, but need to wait to ensure validateCategoryTask has completed and not clobbered the static profileCategory await settled(); await validationPromise; await settled(); assert.equal( workflowSession.getValue('profileCategory'), OPTIONS[1] ); assert.dom('[data-test-category-option]:nth-child(2) input').isChecked(); }); test('it shows the error when custom category validation fails', async function (this: Context, assert) { this.server.post('/card-spaces/validate-profile-category', function () { return new MirageResponse(200, {}, { errors: [{ detail: 'Is bad' }] }); }); let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); await click('[data-test-category-option-other-container]'); await fillIn('[data-test-category-option-other] input', 'Something'); await waitUntil( () => (find('[data-test-validation-state-input]') as HTMLElement).dataset .testValidationStateInput !== 'initial' ); assert .dom('[data-test-validation-state-input]') .hasAttribute('data-test-validation-state-input', 'invalid'); assert .dom( `[data-test-card-space-category-field] [data-test-boxel-input-error-message]` ) .containsText('Is bad'); }); test('it shows an error when custom category validation errors', async function (this: Context, assert) { this.server.post('/card-spaces/validate-profile-category', function () { return new MirageResponse(500, {}, {}); }); let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); await render(hbs` `); await click('[data-test-category-option-other-container]'); await fillIn('[data-test-category-option-other] input', 'Something'); await waitUntil( () => (find('[data-test-validation-state-input]') as HTMLElement).dataset .testValidationStateInput !== 'initial' ); assert .dom('[data-test-validation-state-input]') .hasAttribute('data-test-validation-state-input', 'invalid'); assert .dom( `[data-test-card-space-category-field] [data-test-boxel-input-error-message]` ) .containsText( 'There was an error validating your Card Space profile category.' ); assert.notOk(workflowSession.getValue('profileCategory')); }); test('it restores input from session', async function (assert) { let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); workflowSession.setValue('profileCategory', OPTIONS[1]); await render(hbs` `); assert .dom('[data-test-category-option]:nth-child(2) input') .hasClass('radio-option__input--checked'); }); test('it restores custom input from session', async function (assert) { let workflowSession = new WorkflowSession(); this.set('workflowSession', workflowSession); workflowSession.setValue('profileCategory', 'Hello'); await render(hbs` `); assert .dom( `[data-test-category-option]:nth-child(${OPTIONS.length + 1}) input` ) .hasClass('radio-option__input--checked'); assert.dom('[data-test-category-option-other] input').hasValue('Hello'); }); } );