import { expect, fixture, html, elementUpdated } from '@open-wc/testing';
import './nile-checkbox';
import NileCheckbox from './nile-checkbox';
describe('NileCheckbox', () => {
// === RENDERING ===
it('1. should render without errors', async () => {
const el = await fixture(html``);
expect(el).to.exist;
});
it('2. should have a shadow root', async () => {
const el = await fixture(html``);
expect(el.shadowRoot).to.not.be.null;
});
it('3. should render an input of type checkbox', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input[type="checkbox"]');
expect(input).to.exist;
});
it('4. should render a label element', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label).to.exist;
});
it('5. should render default slot for label content', async () => {
const el = await fixture(html``);
const slot = el.shadowRoot!.querySelector('slot:not([name])');
expect(slot).to.exist;
});
it('6. should render slot content', async () => {
const el = await fixture(html`Check me`);
expect(el.textContent!.trim()).to.equal('Check me');
});
// === DEFAULT PROPERTIES ===
it('7. should have checked default to false', async () => {
const el = await fixture(html``);
expect(el.checked).to.be.false;
});
it('8. should have disabled default to false', async () => {
const el = await fixture(html``);
expect(el.disabled).to.be.false;
});
it('9. should have indeterminate default to false', async () => {
const el = await fixture(html``);
expect(el.indeterminate).to.be.false;
});
it('10. should have size default to medium', async () => {
const el = await fixture(html``);
expect(el.size).to.equal('medium');
});
it('11. should have name default to empty string', async () => {
const el = await fixture(html``);
expect(el.name).to.equal('');
});
it('12. should have label default to empty string', async () => {
const el = await fixture(html``);
expect(el.label).to.equal('');
});
it('13. should have required default to false', async () => {
const el = await fixture(html``);
expect(el.required).to.be.false;
});
it('14. should have helpText default to empty string', async () => {
const el = await fixture(html``);
expect(el.helpText).to.equal('');
});
it('15. should have errorMessage default to empty string', async () => {
const el = await fixture(html``);
expect(el.errorMessage).to.equal('');
});
it('16. should have form default to empty string', async () => {
const el = await fixture(html``);
expect(el.form).to.equal('');
});
it('17. should have title default to empty string', async () => {
const el = await fixture(html``);
expect(el.title).to.equal('');
});
it('18. should have showHelpText default to false', async () => {
const el = await fixture(html``);
expect(el.showHelpText).to.be.false;
});
// === CHECKED STATE ===
it('19. should be checked when checked attribute is set', async () => {
const el = await fixture(html``);
expect(el.checked).to.be.true;
});
it('20. should apply checked class when checked', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--checked')).to.be.true;
});
it('21. should reflect checked attribute', async () => {
const el = await fixture(html``);
expect(el.hasAttribute('checked')).to.be.true;
});
it('22. should set aria-checked to true when checked', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.getAttribute('aria-checked')).to.equal('true');
});
it('23. should set aria-checked to false when unchecked', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.getAttribute('aria-checked')).to.equal('false');
});
it('24. should show checked icon when checked', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__checked-icon');
expect(icon).to.exist;
});
it('25. should not show checked icon when unchecked', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__checked-icon');
expect(icon).to.be.null;
});
// === DISABLED STATE ===
it('26. should be disabled when disabled attribute is set', async () => {
const el = await fixture(html``);
expect(el.disabled).to.be.true;
});
it('27. should apply disabled class', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--disabled')).to.be.true;
});
it('28. should disable the input when disabled', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.disabled).to.be.true;
});
it('29. should reflect disabled attribute', async () => {
const el = await fixture(html``);
expect(el.hasAttribute('disabled')).to.be.true;
});
// === INDETERMINATE STATE ===
it('30. should be indeterminate when attribute is set', async () => {
const el = await fixture(html``);
expect(el.indeterminate).to.be.true;
});
it('31. should apply indeterminate class', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--indeterminate')).to.be.true;
});
it('32. should show indeterminate icon when indeterminate', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__indeterminate-icon');
expect(icon).to.exist;
});
it('33. should not show indeterminate icon when checked', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__indeterminate-icon');
expect(icon).to.be.null;
});
it('34. should reflect indeterminate attribute', async () => {
const el = await fixture(html``);
expect(el.hasAttribute('indeterminate')).to.be.true;
});
// === SIZE ===
it('35. should support small size', async () => {
const el = await fixture(html``);
expect(el.size).to.equal('small');
});
it('36. should support medium size', async () => {
const el = await fixture(html``);
expect(el.size).to.equal('medium');
});
it('37. should support large size', async () => {
const el = await fixture(html``);
expect(el.size).to.equal('large');
});
it('38. should apply medium class by default', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--medium')).to.be.true;
});
it('39. should reflect size attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('size')).to.equal('large');
});
// === EVENTS ===
it('40. should emit nile-change when clicked', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let changeEmitted = false;
el.addEventListener('nile-change', () => (changeEmitted = true));
input!.click();
expect(changeEmitted).to.be.true;
});
it('41. should emit nile-change with checked detail', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let detail: any = null;
el.addEventListener('nile-change', (e: any) => (detail = e.detail));
input!.click();
expect(detail).to.exist;
expect(detail.checked).to.be.true;
});
it('42. should emit valueChange when clicked', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let changeEmitted = false;
el.addEventListener('valueChange', () => (changeEmitted = true));
input!.click();
expect(changeEmitted).to.be.true;
});
it('43. should emit blur event on blur', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let blurEmitted = false;
el.addEventListener('blur', () => (blurEmitted = true));
input!.dispatchEvent(new FocusEvent('blur'));
expect(blurEmitted).to.be.true;
});
it('44. should emit focus event on focus', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let focusEmitted = false;
el.addEventListener('focus', () => (focusEmitted = true));
input!.dispatchEvent(new FocusEvent('focus'));
expect(focusEmitted).to.be.true;
});
it('45. should emit input event on input', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let inputEmitted = false;
el.addEventListener('input', () => (inputEmitted = true));
input!.dispatchEvent(new Event('input'));
expect(inputEmitted).to.be.true;
});
it('46. should emit nile-init on connected', async () => {
let initEmitted = false;
const container = await fixture(html``);
const el = document.createElement('nile-checkbox') as NileCheckbox;
el.addEventListener('nile-init', () => (initEmitted = true));
container.appendChild(el);
await el.updateComplete;
expect(initEmitted).to.be.true;
});
it('47. should emit nile-destroy on disconnected', async () => {
const el = await fixture(html``);
let destroyEmitted = false;
el.addEventListener('nile-destroy', () => (destroyEmitted = true));
el.remove();
expect(destroyEmitted).to.be.true;
});
// === CLICK BEHAVIOR ===
it('48. should toggle checked on click', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.click();
expect(el.checked).to.be.true;
});
it('49. should toggle from checked to unchecked on click', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.click();
expect(el.checked).to.be.false;
});
it('50. should clear indeterminate on click', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.click();
expect(el.indeterminate).to.be.false;
});
// === FOCUS/BLUR METHODS ===
it('51. should focus programmatically', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--focused')).to.be.true;
});
it('52. should blur programmatically', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
el.blur();
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--focused')).to.be.false;
});
it('53. should click programmatically', async () => {
const el = await fixture(html``);
el.click();
await el.updateComplete;
expect(el.checked).to.be.true;
});
// === LABEL PROPERTY ===
it('54. should display label text', async () => {
const el = await fixture(html``);
const labelDiv = el.shadowRoot!.querySelector('.checkbox__label');
expect(labelDiv!.textContent).to.contain('My Label');
});
it('55. should reflect label attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('label')).to.equal('Test');
});
// === NAME ===
it('56. should set name on input', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.getAttribute('name')).to.equal('myCheckbox');
});
// === REQUIRED ===
it('57. should set required on input', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.required).to.be.true;
});
it('58. should reflect required attribute', async () => {
const el = await fixture(html``);
expect(el.hasAttribute('required')).to.be.true;
});
// === HELP TEXT ===
it('59. should render help text when set', async () => {
const el = await fixture(html``);
const helpText = el.shadowRoot!.querySelector('nile-form-help-text');
expect(helpText).to.exist;
});
it('60. should not render help text when empty', async () => {
const el = await fixture(html``);
const helpText = el.shadowRoot!.querySelector('nile-form-help-text');
expect(helpText).to.be.null;
});
it('61. should reflect help-text attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('help-text')).to.equal('Help');
});
it('62. should add full-width class when helpText is set', async () => {
const el = await fixture(html``);
expect(el.classList.contains('full-width')).to.be.true;
});
it('63. should remove full-width class when helpText is empty', async () => {
const el = await fixture(html``);
expect(el.classList.contains('full-width')).to.be.false;
});
// === ERROR MESSAGE ===
it('64. should render error message when set', async () => {
const el = await fixture(html``);
const errorMsg = el.shadowRoot!.querySelector('nile-form-error-message');
expect(errorMsg).to.exist;
});
it('65. should not render error message when empty', async () => {
const el = await fixture(html``);
const errorMsg = el.shadowRoot!.querySelector('nile-form-error-message');
expect(errorMsg).to.be.null;
});
it('66. should reflect error-message attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('error-message')).to.equal('Err');
});
// === DYNAMIC PROPERTY CHANGES ===
it('67. should update checked dynamically', async () => {
const el = await fixture(html``);
el.checked = true;
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--checked')).to.be.true;
});
it('68. should update disabled dynamically', async () => {
const el = await fixture(html``);
el.disabled = true;
await el.updateComplete;
const input = el.shadowRoot!.querySelector('input');
expect(input!.disabled).to.be.true;
});
it('69. should update indeterminate dynamically', async () => {
const el = await fixture(html``);
el.indeterminate = true;
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--indeterminate')).to.be.true;
});
it('70. should update size dynamically', async () => {
const el = await fixture(html``);
el.size = 'large';
await el.updateComplete;
expect(el.getAttribute('size')).to.equal('large');
});
it('71. should update label dynamically', async () => {
const el = await fixture(html``);
el.label = 'New Label';
await el.updateComplete;
const labelDiv = el.shadowRoot!.querySelector('.checkbox__label');
expect(labelDiv!.textContent).to.contain('New Label');
});
it('72. should update name dynamically', async () => {
const el = await fixture(html``);
el.name = 'newName';
await el.updateComplete;
const input = el.shadowRoot!.querySelector('input');
expect(input!.getAttribute('name')).to.equal('newName');
});
it('73. should update helpText dynamically', async () => {
const el = await fixture(html``);
el.helpText = 'New Help';
await el.updateComplete;
const helpText = el.shadowRoot!.querySelector('nile-form-help-text');
expect(helpText).to.exist;
});
it('74. should update errorMessage dynamically', async () => {
const el = await fixture(html``);
el.errorMessage = 'New Error';
await el.updateComplete;
const errorMsg = el.shadowRoot!.querySelector('nile-form-error-message');
expect(errorMsg).to.exist;
});
// === CSS PARTS ===
it('75. should have base part', async () => {
const el = await fixture(html``);
const base = el.shadowRoot!.querySelector('[part~="base"]');
expect(base).to.exist;
});
it('76. should have control part', async () => {
const el = await fixture(html``);
const control = el.shadowRoot!.querySelector('[part~="control"]');
expect(control).to.exist;
});
it('77. should have control--checked part when checked', async () => {
const el = await fixture(html``);
const control = el.shadowRoot!.querySelector('[part~="control--checked"]');
expect(control).to.exist;
});
it('78. should have control--indeterminate part when indeterminate', async () => {
const el = await fixture(html``);
const control = el.shadowRoot!.querySelector('[part~="control--indeterminate"]');
expect(control).to.exist;
});
it('79. should have label part', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('[part~="label"]');
expect(label).to.exist;
});
// === COMBINED STATES ===
it('80. should handle checked and disabled together', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--checked')).to.be.true;
expect(label!.classList.contains('checkbox--disabled')).to.be.true;
});
it('81. should show checked icon with different color when checked and disabled', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__checked-icon');
expect(icon).to.exist;
});
it('82. should show indeterminate icon when indeterminate and disabled', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.checkbox__indeterminate-icon');
expect(icon).to.exist;
});
it('83. should handle checked, disabled, and indeterminate together', async () => {
const el = await fixture(html``);
expect(el.checked).to.be.true;
expect(el.disabled).to.be.true;
expect(el.indeterminate).to.be.true;
});
it('84. should handle help text and error message together', async () => {
const el = await fixture(html``);
const helpText = el.shadowRoot!.querySelector('nile-form-help-text');
const errorMsg = el.shadowRoot!.querySelector('nile-form-error-message');
expect(helpText).to.exist;
expect(errorMsg).to.exist;
});
// === TITLE ===
it('85. should set title on internal input', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
expect(input!.getAttribute('title')).to.equal('My Title');
});
// === FORM ===
it('86. should reflect form attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('form')).to.equal('myForm');
});
// === FOCUSED STATE ===
it('87. should apply focused class on focus', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.dispatchEvent(new FocusEvent('focus'));
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--focused')).to.be.true;
});
it('88. should remove focused class on blur', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.dispatchEvent(new FocusEvent('focus'));
await el.updateComplete;
input!.dispatchEvent(new FocusEvent('blur'));
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--focused')).to.be.false;
});
// === MULTIPLE CLICKS ===
it('89. should toggle multiple times', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
input!.click();
expect(el.checked).to.be.true;
input!.click();
expect(el.checked).to.be.false;
input!.click();
expect(el.checked).to.be.true;
});
it('90. should emit change event each toggle', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
let count = 0;
el.addEventListener('nile-change', () => count++);
input!.click();
input!.click();
input!.click();
expect(count).to.equal(3);
});
// === CONTROL CLASS ===
it('91. should have checkbox class on label', async () => {
const el = await fixture(html``);
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox')).to.be.true;
});
it('92. should have checkbox__input class on input', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('.checkbox__input');
expect(input).to.exist;
});
it('93. should have checkbox__control class on span', async () => {
const el = await fixture(html``);
const control = el.shadowRoot!.querySelector('.checkbox__control');
expect(control).to.exist;
});
it('94. should have checkbox__label class', async () => {
const el = await fixture(html``);
const labelDiv = el.shadowRoot!.querySelector('.checkbox__label');
expect(labelDiv).to.exist;
});
// === VALUE ===
it('95. should set value on input', async () => {
const el = await fixture(html``);
expect(el.value).to.be.true;
});
// === EDGE CASES ===
it('96. should handle empty label (boolean false label)', async () => {
const el = await fixture(html``);
const labelDiv = el.shadowRoot!.querySelector('.checkbox__label');
expect(labelDiv).to.exist;
});
it('97. should update full-width class when helpText changes from set to empty', async () => {
const el = await fixture(html``);
expect(el.classList.contains('full-width')).to.be.true;
el.helpText = '';
await el.updateComplete;
expect(el.classList.contains('full-width')).to.be.false;
});
it('98. should support setting checked via property after render', async () => {
const el = await fixture(html``);
expect(el.checked).to.be.false;
el.checked = true;
await el.updateComplete;
expect(el.checked).to.be.true;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--checked')).to.be.true;
});
it('99. should support setting indeterminate via property after render', async () => {
const el = await fixture(html``);
el.indeterminate = true;
await el.updateComplete;
const label = el.shadowRoot!.querySelector('label');
expect(label!.classList.contains('checkbox--indeterminate')).to.be.true;
});
it('100. should handle rapid toggling', async () => {
const el = await fixture(html``);
const input = el.shadowRoot!.querySelector('input');
for (let i = 0; i < 10; i++) {
input!.click();
}
expect(el.checked).to.be.false; // even number of clicks
});
});