import { newSpecPage } from '@stencil/core/testing';
import { BsDropdownOptions } from '../bs-dropdown-options';
import { faker } from '@faker-js/faker';
import { fireEvent } from '@testing-library/dom';
describe('bs-dropdown-options', () => {
  describe('Test rendering', () => {
    let dropdownId;
    let label;
    beforeEach(() => {
      dropdownId = faker.random.alphaNumeric(20);
      label = faker.random.words(2);
    });
    it('renders top position, dropdown ID, with label', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `
          <bs-dropdown-options
            shown
            dropdown-id="${dropdownId}"
            label="${label}"
            position="top"
          >
          </bs-dropdown-options>`,
      });
      page.rootInstance.isVisible = true;
      await page.waitForChanges();
      const component = page.root;
      const ulComponent = component.shadowRoot.querySelector('ul');
      expect(component.getAttribute('data-position')).toBe('top');
      expect(component.getAttribute('data-variant')).toBeNull();
      expect(ulComponent.getAttribute('id')).toBe(dropdownId);
      expect(ulComponent.getAttribute('aria-label')).toBe(label);
      expect(ulComponent.getAttribute('aria-activedescendant')).toBeNull();
    });
    // not data shown / no data position // no label
    it('renders not shown, no position, without label', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `
          <bs-dropdown-options dropdown-id="${dropdownId}">
          </bs-dropdown-options>`,
      });
      page.rootInstance.isVisible = false;
      await page.waitForChanges();
      const component = page.root;
      const ulComponent = component.shadowRoot.querySelector('ul');
      expect(component.getAttribute('data-shown')).toBeNull();
      expect(component.getAttribute('data-position')).toBeNull();
      expect(component.getAttribute('data-variant')).toBeNull();
      expect(ulComponent.getAttribute('id')).toBe(dropdownId);
      expect(ulComponent.getAttribute('aria-label')).toBeNull();
      expect(ulComponent.getAttribute('aria-activedescendant')).toBeNull();
    });
    // data-variant fit
    it('renders data-variant when given', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `
          <bs-dropdown-options variant="fit" dropdown-id="${dropdownId}">
          </bs-dropdown-options>`,
      });
      expect(page.root.getAttribute('data-variant')).toEqual('fit');
    });
    // activedescendant id / undefined
    it('renders aria-activedescendant when given', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `
          <bs-dropdown-options dropdown-id="${dropdownId}">
          </bs-dropdown-options>`,
      });
      const activeDescendantId = faker.random.alphaNumeric(10);
      page.rootInstance.activeDescendantId = activeDescendantId;
      await page.waitForChanges();
      const ulComponent = page.root.shadowRoot.querySelector('ul');
      expect(ulComponent.getAttribute('aria-activedescendant')).toBe(activeDescendantId);
    });
  });
  describe('Test event handling', () => {
    it('handles click event when shown and clicking inside by not closing', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `<bs-dropdown-options shown dropdown-id="some-id"></bs-dropdown-options>`,
      });
      page.rootInstance.closeOptions = jest.fn();
      fireEvent.click(page.root);
      expect(page.rootInstance.closeOptions).not.toHaveBeenCalled();
    });
    it('handles click event when shown and clicking outside by closing', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `
        <bs-dropdown-options shown dropdown-id="some-id"></bs-dropdown-options>
        <div id="other-area"></div>`,
      });
      page.rootInstance.closeOptions = jest.fn();
      const otherArea = page.doc.getElementById('other-area');
      fireEvent.click(otherArea);
      expect(page.rootInstance.closeOptions).toHaveBeenCalled();
    });
    it('handles click event when not shown by not closing', async () => {
      const page = await newSpecPage({
        components: [BsDropdownOptions],
        html: `<bs-dropdown-options dropdown-id="some-id"></bs-dropdown-options>`,
      });
      page.rootInstance.closeOptions = jest.fn();
      fireEvent.click(page.root);
      expect(page.rootInstance.closeOptions).not.toHaveBeenCalled();
    });
  });
  describe('Test component class logic', () => {
    let comp;
    beforeEach(() => {
      comp = new BsDropdownOptions();
    });
    // describe('handleVisible()', () => {
    //   it('should set not visible', () => {
    //     const ev = new CustomEvent('visible', { detail: false });
    //     comp.isVisible = true;
    //     comp.handleVisible(ev);
    //     expect(comp.isVisible).toBe(false);
    //   });
    //   it('should set visible and scroll focused item into view', () => {
    //     const ev = new CustomEvent('visible', { detail: true });
    //     comp.isVisible = false;
    //     comp.focusedIndex = 3;
    //     const options = generateMockOptions(5);
    //     options[3].scrollIntoView = jest.fn();
    //     jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
    //     comp.handleVisible(ev);
    //     expect(comp.isVisible).toBe(true);
    //     expect(options[3].scrollIntoView).toHaveBeenCalled();
    //   });
    // });
    describe('passDownProps()', () => {
      let dropdownId;
      let index;
      beforeEach(() => {
        dropdownId = faker.random.alphaNumeric(10);
        index = faker.random.numeric(10);
      });
      it('should pass down the props when shown is true', () => {
        const mockEl = document.createElement('b-dropdown-option');
        comp.dropdownId = dropdownId;
        comp.shown = true;
        comp.passDownProps(mockEl, index);
        expect(mockEl.getAttribute('shown')).toBe('true');
        expect(mockEl.getAttribute('index')).toBe(index);
        expect(mockEl.getAttribute('dropdown-id')).toBe(dropdownId);
        expect(mockEl.getAttribute('dropdownId')).toBe(dropdownId);
      });
      it('should pass down the props when shown is false', () => {
        const mockEl = document.createElement('b-dropdown-option');
        comp.dropdownId = dropdownId;
        comp.shown = false;
        comp.passDownProps(mockEl, index);
        expect(mockEl.getAttribute('shown')).toBe('false');
        expect(mockEl.getAttribute('index')).toBe(index);
        expect(mockEl.getAttribute('dropdown-id')).toBe(dropdownId);
        expect(mockEl.getAttribute('dropdownId')).toBe(dropdownId);
      });
    });
    describe('componentDidLoad()', () => {
      beforeEach(() => {
        comp.passDownProps = jest.fn();
      });
      it('should set selected index if an option has selected prop', () => {
        const options = generateMockOptions(3);
        options[1].setAttribute('selected', '');
        jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
        comp.selectedIndex = -1;
        comp.componentDidLoad();
        expect(comp.selectedIndex).toBe(1);
      });
      it('should not set selected index if selected prop is null', () => {
        const options = generateMockOptions(3);
        jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
        comp.selectedIndex = -1;
        comp.componentDidLoad();
        expect(comp.selectedIndex).toBe(-1);
      });
    });
    describe('componentDidUpdate()', () => {
      it('should move the selected index', () => {
        const options = generateMockOptions(3);
        options[1].setAttribute('selected', '');
        jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
        comp.selectedIndex = 2;
        comp.componentDidUpdate();
        expect(options[0].getAttribute('selected')).toBeNull();
        expect(options[1].getAttribute('selected')).toBeNull();
        expect(options[2].getAttribute('selected')).toBe('true');
      });
    });
    describe('removeCurrentFocus()', () => {
      it('should remove focus if something is focused', () => {
        const options = generateMockOptions(3);
        options[2].setAttribute('focused', '');
        jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
        comp.focusedIndex = 2;
        comp.removeCurrentFocus();
        expect(options[2].getAttribute('focused')).toBeNull();
      });
    });
    // describe('handleShown()', () => {
    //   it('should show, store ref of toggle element, and not focus selected', () => {
    //     const toggleEl = document.createElement('button');
    //     Object.defineProperty(document, 'activeElement', { value: toggleEl, configurable: true });
    //     comp.selectedIndex = -1;
    //     comp.focusedIndex = -1;
    //     comp.handleShown(true);
    //     expect(comp.toggleEl).toEqual(toggleEl);
    //     expect(comp.focusedIndex).toEqual(-1);
    //   });
    //   it('should show, store ref of toggle element, and focus selected', () => {
    //     const toggleEl = document.createElement('button');
    //     Object.defineProperty(document, 'activeElement', { value: toggleEl, configurable: true });
    //     const options = generateMockOptions(3);
    //     jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
    //     comp.selectedIndex = 2;
    //     comp.focusedIndex = -1;
    //     comp.handleShown(true);
    //     expect(comp.toggleEl).toEqual(toggleEl);
    //     expect(comp.focusedIndex).toEqual(2);
    //     expect(options[2].getAttribute('focused')).toBe('true');
    //   });
    //   it('should not show, focus the toggle element, and reset focus index', () => {
    //     const toggleEl = document.createElement('button');
    //     const mockEv = jest.fn();
    //     toggleEl.addEventListener('focus', mockEv);
    //     comp.toggleEl = toggleEl;
    //     comp.focusedIndex = 3;
    //     comp.removeCurrentFocus = jest.fn();
    //     comp.handleShown(false);
    //     expect(mockEv).toHaveBeenCalled();
    //     expect(comp.focusedIndex).toEqual(-1);
    //   });
    // });
    // Combined tests of these functions since they're all related
    describe('handleKeyDown()/traverse()/closeOptions()', () => {
      it('should not do anything when not showing', () => {
        comp.shown = false;
        comp.traverse = jest.fn();
        comp.closeOptions = jest.fn();
        const kbEv = new KeyboardEvent('keydown', { key: 'ArrowDown' });
        kbEv.preventDefault = jest.fn();
        comp.handleKeyDown(kbEv);
        expect(comp.traverse).not.toHaveBeenCalled();
        expect(comp.closeOptions).not.toHaveBeenCalled();
        expect(kbEv.preventDefault).not.toHaveBeenCalled();
      });
      describe('shown = true', () => {
        let options;
        beforeEach(() => {
          comp.shown = true;
          comp.removeCurrentFocus = jest.fn();
          options = generateMockOptions(3);
          jest.spyOn(comp, 'slotEls', 'get').mockReturnValue(options);
        });
        it('should handle ArrowDown key', () => {
          comp.focusedIndex = 1;
          const kbEv = new KeyboardEvent('keydown', { key: 'ArrowDown' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(2);
          expect(options[2].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle ArrowDown key and wrap', () => {
          comp.focusedIndex = 2;
          const kbEv = new KeyboardEvent('keydown', { key: 'ArrowDown' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(0);
          expect(options[0].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle ArrowUp key', () => {
          comp.focusedIndex = 1;
          const kbEv = new KeyboardEvent('keydown', { key: 'ArrowUp' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(0);
          expect(options[0].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle ArrowUp key and wrap', () => {
          comp.focusedIndex = 0;
          const kbEv = new KeyboardEvent('keydown', { key: 'ArrowUp' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(2);
          expect(options[2].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle Home key', () => {
          comp.focusedIndex = 1;
          const kbEv = new KeyboardEvent('keydown', { key: 'Home' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(0);
          expect(options[0].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle End key', () => {
          comp.focusedIndex = 1;
          const kbEv = new KeyboardEvent('keydown', { key: 'End' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(comp.focusedIndex).toBe(2);
          expect(options[2].getAttribute('focused')).toBe('true');
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle Tab key', () => {
          const kbEv = new KeyboardEvent('keydown', { key: 'Tab' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
        it('should handle Escape key', () => {
          const evSpy = jest.spyOn(comp.close, 'emit');
          const kbEv = new KeyboardEvent('keydown', { key: 'Escape' });
          kbEv.preventDefault = jest.fn();
          comp.handleKeyDown(kbEv);
          expect(evSpy).toHaveBeenCalled();
          expect(kbEv.preventDefault).toHaveBeenCalled();
        });
      });
    });
    describe('handleMouseFocus()', () => {
      it('should set focused index if shown', () => {
        comp.shown = true;
        comp.focusedIndex = -1;
        comp.removeCurrentFocus = jest.fn();
        const ev = new CustomEvent('mousefocus', { detail: 4 });
        comp.handleMouseFocus(ev);
        expect(comp.focusedIndex).toBe(4);
      });
      it('should not set focused index if not shown', () => {
        comp.shown = false;
        comp.focusedIndex = -1;
        comp.removeCurrentFocus = jest.fn();
        const ev = new CustomEvent('mousefocus', { detail: 4 });
        comp.handleMouseFocus(ev);
        expect(comp.focusedIndex).toBe(-1);
      });
    });
    describe('handleSelect()', () => {
      it('should set various internal states on option select and emit change', () => {
        comp.closeOptions = jest.fn();
        comp.activeDescendantId = '';
        comp.selectedIndex = -1;
        const evSpy = jest.spyOn(comp.dropdownchange, 'emit');
        const id = faker.random.alphaNumeric(10);
        const index = faker.random.numeric(1);
        const value = faker.random.word();
        const ev = new CustomEvent('optionselect', { detail: { id, index, value } });
        comp.handleSelect(ev);
        expect(comp.activeDescendantId).toBe(id);
        expect(comp.selectedIndex).toBe(index);
        expect(evSpy).toHaveBeenCalledWith(value);
      });
    });
  });
  const generateMockOptions = (amount) => {
    const options = [];
    for (let i = 0; i < amount; i++) {
      const option = document.createElement('bs-dropdown-option');
      options.push(option);
    }
    return options;
  };
});
//# sourceMappingURL=bs-dropdown-options.spec.js.map
