import { BubbleMenu } from '@tiptap/vue-2'; import { GlButton, GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; import Vue from 'vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import CodeBlockBubbleMenu from '~/content_editor/components/code_block_bubble_menu.vue'; import eventHubFactory from '~/helpers/event_hub_factory'; import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight'; import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader'; import { createTestEditor, emitEditorEvent } from '../test_utils'; describe('content_editor/components/code_block_bubble_menu', () => { let wrapper; let tiptapEditor; let bubbleMenu; let eventHub; const buildEditor = () => { tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight] }); eventHub = eventHubFactory(); }; const buildWrapper = () => { wrapper = mountExtended(CodeBlockBubbleMenu, { provide: { tiptapEditor, eventHub, }, }); }; const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); const findDropdownItemsData = () => findDropdownItems().wrappers.map((x) => ({ text: x.text(), visible: x.isVisible(), checked: x.props('isChecked'), })); beforeEach(() => { buildEditor(); buildWrapper(); }); afterEach(() => { wrapper.destroy(); }); it('renders bubble menu component', async () => { tiptapEditor.commands.insertContent('
test
'); bubbleMenu = wrapper.findComponent(BubbleMenu); await emitEditorEvent({ event: 'transaction', tiptapEditor }); expect(bubbleMenu.props('editor')).toBe(tiptapEditor); expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base']); }); it('selects plaintext language by default', async () => { tiptapEditor.commands.insertContent('
test
'); bubbleMenu = wrapper.findComponent(BubbleMenu); await emitEditorEvent({ event: 'transaction', tiptapEditor }); expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Plain text'); }); it('selects appropriate language based on the code block', async () => { tiptapEditor.commands.insertContent('
var a = 2;
'); bubbleMenu = wrapper.findComponent(BubbleMenu); await emitEditorEvent({ event: 'transaction', tiptapEditor }); expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Javascript'); }); it("selects Custom (syntax) if the language doesn't exist in the list", async () => { tiptapEditor.commands.insertContent('
test
'); bubbleMenu = wrapper.findComponent(BubbleMenu); await emitEditorEvent({ event: 'transaction', tiptapEditor }); expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Custom (nomnoml)'); }); it('delete button deletes the code block', async () => { tiptapEditor.commands.insertContent('
var a = 2;
'); await wrapper.findComponent(GlButton).vm.$emit('click'); expect(tiptapEditor.getText()).toBe(''); }); describe('when opened and search is changed', () => { beforeEach(async () => { tiptapEditor.commands.insertContent('
var a = 2;
'); wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'js'); await Vue.nextTick(); }); it('shows dropdown items', () => { expect(findDropdownItemsData()).toEqual([ { text: 'Javascript', visible: true, checked: true }, { text: 'Java', visible: true, checked: false }, { text: 'Javascript', visible: false, checked: false }, { text: 'JSON', visible: true, checked: false }, ]); }); describe('when dropdown item is clicked', () => { beforeEach(async () => { jest.spyOn(codeBlockLanguageLoader, 'loadLanguages').mockResolvedValue(); findDropdownItems().at(1).vm.$emit('click'); await Vue.nextTick(); }); it('loads language', () => { expect(codeBlockLanguageLoader.loadLanguages).toHaveBeenCalledWith(['java']); }); it('sets code block', () => { expect(tiptapEditor.getJSON()).toMatchObject({ content: [ { type: 'codeBlock', attrs: { language: 'java', }, }, ], }); }); it('updates selected dropdown', () => { expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Java'); }); }); }); });