summaryrefslogtreecommitdiff
path: root/spec/frontend/listbox/index_spec.js
blob: 45659a0e5232a13dfdfdc3b56d2a38f0152d533a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { nextTick } from 'vue';
import { getAllByRole, getByRole } from '@testing-library/dom';
import { GlDropdown } from '@gitlab/ui';
import { createWrapper } from '@vue/test-utils';
import { initListbox, parseAttributes } from '~/listbox';
import { getFixture, setHTMLFixture } from 'helpers/fixtures';

jest.mock('~/lib/utils/url_utility');

const fixture = getFixture('listbox/redirect_listbox.html');

const parsedAttributes = (() => {
  const div = document.createElement('div');
  div.innerHTML = fixture;
  return parseAttributes(div.firstChild);
})();

describe('initListbox', () => {
  let instance;

  afterEach(() => {
    if (instance) {
      instance.$destroy();
    }
  });

  const setup = (...args) => {
    instance = initListbox(...args);
  };

  // TODO: Rewrite these finders to use better semantics once the
  // implementation is switched to GlListbox
  // https://gitlab.com/gitlab-org/gitlab/-/issues/348738
  const findToggleButton = () => document.body.querySelector('.gl-dropdown-toggle');
  const findItem = (text) => getByRole(document.body, 'menuitem', { name: text });
  const findItems = () => getAllByRole(document.body, 'menuitem');
  const findSelectedItems = () =>
    findItems().filter(
      (menuitem) =>
        !menuitem
          .querySelector('.gl-new-dropdown-item-check-icon')
          .classList.contains('gl-visibility-hidden'),
    );

  it('returns null given no element', () => {
    setup();

    expect(instance).toBe(null);
  });

  it('throws given an invalid element', () => {
    expect(() => setup(document.body)).toThrow();
  });

  describe('given a valid element', () => {
    let onChangeSpy;

    beforeEach(async () => {
      setHTMLFixture(fixture);
      onChangeSpy = jest.fn();
      setup(document.querySelector('.js-redirect-listbox'), { onChange: onChangeSpy });

      await nextTick();
    });

    it('returns an instance', () => {
      expect(instance).not.toBe(null);
    });

    it('renders button with selected item text', () => {
      expect(findToggleButton().textContent.trim()).toBe('Bar');
    });

    it('has the correct item selected', () => {
      const selectedItems = findSelectedItems();
      expect(selectedItems).toHaveLength(1);
      expect(selectedItems[0].textContent.trim()).toBe('Bar');
    });

    it('applies additional classes from the original element', () => {
      expect(instance.$el.classList).toContain('test-class-1', 'test-class-2');
    });

    describe.each(parsedAttributes.items)('clicking on an item', (item) => {
      beforeEach(async () => {
        findItem(item.text).click();

        await nextTick();
      });

      it('calls the onChange callback with the item', () => {
        expect(onChangeSpy).toHaveBeenCalledWith(item);
      });

      it('updates the toggle button text', () => {
        expect(findToggleButton().textContent.trim()).toBe(item.text);
      });

      it('marks the item as selected', () => {
        const selectedItems = findSelectedItems();
        expect(selectedItems).toHaveLength(1);
        expect(selectedItems[0].textContent.trim()).toBe(item.text);
      });
    });

    it('passes the "right" prop through to the underlying component', () => {
      const wrapper = createWrapper(instance).findComponent(GlDropdown);
      expect(wrapper.props('right')).toBe(parsedAttributes.right);
    });
  });
});