[feedback]
~~~`, async () => {
const el = await fixture(html`<${tag}>
${inputSlot}
Enter your Name
No name entered
${tag}>
`);
const nativeInput = Array.from(el.children).find(child => child.slot === 'input');
expect(nativeInput.getAttribute('aria-labelledby')).to.equal(`label-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`help-text-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`feedback-${el._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 el = await fixture(html`<${tag}>
${inputSlot}
[before]
[after]
[prefix]
[suffix]
${tag}>
`);
const nativeInput = Array.from(el.children).find(child => child.slot === 'input');
expect(nativeInput.getAttribute('aria-labelledby')).to.contain(
`before-${el._inputId} after-${el._inputId}`,
);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
`prefix-${el._inputId} suffix-${el._inputId}`,
);
});
// TODO: Move test below to FormControlMixin.test.js.
it(`allows to add to aria description or label via addToAriaLabelledBy() and
addToAriaDescribedBy()`, async () => {
const wrapper = await fixture(html`
<${tag}>
${inputSlot}
Added to description by default
${tag}>
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;
await el.updateComplete;
const { _inputNode } = el;
// 1. addToAriaLabel()
// Check if the aria attr is filled initially
expect(_inputNode.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);
el.addToAriaLabelledBy(wrapper.querySelector('#additionalLabel'));
// Now check if ids are added to the end (not overridden)
expect(_inputNode.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);
// Should be placed in the end
expect(
_inputNode.getAttribute('aria-labelledby').indexOf(`label-${el._inputId}`) <
_inputNode.getAttribute('aria-labelledby').indexOf('additionalLabel'),
);
// 2. addToAriaDescription()
// Check if the aria attr is filled initially
expect(_inputNode.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
el.addToAriaDescribedBy(wrapper.querySelector('#additionalDescription'));
// Now check if ids are added to the end (not overridden)
expect(_inputNode.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
// Should be placed in the end
expect(
_inputNode.getAttribute('aria-describedby').indexOf(`feedback-${el._inputId}`) <
_inputNode.getAttribute('aria-describedby').indexOf('additionalDescription'),
);
});
});
describe(`Validation`, () => {
beforeEach(() => {
// Reset and preload validation translations
localizeTearDown();
localize.addData('en-GB', 'lion-validate', {
error: {
hasX: 'This is error message for hasX',
},
});
});
it('should conditionally show error', async () => {
const HasX = class extends Validator {
constructor() {
super();
this.name = 'HasX';
}
execute(value) {
const result = value.indexOf('x') === -1;
return result;
}
};
const el = await fixture(html`
<${tag}
.validators=${[new HasX()]}
.modelValue=${'a@b.nl'}
>
${inputSlot}
${tag}>
`);
const executeScenario = async (_sceneEl, scenario) => {
const sceneEl = _sceneEl;
sceneEl.resetInteractionState();
sceneEl.touched = scenario.el.touched;
sceneEl.dirty = scenario.el.dirty;
sceneEl.prefilled = scenario.el.prefilled;
sceneEl.submitted = scenario.el.submitted;
await sceneEl.updateComplete;
await sceneEl.feedbackComplete;
expect(sceneEl.showsFeedbackFor).to.deep.equal(scenario.wantedShowsFeedbackFor);
};
await executeScenario(el, {
index: 0,
el: { touched: true, dirty: true, prefilled: false, submitted: false },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 1,
el: { touched: false, dirty: false, prefilled: true, submitted: false },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 2,
el: { touched: false, dirty: false, prefilled: false, submitted: true },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 3,
el: { touched: false, dirty: true, prefilled: false, submitted: false },
wantedShowsFeedbackFor: [],
});
await executeScenario(el, {
index: 4,
el: { touched: true, dirty: false, prefilled: false, submitted: false },
wantedShowsFeedbackFor: [],
});
});
it('can be required', async () => {
const el = await fixture(html`
<${tag}
.validators=${[new Required()]}
>${inputSlot}${tag}>
`);
expect(el.hasFeedbackFor).to.deep.equal(['error']);
expect(el.validationStates.error).to.have.a.property('Required');
el.modelValue = 'cat';
expect(el.hasFeedbackFor).to.deep.equal([]);
expect(el.validationStates.error).not.to.have.a.property('Required');
});
it('will only update formattedValue when valid on `user-input-changed`', async () => {
const formatterSpy = sinon.spy(value => `foo: ${value}`);
const Bar = class extends Validator {
constructor(...args) {
super(...args);
this.name = 'Bar';
}
execute(value) {
const hasError = value !== 'bar';
return hasError;
}
};
const el = await fixture(html`
<${tag}
.modelValue=${'init-string'}
.formatter=${formatterSpy}
.validators=${[new Bar()]}
>${inputSlot}${tag}>
`);
expect(formatterSpy.callCount).to.equal(0);
expect(el.formattedValue).to.equal('init-string');
el.modelValue = 'bar';
expect(formatterSpy.callCount).to.equal(1);
expect(el.formattedValue).to.equal('foo: bar');
mimicUserInput(el, 'foo');
expect(formatterSpy.callCount).to.equal(1);
expect(el.value).to.equal('foo');
});
});
describe(`Content projection`, () => {
it('renders correctly all slot elements in light DOM', async () => {
const el = await fixture(html`
<${tag}>
${inputSlot}
[help-text]
[before]
[after]
[prefix]
[suffix]
[feedback]
${tag}>
`);
const names = [
'label',
'input',
'help-text',
'before',
'after',
'prefix',
'suffix',
'feedback',
];
names.forEach(slotName => {
el.querySelector(`[slot="${slotName}"]`).setAttribute('test-me', 'ok');
const slot = el.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', () => {
it('delegates property value', async () => {
const el = await fixture(html`<${tag}>${inputSlot}${tag}>`);
expect(el._inputNode.value).to.equal('');
el.value = 'one';
expect(el.value).to.equal('one');
expect(el._inputNode.value).to.equal('one');
});
it('delegates property selectionStart and selectionEnd', async () => {
const el = await fixture(html`
<${tag}
.modelValue=${'Some text to select'}
>${unsafeHTML(inputSlotString)}${tag}>
`);
el.selectionStart = 5;
el.selectionEnd = 12;
expect(el._inputNode.selectionStart).to.equal(5);
expect(el._inputNode.selectionEnd).to.equal(12);
});
});
});