import { expect, fixture, html } from '@open-wc/testing';
import './nile-icon-button';
import NileIconButton from './nile-icon-button';
describe('NileIconButton', () => {
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 a button element by default', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button).to.exist;
});
it('4. should render an anchor when href is set', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor).to.exist;
});
it('5. should render a nile-icon inside', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon).to.exist;
});
it('6. should have disabled default to false', async () => {
const el = await fixture(html``);
expect(el.disabled).to.be.false;
});
it('7. should have label default to empty string', async () => {
const el = await fixture(html``);
expect(el.label).to.equal('');
});
it('8. should have ishovered default to false', async () => {
const el = await fixture(html``);
expect(el.ishovered).to.be.false;
});
it('9. should set name on nile-icon', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('name')).to.equal('close');
});
it('10. should set library on nile-icon', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('library')).to.equal('system');
});
it('11. should set src on nile-icon', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('src')).to.equal('/icon.svg');
});
it('12. should set aria-label from label property', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('aria-label')).to.equal('Close');
});
it('13. should have icon-button class on button', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('.icon-button');
expect(button).to.exist;
});
it('14. should apply disabled class when disabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('.icon-button--disabled');
expect(button).to.exist;
});
it('15. should have aria-disabled true when disabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('aria-disabled')).to.equal('true');
});
it('16. should have aria-disabled false when not disabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('aria-disabled')).to.equal('false');
});
it('17. should have tabindex 0 when enabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('tabindex')).to.equal('0');
});
it('18. should have tabindex -1 when disabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('tabindex')).to.equal('-1');
});
it('19. should have type=button on button element', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('type')).to.equal('button');
});
it('20. should have role=button on button element', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('role')).to.equal('button');
});
it('21. should emit nile-focus on focus', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
let focusEmitted = false;
el.addEventListener('nile-focus', () => (focusEmitted = true));
button!.dispatchEvent(new FocusEvent('focus'));
expect(focusEmitted).to.be.true;
});
it('22. should emit nile-blur on blur', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
let blurEmitted = false;
el.addEventListener('nile-blur', () => (blurEmitted = true));
button!.dispatchEvent(new FocusEvent('blur'));
expect(blurEmitted).to.be.true;
});
it('23. should apply focused class on focus', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
const button = el.shadowRoot!.querySelector('.icon-button--focused');
expect(button).to.exist;
});
it('24. should remove focused class on blur', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
el.blur();
await el.updateComplete;
const button = el.shadowRoot!.querySelector('.icon-button--focused');
expect(button).to.be.null;
});
it('25. should have base part on button', async () => {
const el = await fixture(html``);
const base = el.shadowRoot!.querySelector('[part="base"]');
expect(base).to.exist;
});
it('26. should set href on anchor', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('https://example.com');
});
it('27. should set target on anchor', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('target')).to.equal('_blank');
});
it('28. should set download on anchor', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('download')).to.equal('file.pdf');
});
it('29. should set rel on anchor with target', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('rel')).to.equal('noreferrer noopener');
});
it('30. should reflect disabled attribute', async () => {
const el = await fixture(html``);
expect(el.hasAttribute('disabled')).to.be.true;
});
it('31. should update disabled dynamically', async () => {
const el = await fixture(html``);
el.disabled = true;
await el.updateComplete;
const button = el.shadowRoot!.querySelector('.icon-button--disabled');
expect(button).to.exist;
});
it('32. should update name dynamically', async () => {
const el = await fixture(html``);
el.name = 'arrow-left';
await el.updateComplete;
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('name')).to.equal('arrow-left');
});
it('33. should update label dynamically', async () => {
const el = await fixture(html``);
el.label = 'Open';
await el.updateComplete;
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('aria-label')).to.equal('Open');
});
it('34. should update color dynamically', async () => {
const el = await fixture(html``);
el.color = 'blue';
await el.updateComplete;
expect(el.color).to.equal('blue');
});
it('35. should switch from button to anchor when href is set', async () => {
const el = await fixture(html``);
el.href = 'https://example.com';
await el.updateComplete;
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor).to.exist;
});
it('36. nile-icon should have aria-hidden true', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('aria-hidden')).to.equal('true');
});
it('37. nile-icon should have icon-button__icon class', async () => {
const el = await fixture(html``);
const icon = el.shadowRoot!.querySelector('.icon-button__icon');
expect(icon).to.exist;
});
it('38. should handle click programmatically', async () => {
const el = await fixture(html``);
let clicked = false;
el.addEventListener('click', () => (clicked = true));
el.click();
expect(clicked).to.be.true;
});
it('39. should prevent click when disabled', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button') as HTMLButtonElement;
let defaultPrevented = false;
button.addEventListener('click', (e: MouseEvent) => {
defaultPrevented = e.defaultPrevented;
});
button.click();
// The handleClick method prevents and stops propagation when disabled
});
it('40. should set ishovered on mouseover', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
button!.dispatchEvent(new MouseEvent('mouseover'));
await el.updateComplete;
expect(el.ishovered).to.be.true;
});
it('41. should clear ishovered on mouseleave', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
button!.dispatchEvent(new MouseEvent('mouseover'));
await el.updateComplete;
button!.dispatchEvent(new MouseEvent('mouseleave'));
await el.updateComplete;
expect(el.ishovered).to.be.false;
});
it('42. should reflect ishovered attribute', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
button!.dispatchEvent(new MouseEvent('mouseover'));
await el.updateComplete;
expect(el.hasAttribute('ishovered')).to.be.true;
});
it('43. should reflect color attribute', async () => {
const el = await fixture(html``);
expect(el.getAttribute('color')).to.equal('red');
});
it('44. should be instance of NileIconButton', async () => {
const el = await fixture(html``);
expect(el).to.be.instanceOf(NileIconButton);
});
it('45. should have correct tag name', async () => {
const el = await fixture(html``);
expect(el.tagName.toLowerCase()).to.equal('nile-icon-button');
});
it('46. should have static styles', async () => {
expect(NileIconButton.styles).to.exist;
});
it('47. should be a defined custom element', async () => {
expect(customElements.get('nile-icon-button')).to.exist;
});
it('48. should handle undefined name', async () => {
const el = await fixture(html``);
expect(el.name).to.be.undefined;
});
it('49. should handle undefined library', async () => {
const el = await fixture(html``);
expect(el.library).to.be.undefined;
});
it('50. should handle undefined src', async () => {
const el = await fixture(html``);
expect(el.src).to.be.undefined;
});
it('51. should handle undefined href', async () => {
const el = await fixture(html``);
expect(el.href).to.be.undefined;
});
it('52. should handle undefined target', async () => {
const el = await fixture(html``);
expect(el.target).to.be.undefined;
});
it('53. should handle undefined download', async () => {
const el = await fixture(html``);
expect(el.download).to.be.undefined;
});
it('54. should render button element as base', async () => {
const el = await fixture(html``);
const base = el.shadowRoot!.querySelector('[part="base"]');
expect(base!.tagName).to.equal('BUTTON');
});
it('55. should render anchor element as base when href set', async () => {
const el = await fixture(html``);
const base = el.shadowRoot!.querySelector('[part="base"]');
expect(base!.tagName).to.equal('A');
});
it('56. should focus programmatically', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
const focused = el.shadowRoot!.querySelector('.icon-button--focused');
expect(focused).to.exist;
});
it('57. should blur programmatically', async () => {
const el = await fixture(html``);
el.focus();
await el.updateComplete;
el.blur();
await el.updateComplete;
const focused = el.shadowRoot!.querySelector('.icon-button--focused');
expect(focused).to.be.null;
});
it('58. should not have disabled class when not disabled', async () => {
const el = await fixture(html``);
const disabled = el.shadowRoot!.querySelector('.icon-button--disabled');
expect(disabled).to.be.null;
});
it('59. should handle multiple icon buttons independently', async () => {
const container = await fixture(html`
`);
const buttons = container.querySelectorAll('nile-icon-button');
expect(buttons.length).to.equal(2);
expect((buttons[0] as NileIconButton).label).to.equal('Close');
expect((buttons[1] as NileIconButton).label).to.equal('Edit');
});
it('60. should not have any slots', async () => {
const el = await fixture(html``);
const slots = el.shadowRoot!.querySelectorAll('slot');
expect(slots.length).to.equal(0);
});
it('61. icon should be inside button', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
const icon = button!.querySelector('nile-icon');
expect(icon).to.exist;
});
it('62. icon should be inside anchor when href set', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
const icon = anchor!.querySelector('nile-icon');
expect(icon).to.exist;
});
it('63. should handle name property update', async () => {
const el = await fixture(html``);
el.name = 'search';
await el.updateComplete;
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('name')).to.equal('search');
});
it('64. should handle library property update', async () => {
const el = await fixture(html``);
el.library = 'custom';
await el.updateComplete;
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('library')).to.equal('custom');
});
it('65. should handle toggling disabled', async () => {
const el = await fixture(html``);
el.disabled = true;
await el.updateComplete;
expect(el.shadowRoot!.querySelector('.icon-button--disabled')).to.exist;
el.disabled = false;
await el.updateComplete;
expect(el.shadowRoot!.querySelector('.icon-button--disabled')).to.be.null;
});
it('66. should handle color property', async () => {
const el = await fixture(html``);
expect(el.color).to.equal('red');
});
it('67. should handle undefined color', async () => {
const el = await fixture(html``);
expect(el.color).to.be.undefined;
});
it('68. should handle setting all properties', async () => {
const el = await fixture(html`
`);
expect(el.name).to.equal('close');
expect(el.library).to.equal('system');
expect(el.label).to.equal('Close');
expect(el.color).to.equal('red');
expect(el.disabled).to.be.true;
});
it('69. should handle creation via createElement', async () => {
const el = document.createElement('nile-icon-button') as NileIconButton;
document.body.appendChild(el);
await el.updateComplete;
expect(el.shadowRoot!.querySelector('.icon-button')).to.exist;
document.body.removeChild(el);
});
it('70. shadow root mode should be open', async () => {
const el = await fixture(html``);
expect(el.shadowRoot!.mode).to.equal('open');
});
it('71. should have render method', async () => {
const el = await fixture(html``);
expect(el.render).to.be.a('function');
});
it('72. should handle data attributes', async () => {
const el = await fixture(html``);
expect(el.getAttribute('data-action')).to.equal('close');
});
it('73. should handle class attribute', async () => {
const el = await fixture(html``);
expect(el.classList.contains('custom')).to.be.true;
});
it('74. should handle id attribute', async () => {
const el = await fixture(html``);
expect(el.id).to.equal('btn1');
});
it('75. button should have exactly one child (nile-icon)', async () => {
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
const icons = button!.querySelectorAll('nile-icon');
expect(icons.length).to.equal(1);
});
it('76. anchor should have exactly one child (nile-icon)', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
const icons = anchor!.querySelectorAll('nile-icon');
expect(icons.length).to.equal(1);
});
// Various href configurations
it('77. should handle absolute href', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('https://example.com/path');
});
it('78. should handle relative href', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('/path');
});
it('79. should handle hash href', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('#section');
});
// Label combinations
it('80. should handle long label', async () => {
const longLabel = 'A'.repeat(200);
const el = await fixture(html``);
const button = el.shadowRoot!.querySelector('button');
expect(button!.getAttribute('aria-label')).to.equal(longLabel);
});
it('81. should handle special characters in label', async () => {
const el = await fixture(html``);
expect(el.label).to.contain('&');
});
// Disabled + href
it('82. should handle disabled with href', async () => {
const el = await fixture(html``);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('aria-disabled')).to.equal('true');
expect(anchor!.getAttribute('tabindex')).to.equal('-1');
});
// Multiple renders
it('83. should handle multiple rapid property changes', async () => {
const el = await fixture(html``);
el.name = 'a';
el.name = 'b';
el.name = 'c';
await el.updateComplete;
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('name')).to.equal('c');
});
it('84. should maintain structure after requestUpdate', async () => {
const el = await fixture(html``);
el.requestUpdate();
await el.updateComplete;
expect(el.shadowRoot!.querySelector('.icon-button')).to.exist;
});
it('85. should handle being inside a form', async () => {
const container = await fixture(html``);
const btn = container.querySelector('nile-icon-button') as NileIconButton;
expect(btn.shadowRoot!.querySelector('.icon-button')).to.exist;
});
it('86. should handle being inside a nile-button container', async () => {
const container = await fixture(html`
`);
const btn = container.querySelector('nile-icon-button') as NileIconButton;
expect(btn.name).to.equal('x');
});
it('87. should handle hidden attribute', async () => {
const el = await fixture(html``);
expect(el.hidden).to.be.true;
});
it('88. should handle style attribute', async () => {
const el = await fixture(html``);
expect(el.style.opacity).to.equal('0.5');
});
it('89. should have no text content', async () => {
const el = await fixture(html``);
expect(el.textContent!.trim()).to.equal('');
});
it('90. should have updateComplete promise', async () => {
const el = await fixture(html``);
expect(el.updateComplete).to.be.a('promise');
});
it('91. should resolve updateComplete', async () => {
const el = await fixture(html``);
const result = await el.updateComplete;
expect(result).to.not.be.undefined;
});
it('92. should be connected after fixture', async () => {
const el = await fixture(html``);
expect(el.isConnected).to.be.true;
});
it('93. should be disconnected after removal', async () => {
const el = await fixture(html``);
el.remove();
expect(el.isConnected).to.be.false;
});
it('94. button should have nodeType ELEMENT_NODE', async () => {
const el = await fixture(html``);
expect(el.nodeType).to.equal(Node.ELEMENT_NODE);
});
it('95. shadowRoot host should be the element', async () => {
const el = await fixture(html``);
expect(el.shadowRoot!.host).to.equal(el);
});
// Comprehensive combos
it('96. name + library + label + disabled', async () => {
const el = await fixture(html`
`);
expect(el.disabled).to.be.true;
const icon = el.shadowRoot!.querySelector('nile-icon');
expect(icon!.getAttribute('name')).to.equal('close');
expect(icon!.getAttribute('library')).to.equal('system');
});
it('97. name + href + target + label', async () => {
const el = await fixture(html`
`);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('https://example.com');
expect(anchor!.getAttribute('target')).to.equal('_blank');
});
it('98. should handle name + color + label', async () => {
const el = await fixture(html`
`);
expect(el.name).to.equal('edit');
expect(el.color).to.equal('blue');
expect(el.label).to.equal('Edit');
});
it('99. should handle all link properties', async () => {
const el = await fixture(html`
`);
const anchor = el.shadowRoot!.querySelector('a');
expect(anchor!.getAttribute('href')).to.equal('https://example.com/file');
expect(anchor!.getAttribute('target')).to.equal('_blank');
expect(anchor!.getAttribute('download')).to.equal('file.pdf');
});
it('100. should handle switching from anchor to button', async () => {
const el = await fixture(html``);
expect(el.shadowRoot!.querySelector('a')).to.exist;
el.href = undefined;
await el.updateComplete;
expect(el.shadowRoot!.querySelector('button')).to.exist;
});
});