import { expect, fixture, html, oneEvent } from '@open-wc/testing';
import './nile-markdown';
import NileMarkdown from './nile-markdown';
describe('NileMarkdown', () => {
// === 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 have base part', async () => {
const el = await fixture(
html``
);
const base = el.shadowRoot!.querySelector('[part~="base"]');
expect(base).to.exist;
});
it('4. should be instance of NileMarkdown', async () => {
const el = await fixture(
html``
);
expect(el).to.be.instanceOf(NileMarkdown);
});
it('5. should have correct tag name', async () => {
const el = await fixture(
html``
);
expect(el.tagName.toLowerCase()).to.equal('nile-markdown');
});
// === DEFAULT PROPERTIES ===
it('6. should have value default to empty string', async () => {
const el = await fixture(
html``
);
expect(el.value).to.equal('');
});
it('7. should have tabSize default to 4', async () => {
const el = await fixture(
html``
);
expect(el.tabSize).to.equal(4);
});
// === VALUE PROPERTY ===
it('8. should render markdown from value property', async () => {
const el = await fixture(
html``
);
await el.updateComplete;
const h1 = el.shadowRoot!.querySelector('h1');
expect(h1).to.exist;
expect(h1!.textContent).to.equal('Heading');
});
it('9. should render bold text', async () => {
const el = await fixture(
html``
);
await el.updateComplete;
const strong = el.shadowRoot!.querySelector('strong');
expect(strong).to.exist;
expect(strong!.textContent).to.equal('bold');
});
it('10. should render links', async () => {
const el = await fixture(
html``
);
await el.updateComplete;
const a = el.shadowRoot!.querySelector('a');
expect(a).to.exist;
expect(a!.getAttribute('href')).to.equal('https://example.com');
});
it('11. should render lists', async () => {
const el = await fixture(
html``
);
el.value = '- one\n- two\n- three';
await el.updateComplete;
const items = el.shadowRoot!.querySelectorAll('li');
expect(items.length).to.equal(3);
});
it('12. should render code blocks', async () => {
const el = await fixture(
html``
);
el.value = '```\nconst a = 1;\n```';
await el.updateComplete;
const pre = el.shadowRoot!.querySelector('pre code');
expect(pre).to.exist;
});
it('13. should render GFM tables', async () => {
const el = await fixture(
html``
);
el.value = '| a | b |\n| - | - |\n| 1 | 2 |';
await el.updateComplete;
const table = el.shadowRoot!.querySelector('table');
expect(table).to.exist;
});
it('14. should render blockquotes', async () => {
const el = await fixture(
html``
);
el.value = '> quoted';
await el.updateComplete;
const quote = el.shadowRoot!.querySelector('blockquote');
expect(quote).to.exist;
});
// === SCRIPT CHILD ===
it('15. should render markdown from a script child', async () => {
const el = await fixture(html`
`);
await el.updateComplete;
const h1 = el.shadowRoot!.querySelector('h1');
expect(h1).to.exist;
expect(h1!.textContent).to.equal('From Script');
});
it('16. should normalize indented script content', async () => {
const el = await fixture(html`
`);
await el.updateComplete;
// Indented content must not be treated as a code block
expect(el.shadowRoot!.querySelector('pre')).to.not.exist;
expect(el.shadowRoot!.querySelector('h1')).to.exist;
expect(el.shadowRoot!.querySelector('p')).to.exist;
});
it('17. value should take precedence over script child', async () => {
const el = await fixture(html`
`);
await el.updateComplete;
const h1 = el.shadowRoot!.querySelector('h1');
expect(h1!.textContent).to.equal('From Value');
});
// === DYNAMIC UPDATES ===
it('18. should re-render when value changes', async () => {
const el = await fixture(
html``
);
await el.updateComplete;
el.value = '# Two';
await el.updateComplete;
await el.updateComplete;
const h1 = el.shadowRoot!.querySelector('h1');
expect(h1!.textContent).to.equal('Two');
});
it('19. should re-render when script content changes', async () => {
const el = await fixture(html`
`);
await el.updateComplete;
const script = el.querySelector('script')!;
script.textContent = '# After';
// MutationObserver fires async
await new Promise(resolve => setTimeout(resolve, 0));
await el.updateComplete;
const h1 = el.shadowRoot!.querySelector('h1');
expect(h1!.textContent).to.equal('After');
});
it('20. renderMarkdown() should re-parse manually', async () => {
const el = await fixture(
html``
);
await el.updateComplete;
el.renderMarkdown();
await el.updateComplete;
expect(el.shadowRoot!.querySelector('h1')!.textContent).to.equal('Manual');
});
// === EVENTS ===
it('21. should emit nile-markdown-rendered after rendering', async () => {
const el = document.createElement('nile-markdown') as NileMarkdown;
const listener = oneEvent(el, 'nile-markdown-rendered');
el.value = '# Event';
document.body.appendChild(el);
const event = await listener;
expect(event).to.exist;
el.remove();
});
// === STATIC API ===
it('22. getMarked() should return a shared instance', () => {
const a = NileMarkdown.getMarked();
const b = NileMarkdown.getMarked();
expect(a).to.equal(b);
});
it('23. updateAll() should not throw', async () => {
await fixture(
html``
);
expect(() => NileMarkdown.updateAll()).to.not.throw();
});
// === EDGE CASES ===
it('24. should handle empty content', async () => {
const el = await fixture(
html``
);
const base = el.shadowRoot!.querySelector('[part~="base"]');
expect(base!.textContent!.trim()).to.equal('');
});
it('25. should have static styles', () => {
expect(NileMarkdown.styles).to.exist;
});
});