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