[feedback]
~~~`, async () => {
const lionField = await fixture(`<${tagString}>
${inputSlotString}
Enter your Name
No name entered
${tagString}>
`);
const nativeInput = lionField.$$slot('input');
expect(nativeInput.getAttribute('aria-labelledby')).to.equal(` label-${lionField._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
` help-text-${lionField._inputId}`,
);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
` feedback-${lionField._inputId}`,
);
});
it(`allows additional slots (prefix, suffix, before, after) to be included in labelledby
(via attribute data-label) and in describedby (via attribute data-description)`, async () => {
const lionField = await fixture(`<${tagString}>
${inputSlotString}
[before]
[after]
[prefix]
[suffix]
${tagString}>
`);
const nativeInput = lionField.$$slot('input');
expect(nativeInput.getAttribute('aria-labelledby')).to.contain(
` before-${lionField._inputId} after-${lionField._inputId}`,
);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
` prefix-${lionField._inputId} suffix-${lionField._inputId}`,
);
});
// TODO: put this test on FormControlMixin test once there
it(`allows to add to aria description or label via addToAriaLabel() and
addToAriaDescription()`, async () => {
const wrapper = await fixture(`
<${tagString}>
${inputSlotString}
Added to description by default
${tagString}>
This also needs to be read whenever the input has focus
Same for this
`);
const el = wrapper.querySelector(`${tagString}`);
// wait until the field element is done rendering
await el.updateComplete;
const { inputElement } = el;
const get = by => inputElement.getAttribute(`aria-${by}`);
// 1. addToAriaLabel()
// Check if the aria attr is filled initially
expect(get('labelledby')).to.contain(`label-${el._inputId}`);
el.addToAriaLabel('additionalLabel');
// Now check if ids are added to the end (not overridden)
expect(get('labelledby')).to.contain(`label-${el._inputId}`);
// Should be placed in the end
expect(
get('labelledby').indexOf(`label-${el._inputId}`) <
get('labelledby').indexOf('additionalLabel'),
);
// 2. addToAriaDescription()
// Check if the aria attr is filled initially
expect(get('describedby')).to.contain(`feedback-${el._inputId}`);
el.addToAriaDescription('additionalDescription');
// Now check if ids are added to the end (not overridden)
expect(get('describedby')).to.contain(`feedback-${el._inputId}`);
// Should be placed in the end
expect(
get('describedby').indexOf(`feedback-${el._inputId}`) <
get('describedby').indexOf('additionalDescription'),
);
});
});
describe(`Validation${nameSuffix}`, () => {
beforeEach(() => {
// Reset and preload validation translations
localizeTearDown();
localize.addData('en-GB', 'lion-validate', {
error: {
hasX: 'This is error message for hasX',
},
});
});
it('shows validity states(error|warning|info|success) when interaction criteria met ', async () => {
// TODO: in order to make this test work as an integration test, we chose a modelValue
// that is compatible with lion-input-email.
// However, when we can put priorities to validators (making sure error message of hasX is
// shown instead of a predefined validator like isEmail), we should fix this.
function hasX(str) {
return { hasX: str.indexOf('x') > -1 };
}
const lionField = await fixture(`<${tagString}>${inputSlotString}${tagString}>`);
const feedbackEl = lionField._feedbackElement;
lionField.modelValue = 'a@b.nl';
lionField.errorValidators = [[hasX]];
expect(lionField.error.hasX).to.equal(true);
expect(feedbackEl.innerText.trim()).to.equal(
'',
'shows no feedback, although the element has an error',
);
lionField.dirty = true;
lionField.touched = true;
lionField.modelValue = 'ab@c.nl'; // retrigger validation
await lionField.updateComplete;
expect(feedbackEl.innerText.trim()).to.equal(
'This is error message for hasX',
'shows feedback, because touched=true and dirty=true',
);
lionField.touched = false;
lionField.dirty = false;
lionField.prefilled = true;
await lionField.updateComplete;
expect(feedbackEl.innerText.trim()).to.equal(
'This is error message for hasX',
'shows feedback, because prefilled=true',
);
});
it('can be required', async () => {
const lionField = await fixture(html`
<${tag}
.errorValidators=${[['required']]}
>${inputSlot}${tag}>
`);
expect(lionField.error.required).to.be.true;
lionField.modelValue = 'cat';
expect(lionField.error.required).to.be.undefined;
});
});
describe(`Content projection${nameSuffix}`, () => {
it('renders correctly all slot elements in light DOM', async () => {
const lionField = await fixture(`
<${tagString}>
${inputSlotString}
[help-text]
[before]
[after]
[prefix]
[suffix]
[feedback]
${tagString}>
`);
const names = [
'label',
'input',
'help-text',
'before',
'after',
'prefix',
'suffix',
'feedback',
];
names.forEach(slotName => {
lionField.querySelector(`[slot="${slotName}"]`).setAttribute('test-me', 'ok');
const slot = lionField.shadowRoot.querySelector(`slot[name="${slotName}"]`);
const assignedNodes = slot.assignedNodes();
expect(assignedNodes.length).to.equal(1);
expect(assignedNodes[0].getAttribute('test-me')).to.equal('ok');
});
});
});
describe(`Delegation${nameSuffix}`, () => {
it('delegates attribute autofocus', async () => {
const el = await fixture(`<${tagString} autofocus>${inputSlotString}${tagString}>`);
expect(el.hasAttribute('autofocus')).to.be.false;
expect(el.inputElement.hasAttribute('autofocus')).to.be.true;
});
it('delegates property value', async () => {
const el = await fixture(`<${tagString}>${inputSlotString}${tagString}>`);
expect(el.inputElement.value).to.equal('');
el.value = 'one';
expect(el.value).to.equal('one');
expect(el.inputElement.value).to.equal('one');
});
it('delegates property type', async () => {
const el = await fixture(`<${tagString} type="text">${inputSlotString}${tagString}>`);
const inputElemTag = el.inputElement.tagName.toLowerCase();
if (inputElemTag === 'select') {
// TODO: later on we might want to support multi select ?
expect(el.inputElement.type).to.contain('select-one');
} else if (inputElemTag === 'textarea') {
expect(el.inputElement.type).to.contain('textarea');
} else {
// input or custom inputElement
expect(el.inputElement.type).to.contain('text');
el.type = 'password';
expect(el.type).to.equal('password');
expect(el.inputElement.type).to.equal('password');
}
});
it('delegates property onfocus', async () => {
const el = await fixture(`<${tagString}>${inputSlotString}${tagString}>`);
const cbFocusHost = sinon.spy();
el.onfocus = cbFocusHost;
await triggerFocusFor(el.inputElement);
expect(cbFocusHost.callCount).to.equal(1);
});
it('delegates property onblur', async () => {
const el = await fixture(`<${tagString}>${inputSlotString}${tagString}>`);
const cbBlurHost = sinon.spy();
el.onblur = cbBlurHost;
await triggerFocusFor(el.inputElement);
await triggerBlurFor(el.inputElement);
expect(cbBlurHost.callCount).to.equal(1);
});
it('delegates property selectionStart and selectionEnd', async () => {
const lionField = await fixture(html`
<${tag}
.modelValue=${'Some text to select'}
>${unsafeHTML(inputSlotString)}${tag}>
`);
lionField.selectionStart = 5;
lionField.selectionEnd = 12;
expect(lionField.inputElement.selectionStart).to.equal(5);
expect(lionField.inputElement.selectionEnd).to.equal(12);
});
});
});