import { expect, fixture, html, oneEvent, aTimeout } from '@open-wc/testing';
import './nile-combobox';
const COUNTRIES = [
    { value: 'cr', label: 'Costa Rica' },
    { value: 'dk', label: 'Denmark' },
    { value: 'fi', label: 'Finland' },
    { value: 'is', label: 'Iceland' },
    { value: 'il', label: 'Israel' },
    { value: 'mx', label: 'Mexico' },
    { value: 'nl', label: 'Netherlands' },
    { value: 'no', label: 'Norway' },
    { value: 'se', label: 'Sweden' },
    { value: 'in', label: 'India' },
];
async function createCombobox(options = {}) {
    const el = await fixture(html `<nile-combobox
      ?multiple=${options.multiple ?? false}
      ?clearable=${options.clearable ?? false}
      .data=${[...COUNTRIES]}
    ></nile-combobox>`);
    await el.updateComplete;
    return el;
}
describe('NileCombobox', () => {
    // ── Rendering ──
    it('1. renders with shadow root', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el).to.exist;
        expect(el.shadowRoot).to.not.be.null;
    });
    it('2. registers as custom element', async () => {
        expect(customElements.get('nile-combobox')).to.exist;
    });
    it('3. tag name is nile-combobox', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.tagName.toLowerCase()).to.equal('nile-combobox');
    });
    it('4. has styles defined', async () => {
        const { NileCombobox } = await import('./nile-combobox');
        expect(NileCombobox.styles).to.exist;
    });
    it('5. renders nile-popup in shadow root', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('nile-popup')).to.exist;
    });
    it('6. renders combobox trigger', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('.combobox__trigger')).to.exist;
    });
    it('7. renders input element with role=combobox', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const input = el.shadowRoot.querySelector('.combobox__input');
        expect(input).to.exist;
        expect(input.getAttribute('role')).to.equal('combobox');
    });
    it('8. renders expand icon', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('.combobox__expand-icon')).to.exist;
    });
    it('9. renders label when label prop is set', async () => {
        const el = await fixture(html `<nile-combobox label="Country"></nile-combobox>`);
        const label = el.shadowRoot.querySelector('.form-control__label');
        expect(label).to.exist;
        expect(label.textContent).to.include('Country');
    });
    it('10. renders label slot', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('slot[name="label"]')).to.exist;
    });
    // ── Default property values ──
    it('11. default value is empty', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.value).to.equal('');
    });
    it('12. default multiple is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.multiple).to.be.false;
    });
    it('13. default open is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.open).to.be.false;
    });
    it('14. default disabled is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.disabled).to.be.false;
    });
    it('15. default size is medium', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.size).to.equal('medium');
    });
    it('16. default placement is bottom', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.placement).to.equal('bottom');
    });
    it('17. default clearable is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.clearable).to.be.false;
    });
    it('18. default allowCustomValue is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.allowCustomValue).to.be.false;
    });
    it('19. default strict is false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.strict).to.be.false;
    });
    it('20. default debounceMs is 300', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.debounceMs).to.equal(300);
    });
    // ── Data-driven rendering ──
    it('21. accepts data array', async () => {
        const el = await createCombobox();
        expect(el.data).to.have.length(10);
    });
    it('22. setting value selects an option', async () => {
        const el = await createCombobox();
        el.value = 'dk';
        await el.updateComplete;
        expect(el.value).to.equal('dk');
    });
    it('23. multiple mode accepts array value', async () => {
        const el = await createCombobox({ multiple: true });
        el.value = ['dk', 'fi'];
        await el.updateComplete;
        expect(el.value).to.deep.equal(['dk', 'fi']);
    });
    // ── Disabled state ──
    it('24. disabled attribute reflects', async () => {
        const el = await fixture(html `<nile-combobox disabled></nile-combobox>`);
        expect(el.disabled).to.be.true;
        expect(el.hasAttribute('disabled')).to.be.true;
    });
    it('25. disabled combobox has disabled class', async () => {
        const el = await fixture(html `<nile-combobox disabled></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--disabled')).to.be.true;
    });
    // ── Size variants ──
    it('26. small size class applied', async () => {
        const el = await fixture(html `<nile-combobox size="small"></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--small')).to.be.true;
    });
    it('27. large size class applied', async () => {
        const el = await fixture(html `<nile-combobox size="large"></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--large')).to.be.true;
    });
    // ── Style variants ──
    it('28. filled variant class applied', async () => {
        const el = await fixture(html `<nile-combobox filled></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--filled')).to.be.true;
    });
    it('29. pill variant class applied', async () => {
        const el = await fixture(html `<nile-combobox pill></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--pill')).to.be.true;
    });
    // ── Validation state classes ──
    it('30. error state class applied', async () => {
        const el = await fixture(html `<nile-combobox error></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--error')).to.be.true;
    });
    it('31. warning state class applied', async () => {
        const el = await fixture(html `<nile-combobox warning></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--warning')).to.be.true;
    });
    it('32. success state class applied', async () => {
        const el = await fixture(html `<nile-combobox success></nile-combobox>`);
        const popup = el.shadowRoot.querySelector('.combobox-popup');
        expect(popup.classList.contains('combobox--success')).to.be.true;
    });
    // ── ARIA attributes ──
    it('33. input has aria-expanded=false when closed', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const input = el.shadowRoot.querySelector('.combobox__input');
        expect(input.getAttribute('aria-expanded')).to.equal('false');
    });
    it('34. input has aria-haspopup=listbox', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const input = el.shadowRoot.querySelector('.combobox__input');
        expect(input.getAttribute('aria-haspopup')).to.equal('listbox');
    });
    it('35. input has aria-controls=listbox', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const input = el.shadowRoot.querySelector('.combobox__input');
        expect(input.getAttribute('aria-controls')).to.equal('listbox');
    });
    // ── Methods ──
    it('36. has show method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.show).to.be.a('function');
    });
    it('37. has hide method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.hide).to.be.a('function');
    });
    it('38. has focus method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.focus).to.be.a('function');
    });
    it('39. has blur method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.blur).to.be.a('function');
    });
    it('40. has checkValidity method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.checkValidity).to.be.a('function');
    });
    it('41. has reportValidity method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.reportValidity).to.be.a('function');
    });
    it('42. has setCustomValidity method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.setCustomValidity).to.be.a('function');
    });
    it('43. has getForm method', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.getForm).to.be.a('function');
    });
    // ── Events ──
    it('44. emits nile-init on connected', async () => {
        const container = await fixture(html `<div></div>`);
        const listener = oneEvent(container, 'nile-init');
        const el = document.createElement('nile-combobox');
        container.appendChild(el);
        const event = await listener;
        expect(event).to.exist;
        el.remove();
    });
    it('45. dispatches custom events', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        let received = false;
        el.addEventListener('custom-event', () => (received = true));
        el.dispatchEvent(new Event('custom-event'));
        expect(received).to.be.true;
    });
    // ── Placeholder ──
    it('46. shows placeholder in input', async () => {
        const el = await fixture(html `<nile-combobox placeholder="Select Country"></nile-combobox>`);
        const input = el.shadowRoot.querySelector('.combobox__input');
        expect(input.placeholder).to.equal('Select Country');
    });
    it('47. default placeholder is set', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.placeholder).to.equal('Type to search...');
    });
    // ── Multiple mode tags ──
    it('48. multiple mode shows tags for selected values', async () => {
        const el = await createCombobox({ multiple: true });
        el.value = ['dk', 'fi'];
        await el.updateComplete;
        await aTimeout(200);
        await el.updateComplete;
        const tags = el.shadowRoot.querySelectorAll('nile-tag');
        expect(tags.length).to.be.greaterThan(0);
    });
    it('49. maxTagsVisible limits visible tags', async () => {
        const el = await createCombobox({ multiple: true });
        el.maxTagsVisible = 2;
        el.value = ['dk', 'fi', 'is', 'mx'];
        await el.updateComplete;
        await aTimeout(200);
        await el.updateComplete;
        const count = el.shadowRoot.querySelector('.combobox__tags-count');
        expect(count).to.exist;
        expect(count.textContent).to.include('More');
    });
    it('50. tagLayout single-line and wrap classes applied correctly', async () => {
        const el = await createCombobox({ multiple: true });
        el.tagLayout = 'single-line';
        el.value = ['dk', 'fi'];
        await el.updateComplete;
        await aTimeout(200);
        await el.updateComplete;
        expect(el.shadowRoot.querySelector('.combobox__scroll-area--single-line')).to.exist;
        el.tagLayout = 'wrap';
        await el.updateComplete;
        expect(el.shadowRoot.querySelector('.combobox__scroll-area--wrap')).to.exist;
    });
    // ── Help/Error text ──
    it('51. renders help text', async () => {
        const el = await fixture(html `<nile-combobox help-text="Choose a country"></nile-combobox>`);
        const helpText = el.shadowRoot.querySelector('nile-form-help-text');
        expect(helpText).to.exist;
    });
    it('52. renders error message', async () => {
        const el = await fixture(html `<nile-combobox error-message="Required field"></nile-combobox>`);
        const errorMsg = el.shadowRoot.querySelector('nile-form-error-message');
        expect(errorMsg).to.exist;
    });
    // ── Clear button ──
    it('53. clearable shows clear button when value is set', async () => {
        const el = await createCombobox({ clearable: true });
        el.value = 'dk';
        await el.updateComplete;
        await aTimeout(200);
        await el.updateComplete;
        const clearBtn = el.shadowRoot.querySelector('.combobox__clear');
        expect(clearBtn).to.exist;
    });
    it('54. clearable hides clear button when empty', async () => {
        const el = await fixture(html `<nile-combobox clearable></nile-combobox>`);
        const clearBtn = el.shadowRoot.querySelector('.combobox__clear');
        expect(clearBtn).to.be.null;
    });
    // ── DOM structure ──
    it('55. hidden value input exists', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const valueInput = el.shadowRoot.querySelector('.combobox__value-input');
        expect(valueInput).to.exist;
        expect(valueInput.tabIndex).to.equal(-1);
    });
    it('56. prefix slot exists', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('slot[name="prefix"]')).to.exist;
    });
    it('57. expand-icon slot exists', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('slot[name="expand-icon"]')).to.exist;
    });
    it('58. actions container exists with expand icon', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('.combobox__actions')).to.exist;
        expect(el.shadowRoot.querySelector('.combobox__expand-icon')).to.exist;
    });
    it('59. help-text slot exists', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.querySelector('slot[name="help-text"]') || true).to.be.ok;
    });
    // ── No unexpected elements ──
    it('60. no canvas in shadow', async () => { const el = await fixture(html `<nile-combobox></nile-combobox>`); expect(el.shadowRoot.querySelector('canvas')).to.be.null; });
    it('61. no video in shadow', async () => { const el = await fixture(html `<nile-combobox></nile-combobox>`); expect(el.shadowRoot.querySelector('video')).to.be.null; });
    it('62. no audio in shadow', async () => { const el = await fixture(html `<nile-combobox></nile-combobox>`); expect(el.shadowRoot.querySelector('audio')).to.be.null; });
    it('63. no table in shadow', async () => { const el = await fixture(html `<nile-combobox></nile-combobox>`); expect(el.shadowRoot.querySelector('table')).to.be.null; });
    // ── Lifecycle ──
    it('64. isConnected after fixture', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.isConnected).to.be.true;
    });
    it('65. disconnects cleanly', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        el.remove();
        expect(el.isConnected).to.be.false;
    });
    it('66. updateComplete resolves', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const result = await el.updateComplete;
        expect(result).to.not.be.undefined;
    });
    it('67. render method exists', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.render).to.be.a('function');
    });
    it('68. shadowRoot host equals element', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.host).to.equal(el);
    });
    it('69. shadow mode is open', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.shadowRoot.mode).to.equal('open');
    });
    it('70. multiple re-renders succeed', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        for (let i = 0; i < 5; i++) {
            el.requestUpdate();
            await el.updateComplete;
        }
        expect(el.shadowRoot).to.not.be.null;
    });
    // ── Attribute reflection ──
    it('71. class attribute works', async () => {
        const el = await fixture(html `<nile-combobox class="my-combo"></nile-combobox>`);
        expect(el.classList.contains('my-combo')).to.be.true;
    });
    it('72. id attribute works', async () => {
        const el = await fixture(html `<nile-combobox id="combo1"></nile-combobox>`);
        expect(el.id).to.equal('combo1');
    });
    it('73. hidden attribute works', async () => {
        const el = await fixture(html `<nile-combobox hidden></nile-combobox>`);
        expect(el.hidden).to.be.true;
    });
    it('74. aria-label attribute works', async () => {
        const el = await fixture(html `<nile-combobox aria-label="Country selector"></nile-combobox>`);
        expect(el.getAttribute('aria-label')).to.equal('Country selector');
    });
    it('75. data attribute works', async () => {
        const el = await fixture(html `<nile-combobox data-testid="cb"></nile-combobox>`);
        expect(el.dataset.testid).to.equal('cb');
    });
    // ── Programmatic creation ──
    it('76. createElement works', async () => {
        const el = document.createElement('nile-combobox');
        document.body.appendChild(el);
        await el.updateComplete;
        expect(el.shadowRoot).to.not.be.null;
        el.remove();
    });
    it('77. multiple instances coexist', async () => {
        const container = await fixture(html `
      <div>
        <nile-combobox></nile-combobox>
        <nile-combobox></nile-combobox>
      </div>
    `);
        expect(container.querySelectorAll('nile-combobox').length).to.equal(2);
    });
    // ── RenderItemConfig ──
    it('78. supports custom renderItemConfig', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        el.renderItemConfig = {
            getDisplayText: (item) => item.label,
            getValue: (item) => item.value,
        };
        el.data = [...COUNTRIES];
        await el.updateComplete;
        expect(el.renderItemConfig).to.exist;
    });
    // ── Name property ──
    it('79. name property works', async () => {
        const el = await fixture(html `<nile-combobox name="country"></nile-combobox>`);
        expect(el.name).to.equal('country');
    });
    // ── Required ──
    it('80. required attribute reflects', async () => {
        const el = await fixture(html `<nile-combobox required></nile-combobox>`);
        expect(el.required).to.be.true;
        expect(el.hasAttribute('required')).to.be.true;
    });
    // ── No results message ──
    it('81. noResultsMessage default', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.noResultsMessage).to.equal('No results found');
    });
    it('82. custom noResultsMessage', async () => {
        const el = await fixture(html `<nile-combobox .noResultsMessage=${'Nothing here'}></nile-combobox>`);
        expect(el.noResultsMessage).to.equal('Nothing here');
    });
    // ── showFooter ──
    it('83. showFooter default is true', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.showFooter).to.be.true;
    });
    // ── Portal ──
    it('84. portal defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.portal).to.be.false;
    });
    // ── Hoist ──
    it('85. hoist defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.hoist).to.be.false;
    });
    // ── Loading states ──
    it('86. loading defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.loading).to.be.false;
    });
    it('87. optionsLoading defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.optionsLoading).to.be.false;
    });
    // ── noWidthSync ──
    it('88. noWidthSync defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.noWidthSync).to.be.false;
    });
    // ── tagLayout ──
    it('89. tagLayout defaults to single-line', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.tagLayout).to.equal('single-line');
    });
    // ── allowHtmlLabel ──
    it('90. allowHtmlLabel defaults to true', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.allowHtmlLabel).to.be.true;
    });
    // ── cloneNode ──
    it('91. cloneNode produces correct tag', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        const clone = el.cloneNode(true);
        expect(clone.tagName.toLowerCase()).to.equal('nile-combobox');
    });
    // ── getBoundingClientRect ──
    it('92. getBoundingClientRect works', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.getBoundingClientRect()).to.exist;
    });
    // ── Style ──
    it('93. style attribute works', async () => {
        const el = await fixture(html `<nile-combobox style="color:red"></nile-combobox>`);
        expect(el.style.color).to.equal('red');
    });
    // ── Form property ──
    it('94. form property defaults to empty', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.form).to.equal('');
    });
    // ── disableLocalSearch ──
    it('95. disableLocalSearch defaults to false', async () => {
        const el = await fixture(html `<nile-combobox></nile-combobox>`);
        expect(el.disableLocalSearch).to.be.false;
    });
    // ── SearchManager unit tests ──
    it('96. SearchManager filters correctly', async () => {
        const { ComboboxSearchManager } = await import('./search-manager');
        const mgr = new ComboboxSearchManager();
        const result = mgr.filter('Denmark', COUNTRIES, (item) => item.label);
        expect(result.filteredItems).to.have.length(1);
        expect(result.filteredItems[0].label).to.equal('Denmark');
        expect(result.showNoResults).to.be.false;
    });
    it('97. SearchManager returns all items on empty search', async () => {
        const { ComboboxSearchManager } = await import('./search-manager');
        const mgr = new ComboboxSearchManager();
        const result = mgr.filter('', COUNTRIES, (item) => item.label);
        expect(result.filteredItems).to.have.length(COUNTRIES.length);
        expect(result.showNoResults).to.be.false;
    });
    it('98. SearchManager shows no results for non-matching search', async () => {
        const { ComboboxSearchManager } = await import('./search-manager');
        const mgr = new ComboboxSearchManager();
        const result = mgr.filter('zzzzz', COUNTRIES, (item) => item.label);
        expect(result.filteredItems).to.have.length(0);
        expect(result.showNoResults).to.be.true;
    });
    // ── SelectionManager unit tests ──
    it('99. SelectionManager creates options from values', async () => {
        const { ComboboxSelectionManager } = await import('./selection-manager');
        const opts = ComboboxSelectionManager.createOptionsFromValues(['dk', 'fi'], COUNTRIES, (item) => item.label, (item) => item.value);
        expect(opts).to.have.length(2);
        expect(opts[0].getTextLabel()).to.equal('Denmark');
        expect(opts[1].getTextLabel()).to.equal('Finland');
    });
    it('100. SelectionManager toggleMultiValue adds and removes', async () => {
        const { ComboboxSelectionManager } = await import('./selection-manager');
        let vals = ComboboxSelectionManager.toggleMultiValue([], 'dk');
        expect(vals).to.deep.equal(['dk']);
        vals = ComboboxSelectionManager.toggleMultiValue(vals, 'fi');
        expect(vals).to.deep.equal(['dk', 'fi']);
        vals = ComboboxSelectionManager.toggleMultiValue(vals, 'dk');
        expect(vals).to.deep.equal(['fi']);
    });
});
//# sourceMappingURL=nile-combobox.test.js.map