import { expect, fixture, html } from '@open-wc/testing'; import './nile-toast'; import NileToast from './nile-toast'; describe('NileToast', () => { 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 base div', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base).to.exist; }); it('4. should have open default to false', async () => { const el = await fixture(html``); expect(el.open).to.be.false; }); it('5. should have closable default to false', async () => { const el = await fixture(html``); expect(el.closable).to.be.false; }); it('6. should have variant default to success', async () => { const el = await fixture(html``); expect(el.variant).to.equal('success'); }); it('7. should have duration default to Infinity', async () => { const el = await fixture(html``); expect(el.duration).to.equal(Infinity); }); it('8. should have title default to empty string', async () => { const el = await fixture(html``); expect(el.title).to.equal(''); }); it('9. should have content default to empty string', async () => { const el = await fixture(html``); expect(el.content).to.equal(''); }); it('10. should have noIcon default to false', async () => { const el = await fixture(html``); expect(el.noIcon).to.be.false; }); it('11. should have tags default to empty array', async () => { const el = await fixture(html``); expect(el.tags).to.be.an('array'); expect(el.tags.length).to.equal(0); }); it('12. should have prefixImageUrl default to empty string', async () => { const el = await fixture(html``); expect(el.prefixImageUrl).to.equal(''); }); it('13. should have hasSlottedContent default to false', async () => { const el = await fixture(html``); expect(el.hasSlottedContent).to.be.false; }); it('14. should have hasSlottedIcon default to false', async () => { const el = await fixture(html``); expect(el.hasSlottedIcon).to.be.false; }); // === VARIANT TESTS === it('15. should apply success variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--success')).to.be.true; }); it('16. should apply info variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--info')).to.be.true; }); it('17. should apply warning variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--warning')).to.be.true; }); it('18. should apply error variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--error')).to.be.true; }); it('19. should apply gray variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--gray')).to.be.true; }); it('20. should apply black variant class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--black')).to.be.true; }); it('21. should reflect variant attribute', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('error'); }); // === OPEN STATE === it('22. should apply open class when open', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--open')).to.be.true; }); it('23. should reflect open attribute', async () => { const el = await fixture(html``); expect(el.hasAttribute('open')).to.be.true; }); it('24. should have role=alert', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.getAttribute('role')).to.equal('alert'); }); it('25. should have aria-hidden true when closed', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.getAttribute('aria-hidden')).to.equal('true'); }); it('26. should have aria-hidden false when open', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.getAttribute('aria-hidden')).to.equal('false'); }); // === CLOSABLE === it('27. should show close button when closable', async () => { const el = await fixture(html``); const closeBtn = el.shadowRoot!.querySelector('[part~="close-button"]'); expect(closeBtn).to.exist; }); it('28. should not show close button when not closable', async () => { const el = await fixture(html``); const closeBtn = el.shadowRoot!.querySelector('[part~="close-button"]'); expect(closeBtn).to.be.null; }); it('29. should apply closable class when closable', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--closable')).to.be.true; }); it('30. should reflect closable attribute', async () => { const el = await fixture(html``); expect(el.hasAttribute('closable')).to.be.true; }); // === TITLE & CONTENT === it('31. should display title text', async () => { const el = await fixture(html``); const titleEl = el.shadowRoot!.querySelector('.alert__message--title'); expect(titleEl!.textContent).to.contain('Alert!'); }); it('32. should display content text', async () => { const el = await fixture(html``); const contentEl = el.shadowRoot!.querySelector('.alert__message--content'); expect(contentEl!.textContent).to.contain('Description'); }); it('33. should apply no-content class when content is empty', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--no-content')).to.be.true; }); // === ICON === it('34. should show default icon for success variant', async () => { const el = await fixture(html``); const icon = el.shadowRoot!.querySelector('.alert__prefix-icon-container nile-icon'); expect(icon).to.exist; }); it('35. should hide icon when noIcon is true', async () => { const el = await fixture(html``); await el.updateComplete; const iconSlot = el.shadowRoot!.querySelector('slot[name="icon"]'); expect(iconSlot!.hasAttribute('hidden')).to.be.true; }); it('36. should reflect no-icon attribute', async () => { const el = await fixture(html``); expect(el.hasAttribute('no-icon')).to.be.true; }); // === ICON NAMES BY VARIANT === it('37. should return correct icon name for success', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('check-circle'); }); it('38. should return correct icon name for info', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('info-circle'); }); it('39. should return correct icon name for warning', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('alert-circle'); }); it('40. should return correct icon name for error', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('alert-circle'); }); it('41. should return correct icon name for gray', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('info-circle'); }); it('42. should return correct icon name for black', async () => { const el = await fixture(html``); const name = el.getIconNameByVariant(); expect(name).to.contain('info-circle'); }); // === ICON COLORS BY VARIANT === it('43. should return correct icon color for success', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('success'); }); it('44. should return correct icon color for info', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('brand'); }); it('45. should return correct icon color for warning', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('warning'); }); it('46. should return correct icon color for error', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('error'); }); it('47. should return correct icon color for gray', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('tertiary'); }); it('48. should return correct icon color for black', async () => { const el = await fixture(html``); const color = el.getIconColorByVariant(); expect(color).to.contain('black'); }); // === SLOTS === it('49. should have icon slot', async () => { const el = await fixture(html``); const iconSlot = el.shadowRoot!.querySelector('slot[name="icon"]'); expect(iconSlot).to.exist; }); it('50. should have message slot', async () => { const el = await fixture(html``); const messageSlot = el.shadowRoot!.querySelector('slot[name="message"]'); expect(messageSlot).to.exist; }); // === CSS PARTS === it('51. should have base part', async () => { const el = await fixture(html``); expect(el.shadowRoot!.querySelector('[part~="base"]')).to.exist; }); it('52. should have icon part', async () => { const el = await fixture(html``); expect(el.shadowRoot!.querySelector('[part~="icon"]')).to.exist; }); it('53. should have message part', async () => { const el = await fixture(html``); expect(el.shadowRoot!.querySelector('[part~="message"]')).to.exist; }); it('54. should have close-button part when closable', async () => { const el = await fixture(html``); expect(el.shadowRoot!.querySelector('[part~="close-button"]')).to.exist; }); // === DYNAMIC UPDATES === it('55. should update variant dynamically', async () => { const el = await fixture(html``); el.variant = 'error'; await el.updateComplete; const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--error')).to.be.true; }); it('56. should update closable dynamically', async () => { const el = await fixture(html``); el.closable = true; await el.updateComplete; expect(el.shadowRoot!.querySelector('[part~="close-button"]')).to.exist; }); it('57. should update title dynamically', async () => { const el = await fixture(html``); el.title = 'New Title'; await el.updateComplete; const titleEl = el.shadowRoot!.querySelector('.alert__message--title'); expect(titleEl!.textContent).to.contain('New Title'); }); it('58. should update content dynamically', async () => { const el = await fixture(html``); el.content = 'New Content'; await el.updateComplete; const contentEl = el.shadowRoot!.querySelector('.alert__message--content'); expect(contentEl!.textContent).to.contain('New Content'); }); it('59. should update noIcon dynamically', async () => { const el = await fixture(html``); el.noIcon = true; await el.updateComplete; const iconSlot = el.shadowRoot!.querySelector('slot[name="icon"]'); expect(iconSlot!.hasAttribute('hidden')).to.be.true; }); it('60. should update duration dynamically', async () => { const el = await fixture(html``); el.duration = 5000; await el.updateComplete; expect(el.duration).to.equal(5000); }); // === PREFIX IMAGE === it('61. should render prefix image when prefixImageUrl is set', async () => { const el = await fixture(html``); await el.updateComplete; const img = el.shadowRoot!.querySelector('.alert__prefix-img'); expect(img).to.exist; }); it('62. should not render prefix image when prefixImageUrl is empty', async () => { const el = await fixture(html``); const img = el.shadowRoot!.querySelector('.alert__prefix-img'); expect(img).to.be.null; }); it('63. should reflect prefix-image-url attribute', async () => { const el = await fixture(html``); expect(el.getAttribute('prefix-image-url')).to.equal('test.png'); }); // === CLOSE ICON NAME === it('64. should have default closeIconName', async () => { const el = await fixture(html``); expect(el.closeIconName).to.contain('x-close'); }); it('65. should reflect close-icon-name attribute', async () => { const el = await fixture(html``); expect(el.closeIconName).to.equal('custom-close'); }); // === ELEMENT INSTANCE === it('66. should be instance of NileToast', async () => { const el = await fixture(html``); expect(el).to.be.instanceOf(NileToast); }); it('67. should have correct tag name', async () => { const el = await fixture(html``); expect(el.tagName.toLowerCase()).to.equal('nile-toast'); }); it('68. should have static styles', async () => { expect(NileToast.styles).to.exist; }); it('69. should be a defined custom element', async () => { expect(customElements.get('nile-toast')).to.exist; }); // === SHOW/HIDE METHODS === it('70. show() should set open to true', async () => { const el = await fixture(html``); el.show(); await el.updateComplete; expect(el.open).to.be.true; }); it('71. hide() should set open to false', async () => { const el = await fixture(html``); await el.updateComplete; el.hide(); await el.updateComplete; expect(el.open).to.be.false; }); it('72. show() should return undefined if already open', async () => { const el = await fixture(html``); await el.updateComplete; const result = await el.show(); expect(result).to.be.undefined; }); it('73. hide() should return undefined if already closed', async () => { const el = await fixture(html``); const result = await el.hide(); expect(result).to.be.undefined; }); // === MESSAGE SLOT === it('74. message slot should have aria-live polite', async () => { const el = await fixture(html``); const messageSlot = el.shadowRoot!.querySelector('slot[name="message"]'); expect(messageSlot!.getAttribute('aria-live')).to.equal('polite'); }); it('75. message slot should have alert__message class', async () => { const el = await fixture(html``); const messageSlot = el.shadowRoot!.querySelector('.alert__message'); expect(messageSlot).to.exist; }); // === VARIANT ATTRIBUTE REFLECTIONS === it('76. success variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('success'); }); it('77. info variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('info'); }); it('78. warning variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('warning'); }); it('79. error variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('error'); }); it('80. gray variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('gray'); }); it('81. black variant reflects', async () => { const el = await fixture(html``); expect(el.getAttribute('variant')).to.equal('black'); }); // === COMBINED STATES === it('82. open + closable', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--open')).to.be.true; expect(base!.classList.contains('alert--closable')).to.be.true; }); it('83. open + closable + error variant', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--open')).to.be.true; expect(base!.classList.contains('alert--closable')).to.be.true; expect(base!.classList.contains('alert--error')).to.be.true; }); it('84. open + title + content', async () => { const el = await fixture(html``); expect(el.title).to.equal('Alert'); expect(el.content).to.equal('Details'); }); it('85. closable + noIcon', async () => { const el = await fixture(html``); expect(el.closable).to.be.true; expect(el.noIcon).to.be.true; }); // === CLOSE BUTTON === it('86. close button should have label "close"', async () => { const el = await fixture(html``); const closeBtn = el.shadowRoot!.querySelector('[part~="close-button"]'); expect(closeBtn!.getAttribute('label')).to.equal('close'); }); it('87. close button should be nile-icon-button', async () => { const el = await fixture(html``); const closeBtn = el.shadowRoot!.querySelector('[part~="close-button"]'); expect(closeBtn!.tagName.toLowerCase()).to.equal('nile-icon-button'); }); it('88. close button should have alert__close-button class', async () => { const el = await fixture(html``); const closeBtn = el.shadowRoot!.querySelector('.alert__close-button'); expect(closeBtn).to.exist; }); // === ALERT CLASS === it('89. should always have alert class', async () => { const el = await fixture(html``); const base = el.shadowRoot!.querySelector('.alert'); expect(base).to.exist; }); // === TITLE DISPLAY === it('90. should have alert__message--title element', async () => { const el = await fixture(html``); const title = el.shadowRoot!.querySelector('.alert__message--title'); expect(title).to.exist; }); it('91. title should be visible when set', async () => { const el = await fixture(html``); const title = el.shadowRoot!.querySelector('.alert__message--title'); expect(title!.textContent).to.contain('Visible Title'); }); // === CONTENT DISPLAY === it('92. should show content when both title and content are set', async () => { const el = await fixture(html``); const content = el.shadowRoot!.querySelector('.alert__message--content'); expect(content).to.exist; }); it('93. should not show content element when title is empty', async () => { const el = await fixture(html``); const content = el.shadowRoot!.querySelector('.alert__message--content'); expect(content).to.be.null; }); // === CREATION === it('94. should work when created via createElement', async () => { const el = document.createElement('nile-toast') as NileToast; document.body.appendChild(el); await el.updateComplete; expect(el.shadowRoot!.querySelector('.alert')).to.exist; document.body.removeChild(el); }); it('95. shadow root mode should be open', async () => { const el = await fixture(html``); expect(el.shadowRoot!.mode).to.equal('open'); }); it('96. should handle rapid variant changes', async () => { const el = await fixture(html``); el.variant = 'error'; el.variant = 'warning'; el.variant = 'info'; await el.updateComplete; const base = el.shadowRoot!.querySelector('[part~="base"]'); expect(base!.classList.contains('alert--info')).to.be.true; }); it('97. should handle rapid open/close', async () => { const el = await fixture(html``); el.open = true; el.open = false; el.open = true; await el.updateComplete; expect(el.open).to.be.true; }); it('98. should update tags dynamically', async () => { const el = await fixture(html``); el.tags = [{ content: 'Tag1', imageUrl: '' }]; await el.updateComplete; expect(el.tags.length).to.equal(1); }); it('99. should handle all properties together', async () => { const el = await fixture(html` `); expect(el.open).to.be.true; expect(el.closable).to.be.true; expect(el.variant).to.equal('error'); expect(el.title).to.equal('Error'); expect(el.content).to.equal('Something went wrong'); expect(el.noIcon).to.be.true; }); it('100. should handle setting duration to finite value', async () => { const el = await fixture(html``); expect(el.duration).to.equal(3000); }); });