feat: integrate and pass automated a11y testing
This commit is contained in:
parent
4d029a5d4d
commit
e1a417b041
17 changed files with 408 additions and 2 deletions
|
|
@ -137,7 +137,7 @@ export class LionButton extends DisabledWithTabIndexMixin(SlotMixin(LitElement))
|
|||
if (!this.constructor._button) {
|
||||
this.constructor._button = document.createElement('button');
|
||||
this.constructor._button.setAttribute('tabindex', '-1');
|
||||
this.constructor._button.setAttribute('aria-hidden', true);
|
||||
this.constructor._button.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
return this.constructor._button.cloneNode();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -228,6 +228,16 @@ describe('lion-button', () => {
|
|||
|
||||
expect(el._nativeButtonNode.getAttribute('aria-hidden')).to.equal('true');
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(`<lion-button>foo</lion-button>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(`<lion-button disabled>foo</lion-button>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form integration', () => {
|
||||
|
|
|
|||
|
|
@ -1193,4 +1193,37 @@ describe('<lion-calendar>', () => {
|
|||
*/
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(
|
||||
html`
|
||||
<lion-calendar></lion-calendar>
|
||||
`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible with a date selected', async () => {
|
||||
const today = new Date();
|
||||
const selectedDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
||||
const el = await fixture(
|
||||
html`
|
||||
<lion-calendar .selectedDate="${selectedDate}"></lion-calendar>
|
||||
`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible with disabled dates', async () => {
|
||||
const el = await fixture(
|
||||
html`
|
||||
<lion-calendar
|
||||
.disableDates=${day => day.getDay() === 6 || day.getDay() === 0}
|
||||
></lion-calendar>
|
||||
`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,4 +25,78 @@ describe('<lion-checkbox-group>', () => {
|
|||
el.formElements['sports[]'][0].checked = true;
|
||||
expect(el.hasFeedbackFor).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-checkbox-group name="scientistsGroup" label="Who are your favorite scientists?">
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Archimedes"
|
||||
.choiceValue=${'Archimedes'}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Francis Bacon"
|
||||
.choiceValue=${'Francis Bacon'}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Marie Curie"
|
||||
.modelValue=${{ value: 'Marie Curie', checked: false }}
|
||||
></lion-checkbox>
|
||||
</lion-checkbox-group>
|
||||
`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when pre-selected', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-checkbox-group name="scientistsGroup" label="Who are your favorite scientists?">
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Archimedes"
|
||||
.choiceValue=${'Archimedes'}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Francis Bacon"
|
||||
.choiceValue=${'Francis Bacon'}
|
||||
.choiceChecked=${true}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Marie Curie"
|
||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||
></lion-checkbox>
|
||||
</lion-checkbox-group>
|
||||
`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-checkbox-group
|
||||
name="scientistsGroup"
|
||||
label="Who are your favorite scientists?"
|
||||
disabled
|
||||
>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Archimedes"
|
||||
.choiceValue=${'Archimedes'}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Francis Bacon"
|
||||
.choiceValue=${'Francis Bacon'}
|
||||
></lion-checkbox>
|
||||
<lion-checkbox
|
||||
name="scientists[]"
|
||||
label="Marie Curie"
|
||||
.modelValue=${{ value: 'Marie Curie', checked: true }}
|
||||
></lion-checkbox>
|
||||
</lion-checkbox-group>
|
||||
`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -71,6 +71,24 @@ describe('lion-icon', () => {
|
|||
expect(el.hasAttribute('aria-label')).to.equal(false);
|
||||
});
|
||||
|
||||
it('is accessible with an aria label', async () => {
|
||||
const el = await fixture(
|
||||
html`
|
||||
<lion-icon .svg=${heartSvg} aria-label="Love"></lion-icon>
|
||||
`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible without an aria label', async () => {
|
||||
const el = await fixture(
|
||||
html`
|
||||
<lion-icon .svg=${heartSvg}></lion-icon>
|
||||
`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('expects svg-icons to have the attribute `focusable="false"` so the icon doesn\'t appear in tab-order in IE/Edge', async () => {
|
||||
const icon = await fixture(
|
||||
html`
|
||||
|
|
|
|||
|
|
@ -118,4 +118,25 @@ describe('<lion-input-amount>', () => {
|
|||
expect(el._currencyDisplayNode.getAttribute('aria-label')).to.equal('Philippine pisos');
|
||||
});
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-amount><label slot="label">Label</label></lion-input-amount>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when readonly', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-amount readonly .modelValue=${'123'}><label slot="label">Label</label></lion-input-amount>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-amount disabled><label slot="label">Label</label></lion-input-amount>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -96,4 +96,27 @@ describe('<lion-input-date>', () => {
|
|||
await el.updateComplete;
|
||||
expect(el.formattedValue).to.equal('15/06/2017'); // should stay british
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-date><label slot="label">Label</label></lion-input-date>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when readonly', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-date readonly .modelValue=${new Date(
|
||||
'2017/06/15',
|
||||
)}><label slot="label">Label</label></lion-input-date>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-date disabled><label slot="label">Label</label></lion-input-date>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -301,6 +301,50 @@ describe('<lion-input-datepicker>', () => {
|
|||
await elObj.closeCalendar();
|
||||
expect(elObj.invokerEl.getAttribute('aria-expanded')).to.equal('false');
|
||||
});
|
||||
|
||||
it('is accessible when closed', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-input-datepicker></lion-input-datepicker>
|
||||
`);
|
||||
const elObj = new DatepickerInputObject(el);
|
||||
|
||||
await expect(elObj.invokerEl).to.be.accessible();
|
||||
await expect(elObj.calendarEl).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when open', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-input-datepicker></lion-input-datepicker>
|
||||
`);
|
||||
const elObj = new DatepickerInputObject(el);
|
||||
await elObj.openCalendar();
|
||||
|
||||
await expect(elObj.calendarEl).to.be.accessible();
|
||||
elObj.overlayCloseButtonEl.click();
|
||||
});
|
||||
|
||||
it('has accessible invoker when open', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-input-datepicker></lion-input-datepicker>
|
||||
`);
|
||||
const elObj = new DatepickerInputObject(el);
|
||||
await elObj.openCalendar();
|
||||
|
||||
await expect(elObj.invokerEl).to.be.accessible();
|
||||
elObj.overlayCloseButtonEl.click();
|
||||
});
|
||||
|
||||
it('is accessible with a disabled date', async () => {
|
||||
const no15th = d => d.getDate() !== 15;
|
||||
const el = await fixture(html`
|
||||
<lion-input-datepicker .validators=${[new IsDateDisabled(no15th)]}> </lion-input-datepicker>
|
||||
`);
|
||||
const elObj = new DatepickerInputObject(el);
|
||||
await elObj.openCalendar();
|
||||
|
||||
await expect(elObj.calendarEl).to.be.accessible();
|
||||
elObj.overlayCloseButtonEl.click();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('Subclassers', () => {
|
||||
|
|
|
|||
|
|
@ -15,4 +15,25 @@ describe('<lion-input-email>', () => {
|
|||
expect(el.hasFeedbackFor).to.deep.equal(['error']);
|
||||
expect(el.validationStates.error.IsEmail).to.be.true;
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const lionInputEmail = await fixture(
|
||||
`<lion-input-email><label slot="label">Label</label></lion-input-email>`,
|
||||
);
|
||||
await expect(lionInputEmail).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when readonly', async () => {
|
||||
const lionInputEmail = await fixture(
|
||||
`<lion-input-email readonly><label slot="label">Label</label></lion-input-email>`,
|
||||
);
|
||||
await expect(lionInputEmail).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const lionInputEmail = await fixture(
|
||||
`<lion-input-email disabled><label slot="label">Label</label></lion-input-email>`,
|
||||
);
|
||||
await expect(lionInputEmail).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,4 +53,25 @@ describe('<lion-input-iban>', () => {
|
|||
expect(el.validationStates.error).to.have.a.property('IsIBAN');
|
||||
expect(el.validationStates.error).to.have.a.property('IsCountryIBAN');
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-iban><label slot="label">Label</label></lion-input-iban>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when readonly', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-iban readonly><label slot="label">Label</label></lion-input-iban>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input-iban disabled><label slot="label">Label</label></lion-input-iban>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,4 +41,21 @@ describe('<lion-input>', () => {
|
|||
expect(el.getAttribute('placeholder')).to.equal('foo');
|
||||
expect(el._inputNode.getAttribute('placeholder')).to.equal('foo');
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(`<lion-input><label slot="label">Label</label></lion-input>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when readonly', async () => {
|
||||
const el = await fixture(
|
||||
`<lion-input readonly .modelValue=${'read only'}><label slot="label">Label</label></lion-input>`,
|
||||
);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(`<lion-input disabled><label slot="label">Label</label></lion-input>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ export class OverlayController {
|
|||
if (this.invokerNode) {
|
||||
this.invokerNode.setAttribute('aria-expanded', this.isShown);
|
||||
}
|
||||
if (!this.contentNode.hasAttribute('role')) {
|
||||
if (!this.contentNode.role) {
|
||||
this.contentNode.setAttribute('role', 'dialog');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,4 +265,52 @@ describe('<lion-radio-group>', () => {
|
|||
el.children[1].checked = true;
|
||||
expect(el.touched, `focused via a mouse click, group should be touched`).to.be.true;
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-radio-group>
|
||||
<label slot="label">My group</label>
|
||||
<lion-radio name="gender[]" value="male">
|
||||
<label slot="label">male</label>
|
||||
</lion-radio>
|
||||
<lion-radio name="gender[]" value="female" checked>
|
||||
<label slot="label">female</label>
|
||||
</lion-radio>
|
||||
</lion-radio-group>
|
||||
`);
|
||||
await nextFrame();
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when the group is disabled', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-radio-group disabled>
|
||||
<label slot="label">My group</label>
|
||||
<lion-radio name="gender[]" value="male">
|
||||
<label slot="label">male</label>
|
||||
</lion-radio>
|
||||
<lion-radio name="gender[]" value="female">
|
||||
<label slot="label">female</label>
|
||||
</lion-radio>
|
||||
</lion-radio-group>
|
||||
`);
|
||||
await nextFrame();
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when an option is disabled', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-radio-group>
|
||||
<label slot="label">My group</label>
|
||||
<lion-radio name="gender[]" value="male" disabled>
|
||||
<label slot="label">male</label>
|
||||
</lion-radio>
|
||||
<lion-radio name="gender[]" value="female">
|
||||
<label slot="label">female</label>
|
||||
</lion-radio>
|
||||
</lion-radio-group>
|
||||
`);
|
||||
await nextFrame();
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -400,6 +400,34 @@ describe('lion-select-rich', () => {
|
|||
|
||||
expect(el._invokerNode.getAttribute('aria-expanded')).to.equal('true');
|
||||
});
|
||||
|
||||
it('is accessible when closed', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-select-rich label="age">
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||
</lion-options>
|
||||
</lion-select-rich>
|
||||
`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when opened', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-select-rich label="age">
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||
</lion-options>
|
||||
</lion-select-rich>
|
||||
`);
|
||||
el.opened = true;
|
||||
await el.updateComplete;
|
||||
await el.updateComplete; // need 2 awaits as overlay.show is an async function
|
||||
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Use cases', () => {
|
||||
|
|
|
|||
|
|
@ -15,4 +15,17 @@ describe('lion-select', () => {
|
|||
`);
|
||||
expect(lionSelect.querySelector('select').value).to.equal('nr2');
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const lionSelect = await fixture(html`
|
||||
<lion-select .modelValue="${'nr2'}">
|
||||
<label slot="label">Label</label>
|
||||
<select slot="input">
|
||||
<option value="nr1">Item 1</option>
|
||||
<option value="nr2">Item 2</option>
|
||||
</select>
|
||||
</lion-select>
|
||||
`);
|
||||
await expect(lionSelect).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -139,4 +139,14 @@ describe('<lion-textarea>', () => {
|
|||
.to.be.below(el.clientHeight)
|
||||
.and.to.be.below(el.scrollHeight);
|
||||
});
|
||||
|
||||
it('is accessible', async () => {
|
||||
const el = await fixture(`<lion-textarea label="Label"></lion-textarea>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it('is accessible when disabled', async () => {
|
||||
const el = await fixture(`<lion-textarea label="Label" disabled></lion-textarea>`);
|
||||
await expect(el).to.be.accessible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -117,5 +117,30 @@ describe('lion-tooltip', () => {
|
|||
const content = el.querySelector('[slot=content]');
|
||||
expect(content.getAttribute('role')).to.be.equal('tooltip');
|
||||
});
|
||||
|
||||
it('should be accessible when closed', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-tooltip>
|
||||
<div slot="content">Hey there</div>
|
||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
||||
</lion-tooltip>
|
||||
`);
|
||||
await expect(el).to.be.accessible;
|
||||
});
|
||||
|
||||
it('should be accessible when opened', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-tooltip>
|
||||
<div slot="content">Hey there</div>
|
||||
<lion-button slot="invoker">Tooltip button</lion-button>
|
||||
</lion-tooltip>
|
||||
`);
|
||||
const invoker = el.querySelector('[slot="invoker"]');
|
||||
const eventFocusIn = new Event('focusin');
|
||||
invoker.dispatchEvent(eventFocusIn);
|
||||
await el.updateComplete;
|
||||
|
||||
await expect(el).to.be.accessible;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue