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 }); });