import { expect, fixture, html, unsafeStatic, triggerFocusFor, triggerBlurFor, nextFrame, } from '@open-wc/testing'; import sinon from 'sinon'; import { localizeTearDown } from '@lion/localize/test-helpers.js'; import '@lion/input/lion-input.js'; import '../lion-fieldset.js'; const tagString = 'lion-fieldset'; const tag = unsafeStatic(tagString); const inputSlotString = ` `; const nonPrefilledModelValue = ''; const prefilledModelValue = 'prefill'; beforeEach(() => { localizeTearDown(); }); describe('', () => { it(`${tagString} has an up to date list of every form element in #formElements`, async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); expect(Object.keys(fieldset.formElements).length).to.equal(3); expect(fieldset.formElements['hobbies[]'].length).to.equal(2); fieldset.removeChild(fieldset.formElements['hobbies[]'][0]); expect(Object.keys(fieldset.formElements).length).to.equal(3); expect(fieldset.formElements['hobbies[]'].length).to.equal(1); }); it(`supports in html wrapped form elements`, async () => { const el = await fixture(`
`); await nextFrame(); expect(el.formElementsArray.length).to.equal(1); el.children[0].removeChild(el.formElements.foo); expect(el.formElementsArray.length).to.equal(0); }); it('handles names with ending [] as an array', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['gender[]'][0].modelValue = { value: 'male' }; fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; expect(Object.keys(fieldset.formElements).length).to.equal(3); expect(fieldset.formElements['hobbies[]'].length).to.equal(2); expect(fieldset.formElements['hobbies[]'][0].modelValue.value).to.equal('chess'); expect(fieldset.formElements['gender[]'][0].modelValue.value).to.equal('male'); expect(fieldset.modelValue['hobbies[]']).to.deep.equal([ { checked: false, value: 'chess' }, { checked: false, value: 'rugby' }, ]); }); it('throws if an element without a name tries to register', async () => { const orig = console.info; console.info = () => {}; let error = false; const el = await fixture(``); try { // we need to use the private api here as errors thrown from a web component are in a // different context and we can not catch them here => register fake elements el.__onFormElementRegister({ detail: { element: {} }, }); } catch (err) { error = err; } expect(error).to.be.instanceOf(TypeError); expect(error.message).to.equal('You need to define a name'); console.info = orig; // restore original console }); it('throws if name is the same as its parent', async () => { const orig = console.info; console.info = () => {}; let error = false; const el = await fixture(``); try { // we need to use the private api here as errors thrown from a web component are in a // different context and we can not catch them here => register fake elements el.__onFormElementRegister({ detail: { element: { name: 'foo' } }, }); } catch (err) { error = err; } expect(error).to.be.instanceOf(TypeError); expect(error.message).to.equal('You can not have the same name "foo" as your parent'); console.info = orig; // restore original console }); it('throws if same name without ending [] is used', async () => { const orig = console.info; console.info = () => {}; let error = false; const el = await fixture(``); try { // we need to use the private api here as errors thrown from a web component are in a // different context and we can not catch them here => register fake elements el.__onFormElementRegister({ stopPropagation: () => {}, detail: { element: { name: 'fooBar', addToAriaDescription: () => {} } }, }); el.__onFormElementRegister({ stopPropagation: () => {}, detail: { element: { name: 'fooBar' } }, }); } catch (err) { error = err; } expect(error).to.be.instanceOf(TypeError); expect(error.message).to.equal( 'Name "fooBar" is already registered - if you want an array add [] to the end', ); console.info = orig; // restore original console }); /* eslint-enable no-console */ it('can dynamically add/remove elements', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); const newField = await fixture(``); await nextFrame(); expect(Object.keys(fieldset.formElements).length).to.equal(3); fieldset.appendChild(newField); await nextFrame(); expect(Object.keys(fieldset.formElements).length).to.equal(4); fieldset.inputElement.removeChild(newField); expect(Object.keys(fieldset.formElements).length).to.equal(3); }); it('can read/write all values (of every input) via this.modelValue', async () => { const fieldset = await fixture(` <${tagString} name="newfieldset">${inputSlotString} `); await nextFrame(); const newFieldset = fieldset.querySelector('lion-fieldset'); fieldset.formElements.lastName.modelValue = 'Bar'; newFieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'chess' }; newFieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'football' }; newFieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; newFieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; newFieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.modelValue).to.deep.equal({ lastName: 'Bar', newfieldset: { 'hobbies[]': [{ checked: true, value: 'chess' }, { checked: false, value: 'football' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }, }); fieldset.modelValue = { lastName: 2, newfieldset: { 'hobbies[]': [{ checked: true, value: 'chess' }, { checked: false, value: 'baseball' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }, }; expect(newFieldset.formElements['hobbies[]'][0].modelValue).to.deep.equal({ checked: true, value: 'chess', }); expect(newFieldset.formElements['hobbies[]'][1].modelValue).to.deep.equal({ checked: false, value: 'baseball', }); expect(fieldset.formElements.lastName.modelValue).to.equal(2); }); it('does not throw if setter data of this.modelValue can not be handled', async () => { const el = await fixture(html` `); await nextFrame(); const initState = { firstName: 'foo', lastName: 'bar', }; expect(el.modelValue).to.deep.equal(initState); el.modelValue = undefined; expect(el.modelValue).to.deep.equal(initState); el.modelValue = null; expect(el.modelValue).to.deep.equal(initState); }); it('disables/enables all its formElements if it becomes disabled/enabled', async () => { const el = await fixture(`<${tagString} disabled>${inputSlotString}`); await nextFrame(); expect(el.formElements.color.disabled).to.equal(true); expect(el.formElements['hobbies[]'][0].disabled).to.equal(true); expect(el.formElements['hobbies[]'][1].disabled).to.equal(true); el.disabled = false; await el.updateComplete; expect(el.formElements.color.disabled).to.equal(false); expect(el.formElements['hobbies[]'][0].disabled).to.equal(false); expect(el.formElements['hobbies[]'][1].disabled).to.equal(false); }); it('does not propagate/override inital disabled value on nested form elements', async () => { const el = await fixture( `<${tagString}><${tagString} name="sub" disabled>${inputSlotString}`, ); await nextFrame(); expect(el.disabled).to.equal(false); expect(el.formElements.sub.disabled).to.equal(true); expect(el.formElements.sub.formElements.color.disabled).to.equal(true); expect(el.formElements.sub.formElements['hobbies[]'][0].disabled).to.equal(true); expect(el.formElements.sub.formElements['hobbies[]'][1].disabled).to.equal(true); }); describe('validation', () => { it('validates on init', async () => { function isCat(value) { return { isCat: value === 'cat' }; } const el = await fixture(html` <${tag}> `); await nextFrame(); expect(el.formElements.color.error.isCat).to.equal(true); }); it('validates when a value changes', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); const spy = sinon.spy(fieldset, 'validate'); fieldset.formElements.color.modelValue = { checked: true, value: 'red' }; expect(spy.callCount).to.equal(1); }); it('has a special {error, warning, info, success} validator for all children - can be checked via this.error.formElementsHaveNoError', async () => { function isCat(value) { return { isCat: value === 'cat' }; } const el = await fixture(html` <${tag}> `); await nextFrame(); expect(el.error.formElementsHaveNoError).to.equal(true); expect(el.formElements.color.error.isCat).to.equal(true); el.formElements.color.modelValue = 'cat'; expect(el.error).to.deep.equal({}); }); }); describe('interaction states', () => { it('has false states (dirty, touched, prefilled) on init', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); expect(fieldset.dirty).to.equal(false, 'dirty'); expect(fieldset.touched).to.equal(false, 'touched'); expect(fieldset.prefilled).to.equal(false, 'prefilled'); }); it('sets dirty when value changed', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'football' }; expect(fieldset.dirty).to.equal(true); }); it('sets touched when field left after focus', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); await triggerFocusFor(fieldset.formElements['gender[]'][0].inputElement); await triggerBlurFor(fieldset.formElements['gender[]'][0].inputElement); expect(fieldset.touched).to.equal(true); }); it('sets a class "state-(touched|dirty)"', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements.color.touched = true; await fieldset.updateComplete; expect(fieldset.classList.contains('state-touched')).to.equal( true, 'has class "state-touched"', ); fieldset.formElements.color.dirty = true; await fieldset.updateComplete; expect(fieldset.classList.contains('state-dirty')).to.equal(true, 'has class "state-dirty"'); }); it('sets prefilled when field left and value non-empty', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'football' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = nonPrefilledModelValue; await triggerFocusFor(fieldset.formElements.color.inputElement); fieldset.formElements.color.modelValue = prefilledModelValue; await triggerBlurFor(fieldset.formElements.color.inputElement); expect(fieldset.prefilled).to.equal(true, 'sets prefilled when left non empty'); await triggerFocusFor(fieldset.formElements.color.inputElement); fieldset.formElements.color.modelValue = nonPrefilledModelValue; await triggerBlurFor(fieldset.formElements.color.inputElement); expect(fieldset.prefilled).to.equal(false, 'unsets prefilled when left empty'); }); it('sets prefilled once instantiated', async () => { // no prefilled when nothing has value const fieldsetNotPrefilled = await fixture(html`<${tag}>${inputSlotString}`); expect(fieldsetNotPrefilled.prefilled).to.equal(false, 'not prefilled on init'); // prefilled when at least one child has value const fieldsetPrefilled = await fixture(html` <${tag}> `); await nextFrame(); expect(fieldsetPrefilled.prefilled).to.equal(true, 'prefilled on init'); }); }); describe('serialize', () => { it('use form elements serializedValue', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['hobbies[]'][0].serializer = v => `${v.value}-serialized`; fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'Bar' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.formElements['hobbies[]'][0].serializedValue).to.equal('Bar-serialized'); expect(fieldset.serializeGroup()).to.deep.equal({ 'hobbies[]': ['Bar-serialized', { checked: false, value: 'rugby' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }); }); it('form elements which are not disabled', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; fieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'football' }; fieldset.formElements['gender[]'][0].modelValue = { checked: true, value: 'male' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.serializeGroup()).to.deep.equal({ 'hobbies[]': [{ checked: true, value: 'football' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: true, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }); fieldset.formElements.color.disabled = true; expect(fieldset.serializeGroup()).to.deep.equal({ 'hobbies[]': [{ checked: true, value: 'football' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: true, value: 'male' }, { checked: false, value: 'female' }], }); }); it('allows for nested fieldsets', async () => { const fieldset = await fixture(` <${tagString} name="newfieldset">${inputSlotString} `); await nextFrame(); const newFieldset = fieldset.querySelector('lion-fieldset'); newFieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; newFieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; newFieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; newFieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; newFieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; fieldset.formElements.comment.modelValue = 'Foo'; expect(Object.keys(fieldset.formElements).length).to.equal(2); expect(Object.keys(newFieldset.formElements).length).to.equal(3); expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }, }); }); it('will exclude form elements within an disabled fieldset', async () => { const fieldset = await fixture(` <${tagString} name="newfieldset">${inputSlotString} `); await nextFrame(); const newFieldset = fieldset.querySelector('lion-fieldset'); fieldset.formElements.comment.modelValue = 'Foo'; newFieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; newFieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; newFieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; newFieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; newFieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; newFieldset.formElements.color.disabled = true; expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], }, }); newFieldset.formElements.color.disabled = false; expect(fieldset.serializeGroup()).to.deep.equal({ comment: 'Foo', newfieldset: { 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }, }); }); it('treats names with ending [] as arrays', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.serializeGroup()).to.deep.equal({ 'hobbies[]': [{ checked: false, value: 'chess' }, { checked: false, value: 'rugby' }], 'gender[]': [{ checked: false, value: 'male' }, { checked: false, value: 'female' }], color: { checked: false, value: 'blue' }, }); }); it('does not serialize undefined values (nb radios/checkboxes are always serialized)', async () => { const fieldset = await fixture(` `); await nextFrame(); fieldset.formElements['custom[]'][0].modelValue = 'custom 1'; fieldset.formElements['custom[]'][1].modelValue = undefined; expect(fieldset.serializeGroup()).to.deep.equal({ 'custom[]': ['custom 1'], }); }); }); describe('reset', () => { it('restores default values if changes were made', async () => { const el = await fixture(html` `); await el.querySelector('lion-input').updateComplete; const input = el.querySelector('#firstName'); input.modelValue = 'Bar'; expect(el.modelValue).to.deep.equal({ firstName: 'Bar' }); expect(input.modelValue).to.equal('Bar'); el.resetGroup(); expect(el.modelValue).to.deep.equal({ firstName: 'Foo' }); expect(input.modelValue).to.equal('Foo'); }); it('restores default values of arrays if changes were made', async () => { const el = await fixture(html` `); await el.querySelector('lion-input').updateComplete; const input = el.querySelector('#firstName'); input.modelValue = 'Bar'; expect(el.modelValue).to.deep.equal({ 'firstName[]': ['Bar'] }); expect(input.modelValue).to.equal('Bar'); el.resetGroup(); expect(el.modelValue).to.deep.equal({ 'firstName[]': ['Foo'] }); expect(input.modelValue).to.equal('Foo'); }); it('restores default values of a nested fieldset if changes were made', async () => { const el = await fixture(html` `); await Promise.all([ el.querySelector('lion-fieldset').updateComplete, el.querySelector('lion-input').updateComplete, ]); const input = el.querySelector('#firstName'); const nestedFieldset = el.querySelector('#name'); input.modelValue = 'Bar'; expect(el.modelValue).to.deep.equal({ 'name[]': [{ firstName: 'Bar' }] }); expect(nestedFieldset.modelValue).to.deep.equal({ firstName: 'Bar' }); expect(input.modelValue).to.equal('Bar'); el.resetGroup(); expect(el.modelValue).to.deep.equal({ 'name[]': [{ firstName: 'Foo' }] }); expect(nestedFieldset.modelValue).to.deep.equal({ firstName: 'Foo' }); expect(input.modelValue).to.equal('Foo'); }); it('clears interaction state', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); // Safety check initially fieldset._setValueForAllFormElements('dirty', true); fieldset._setValueForAllFormElements('touched', true); fieldset._setValueForAllFormElements('prefilled', true); expect(fieldset.dirty).to.equal(true, '"dirty" initially'); expect(fieldset.touched).to.equal(true, '"touched" initially'); expect(fieldset.prefilled).to.equal(true, '"prefilled" initially'); // Reset all children states, with prefilled false fieldset._setValueForAllFormElements('modelValue', {}); fieldset.resetInteractionState(); expect(fieldset.dirty).to.equal(false, 'not "dirty" after reset'); expect(fieldset.touched).to.equal(false, 'not "touched" after reset'); expect(fieldset.prefilled).to.equal(false, 'not "prefilled" after reset'); // Reset all children states with prefilled true fieldset._setValueForAllFormElements('dirty', true); fieldset._setValueForAllFormElements('touched', true); fieldset._setValueForAllFormElements('modelValue', { checked: true }); // not prefilled fieldset.resetInteractionState(); expect(fieldset.dirty).to.equal(false, 'not "dirty" after 2nd reset'); expect(fieldset.touched).to.equal(false, 'not "touched" after 2nd reset'); // prefilled state is dependant on value expect(fieldset.prefilled).to.equal(true, '"prefilled" after 2nd reset'); }); it('clears submitted state', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.submitted = true; fieldset.resetGroup(); expect(fieldset.submitted).to.equal(false); fieldset.formElementsArray.forEach(el => { expect(el.submitted).to.equal(false); }); }); it('has correct validation afterwards', async () => { const isCat = modelValue => ({ isCat: modelValue.value === 'cat' }); const containsA = modelValues => ({ containsA: modelValues.color.value ? modelValues.color.value.indexOf('a') > -1 : false, }); const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements.color.modelValue = { value: 'onlyb' }; fieldset.errorValidators = [[containsA]]; fieldset.formElements.color.errorValidators = [[isCat]]; expect(fieldset.errorState).to.equal(true); expect(fieldset.error.containsA).to.equal(true); expect(fieldset.formElements.color.error.isCat).to.equal(true); fieldset.formElements.color.modelValue = { value: 'cat' }; expect(fieldset.errorState).to.equal(false); fieldset.resetGroup(); fieldset.formElements.color.modelValue = { value: 'Foo' }; fieldset.errorValidators = [[containsA]]; fieldset.formElements.color.errorValidators = [[isCat]]; expect(fieldset.errorState).to.equal(true); expect(fieldset.error.containsA).to.equal(true); expect(fieldset.formElements.color.error.isCat).to.equal(true); }); }); describe('a11y', () => { // beforeEach(() => { // localizeTearDown(); // }); it('has role="group" set', async () => { const fieldset = await fixture(`<${tagString}>${inputSlotString}`); await nextFrame(); fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; fieldset.formElements['gender[]'][1].modelValue = { checked: false, value: 'female' }; fieldset.formElements.color.modelValue = { checked: false, value: 'blue' }; expect(fieldset.hasAttribute('role')).to.equal(true); expect(fieldset.getAttribute('role')).to.contain('group'); }); it('has an aria-labelledby from element with slot="label"', async () => { const el = await fixture(` <${tagString}> ${inputSlotString} `); const label = el.querySelector('[slot="label"]'); expect(el.hasAttribute('aria-labelledby')).to.equal(true); expect(el.getAttribute('aria-labelledby')).contains(label.id); }); describe('Screen reader relations (aria-describedby) for child fields and fieldsets', () => { let childAriaFixture; // function let childAriaTest; // function before(() => { // Legend: // - l1 means level 1 (outer) fieldset // - l2 means level 2 (inner) fieldset // - g means group: the help-text or feedback belongs to group // - f means field(lion-input in fixture below): the help-text or feedback belongs to field // - 'a' or 'b' behind 'f' indicate which field in a fieldset is meant (a: first, b: second) childAriaFixture = async ( msgSlotType = 'feedback', // eslint-disable-line no-shadow ) => { const dom = fixture(`
`); await nextFrame(); return dom; }; // eslint-disable-next-line no-shadow childAriaTest = childAriaFixture => { /* eslint-disable camelcase */ // Message elements: all elements pointed at by inputs const msg_l1_g = childAriaFixture.querySelector('#msg_l1_g'); const msg_l1_fa = childAriaFixture.querySelector('#msg_l1_fa'); const msg_l1_fb = childAriaFixture.querySelector('#msg_l1_fb'); const msg_l2_g = childAriaFixture.querySelector('#msg_l2_g'); const msg_l2_fa = childAriaFixture.querySelector('#msg_l2_fa'); const msg_l2_fb = childAriaFixture.querySelector('#msg_l2_fb'); // Field elements: all inputs pointing to message elements const input_l1_fa = childAriaFixture.querySelector('[name=l1_fa]'); const input_l1_fb = childAriaFixture.querySelector('[name=l1_fb]'); const input_l2_fa = childAriaFixture.querySelector('[name=l2_fa]'); const input_l2_fb = childAriaFixture.querySelector('[name=l2_fb]'); /* eslint-enable camelcase */ const ariaDescribedBy = el => el.getAttribute('aria-describedby'); // 'L1' fields (inside lion-fieldset[name="l1_g"]) should point to l1(group) msg expect(ariaDescribedBy(input_l1_fa)).to.contain( msg_l1_g.id, 'l1 input(a) refers parent/group', ); expect(ariaDescribedBy(input_l1_fb)).to.contain( msg_l1_g.id, 'l1 input(b) refers parent/group', ); // Also check that aria-describedby of the inputs are not overridden (this relation was // put there in lion-input(using lion-field)). expect(ariaDescribedBy(input_l1_fa)).to.contain( msg_l1_fa.id, 'l1 input(a) refers local field', ); expect(ariaDescribedBy(input_l1_fb)).to.contain( msg_l1_fb.id, 'l1 input(b) refers local field', ); // Also make feedback element point to nested fieldset inputs expect(ariaDescribedBy(input_l2_fa)).to.contain( msg_l1_g.id, 'l2 input(a) refers grandparent/group.group', ); expect(ariaDescribedBy(input_l2_fb)).to.contain( msg_l1_g.id, 'l2 input(b) refers grandparent/group.group', ); // Check order: the nearest ('dom wise': so 1. local, 2. parent, 3. grandparent) message // should be read first by screen reader let d = ariaDescribedBy(input_l2_fa); expect( d.indexOf(msg_l1_g.id) < d.indexOf(msg_l2_g.id) < d.indexOf(msg_l2_fa.id), ).to.equal(true, 'order of ids'); d = ariaDescribedBy(input_l2_fb); expect( d.indexOf(msg_l1_g.id) < d.indexOf(msg_l2_g.id) < d.indexOf(msg_l2_fb.id), ).to.equal(true, 'order of ids'); }; }); it(`reads feedback message belonging to fieldset when child input is focused (via aria-describedby)`, async () => { childAriaTest(await childAriaFixture('feedback')); }); it(`reads help-text message belonging to fieldset when child input is focused (via aria-describedby)`, async () => { childAriaTest(await childAriaFixture('help-text')); }); }); }); });