summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared/components/editor_lite_spec.js
blob: 9298851d1433965859bba818fb068336b2ec18c0 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
import Editor from '~/editor/editor_lite';
import { EDITOR_READY_EVENT } from '~/editor/constants';

jest.mock('~/editor/editor_lite');

describe('Editor Lite component', () => {
  let wrapper;
  let mockInstance;

  const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
  const fileName = 'lorem.txt';
  const fileGlobalId = 'snippet_777';
  const createInstanceMock = jest.fn().mockImplementation(() => {
    mockInstance = {
      onDidChangeModelContent: jest.fn(),
      updateModelLanguage: jest.fn(),
      getValue: jest.fn(),
      setValue: jest.fn(),
      dispose: jest.fn(),
    };
    return mockInstance;
  });

  Editor.mockImplementation(() => {
    return {
      createInstance: createInstanceMock,
    };
  });
  function createComponent(props = {}) {
    wrapper = shallowMount(EditorLite, {
      propsData: {
        value,
        fileName,
        fileGlobalId,
        ...props,
      },
    });
  }

  beforeEach(() => {
    createComponent();
  });

  afterEach(() => {
    wrapper.destroy();
  });

  const triggerChangeContent = (val) => {
    mockInstance.getValue.mockReturnValue(val);
    const [cb] = mockInstance.onDidChangeModelContent.mock.calls[0];

    cb();

    jest.runOnlyPendingTimers();
  };

  describe('rendering', () => {
    it('matches the snapshot', () => {
      expect(wrapper.element).toMatchSnapshot();
    });

    it('renders content', () => {
      expect(wrapper.text()).toContain(value);
    });
  });

  describe('functionality', () => {
    it('does not fail without content', () => {
      const spy = jest.spyOn(global.console, 'error');
      createComponent({ value: undefined });

      expect(spy).not.toHaveBeenCalled();
      expect(wrapper.find('[id^="editor-lite-"]').exists()).toBe(true);
    });

    it('initialises Editor Lite instance', () => {
      const el = wrapper.find({ ref: 'editor' }).element;
      expect(createInstanceMock).toHaveBeenCalledWith({
        el,
        blobPath: fileName,
        blobGlobalId: fileGlobalId,
        blobContent: value,
        extensions: null,
      });
    });

    it('reacts to the changes in fileName', () => {
      const newFileName = 'ipsum.txt';

      wrapper.setProps({
        fileName: newFileName,
      });

      return nextTick().then(() => {
        expect(mockInstance.updateModelLanguage).toHaveBeenCalledWith(newFileName);
      });
    });

    it('registers callback with editor onChangeContent', () => {
      expect(mockInstance.onDidChangeModelContent).toHaveBeenCalledWith(expect.any(Function));
    });

    it('emits input event when the blob content is changed', () => {
      expect(wrapper.emitted().input).toBeUndefined();

      triggerChangeContent(value);

      expect(wrapper.emitted().input).toEqual([[value]]);
    });

    it('emits EDITOR_READY_EVENT event when the Editor Lite is ready', async () => {
      const el = wrapper.find({ ref: 'editor' }).element;
      expect(wrapper.emitted()[EDITOR_READY_EVENT]).toBeUndefined();

      await el.dispatchEvent(new Event(EDITOR_READY_EVENT));

      expect(wrapper.emitted()[EDITOR_READY_EVENT]).toBeDefined();
    });

    it('component API `getEditor()` returns the editor instance', () => {
      expect(wrapper.vm.getEditor()).toBe(mockInstance);
    });

    describe('reaction to the value update', () => {
      it('reacts to the changes in the passed value', async () => {
        const newValue = 'New Value';

        wrapper.setProps({
          value: newValue,
        });

        await nextTick();
        expect(mockInstance.setValue).toHaveBeenCalledWith(newValue);
      });

      it("does not update value if the passed one is exactly the same as the editor's content", async () => {
        const newValue = `${value}`; // to make sure we're creating a new String with the same content and not just a reference

        wrapper.setProps({
          value: newValue,
        });

        await nextTick();
        expect(mockInstance.setValue).not.toHaveBeenCalled();
      });
    });
  });
});