diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-29 12:08:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-29 12:08:46 +0300 |
commit | f931527bc5120097db15737f8594a431bcad9116 (patch) | |
tree | 9f222683bb0ee7b88e3f7c0e02362a530e3c98a6 /spec/frontend/content_editor | |
parent | fd3d0d4c2c5ecac651fdeff92a1f2e4c927e05e0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/content_editor')
9 files changed, 123 insertions, 39 deletions
diff --git a/spec/frontend/content_editor/components/content_editor_spec.js b/spec/frontend/content_editor/components/content_editor_spec.js index 563e80e04c1..c52fc395487 100644 --- a/spec/frontend/content_editor/components/content_editor_spec.js +++ b/spec/frontend/content_editor/components/content_editor_spec.js @@ -38,10 +38,10 @@ describe('ContentEditor', () => { expect(editorContent.classes()).toContain('md'); }); - it('renders top toolbar component and attaches editor instance', () => { + it('renders top toolbar component', () => { createWrapper(editor); - expect(wrapper.findComponent(TopToolbar).props().contentEditor).toBe(editor); + expect(wrapper.findComponent(TopToolbar).exists()).toBe(true); }); it.each` diff --git a/spec/frontend/content_editor/components/editor_state_observer_spec.js b/spec/frontend/content_editor/components/editor_state_observer_spec.js new file mode 100644 index 00000000000..5e4bb348e1f --- /dev/null +++ b/spec/frontend/content_editor/components/editor_state_observer_spec.js @@ -0,0 +1,75 @@ +import { shallowMount } from '@vue/test-utils'; +import { each } from 'lodash'; +import EditorStateObserver, { + tiptapToComponentMap, +} from '~/content_editor/components/editor_state_observer.vue'; +import { createTestEditor } from '../test_utils'; + +describe('content_editor/components/editor_state_observer', () => { + let tiptapEditor; + let wrapper; + let onDocUpdateListener; + let onSelectionUpdateListener; + let onTransactionListener; + + const buildEditor = () => { + tiptapEditor = createTestEditor(); + jest.spyOn(tiptapEditor, 'on'); + }; + + const buildWrapper = () => { + wrapper = shallowMount(EditorStateObserver, { + provide: { tiptapEditor }, + listeners: { + docUpdate: onDocUpdateListener, + selectionUpdate: onSelectionUpdateListener, + transaction: onTransactionListener, + }, + }); + }; + + beforeEach(() => { + onDocUpdateListener = jest.fn(); + onSelectionUpdateListener = jest.fn(); + onTransactionListener = jest.fn(); + buildEditor(); + buildWrapper(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when editor content changes', () => { + it('emits update, selectionUpdate, and transaction events', () => { + const content = '<p>My paragraph</p>'; + + tiptapEditor.commands.insertContent(content); + + expect(onDocUpdateListener).toHaveBeenCalledWith( + expect.objectContaining({ editor: tiptapEditor }), + ); + expect(onSelectionUpdateListener).toHaveBeenCalledWith( + expect.objectContaining({ editor: tiptapEditor }), + ); + expect(onSelectionUpdateListener).toHaveBeenCalledWith( + expect.objectContaining({ editor: tiptapEditor }), + ); + }); + }); + + describe('when component is destroyed', () => { + it('removes onTiptapDocUpdate and onTiptapSelectionUpdate hooks', () => { + jest.spyOn(tiptapEditor, 'off'); + + wrapper.destroy(); + + each(tiptapToComponentMap, (_, tiptapEvent) => { + expect(tiptapEditor.off).toHaveBeenCalledWith( + tiptapEvent, + tiptapEditor.on.mock.calls.find(([eventName]) => eventName === tiptapEvent)[1], + ); + }); + }); + }); +}); diff --git a/spec/frontend/content_editor/components/toolbar_button_spec.js b/spec/frontend/content_editor/components/toolbar_button_spec.js index d848adcbff8..da32f308481 100644 --- a/spec/frontend/content_editor/components/toolbar_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_button_spec.js @@ -1,7 +1,8 @@ import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue'; import ToolbarButton from '~/content_editor/components/toolbar_button.vue'; -import { createTestEditor, mockChainedCommands } from '../test_utils'; +import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils'; describe('content_editor/components/toolbar_button', () => { let wrapper; @@ -20,9 +21,12 @@ describe('content_editor/components/toolbar_button', () => { wrapper = shallowMount(ToolbarButton, { stubs: { GlButton, + EditorStateObserver, }, - propsData: { + provide: { tiptapEditor, + }, + propsData: { contentType: CONTENT_TYPE, iconName: ICON_NAME, label: LABEL, @@ -51,14 +55,20 @@ describe('content_editor/components/toolbar_button', () => { ${{ isActive: true, isFocused: true }} | ${'button is active'} | ${true} ${{ isActive: false, isFocused: true }} | ${'button is not active'} | ${false} ${{ isActive: true, isFocused: false }} | ${'button is not active '} | ${false} - `('$outcomeDescription when when editor state is $editorState', ({ editorState, outcome }) => { - tiptapEditor.isActive.mockReturnValueOnce(editorState.isActive); - tiptapEditor.isFocused = editorState.isFocused; - buildWrapper(); + `( + '$outcomeDescription when when editor state is $editorState', + async ({ editorState, outcome }) => { + tiptapEditor.isActive.mockReturnValueOnce(editorState.isActive); + tiptapEditor.isFocused = editorState.isFocused; - expect(findButton().classes().includes('active')).toBe(outcome); - expect(tiptapEditor.isActive).toHaveBeenCalledWith(CONTENT_TYPE); - }); + buildWrapper(); + + await emitEditorEvent({ event: 'transaction', tiptapEditor }); + + expect(findButton().classes().includes('active')).toBe(outcome); + expect(tiptapEditor.isActive).toHaveBeenCalledWith(CONTENT_TYPE); + }, + ); describe('when button is clicked', () => { it('executes the content type command when executeCommand = true', async () => { diff --git a/spec/frontend/content_editor/components/toolbar_image_button_spec.js b/spec/frontend/content_editor/components/toolbar_image_button_spec.js index 6553d8fd357..bf744d5d385 100644 --- a/spec/frontend/content_editor/components/toolbar_image_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_image_button_spec.js @@ -10,7 +10,7 @@ describe('content_editor/components/toolbar_image_button', () => { const buildWrapper = () => { wrapper = mountExtended(ToolbarImageButton, { - propsData: { + provide: { tiptapEditor: editor, }, }); diff --git a/spec/frontend/content_editor/components/toolbar_link_button_spec.js b/spec/frontend/content_editor/components/toolbar_link_button_spec.js index 89a40c42590..405213d0487 100644 --- a/spec/frontend/content_editor/components/toolbar_link_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_link_button_spec.js @@ -3,7 +3,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper'; import ToolbarLinkButton from '~/content_editor/components/toolbar_link_button.vue'; import Link from '~/content_editor/extensions/link'; import { hasSelection } from '~/content_editor/services/utils'; -import { createTestEditor, mockChainedCommands } from '../test_utils'; +import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils'; jest.mock('~/content_editor/services/utils'); @@ -13,7 +13,7 @@ describe('content_editor/components/toolbar_link_button', () => { const buildWrapper = () => { wrapper = mountExtended(ToolbarLinkButton, { - propsData: { + provide: { tiptapEditor: editor, }, }); @@ -43,6 +43,8 @@ describe('content_editor/components/toolbar_link_button', () => { beforeEach(async () => { jest.spyOn(editor, 'isActive').mockReturnValueOnce(true); buildWrapper(); + + await emitEditorEvent({ event: 'transaction', tiptapEditor: editor }); }); it('sets dropdown as active when link extension is active', () => { @@ -88,7 +90,7 @@ describe('content_editor/components/toolbar_link_button', () => { href: '/username/my-project/uploads/abcdefgh133535/my-file.zip', }); - await editor.emit('selectionUpdate', { editor }); + await emitEditorEvent({ event: 'transaction', tiptapEditor: editor }); expect(findLinkURLInput().element.value).toEqual('uploads/my-file.zip'); }); @@ -98,7 +100,7 @@ describe('content_editor/components/toolbar_link_button', () => { href: 'https://gitlab.com', }); - await editor.emit('selectionUpdate', { editor }); + await emitEditorEvent({ event: 'transaction', tiptapEditor: editor }); expect(findLinkURLInput().element.value).toEqual('https://gitlab.com'); }); diff --git a/spec/frontend/content_editor/components/toolbar_table_button_spec.js b/spec/frontend/content_editor/components/toolbar_table_button_spec.js index fe5212cfe3c..336391f9b63 100644 --- a/spec/frontend/content_editor/components/toolbar_table_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_table_button_spec.js @@ -9,7 +9,7 @@ describe('content_editor/components/toolbar_table_button', () => { const buildWrapper = () => { wrapper = mountExtended(ToolbarTableButton, { - propsData: { + provide: { tiptapEditor: editor, }, }); diff --git a/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js index 8c98fe95bd4..65c1c8c8310 100644 --- a/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js +++ b/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js @@ -1,11 +1,12 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue'; import ToolbarTextStyleDropdown from '~/content_editor/components/toolbar_text_style_dropdown.vue'; import { TEXT_STYLE_DROPDOWN_ITEMS } from '~/content_editor/constants'; import Heading from '~/content_editor/extensions/heading'; -import { createTestEditor, mockChainedCommands } from '../test_utils'; +import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils'; -describe('content_editor/components/toolbar_headings_dropdown', () => { +describe('content_editor/components/toolbar_text_style_dropdown', () => { let wrapper; let tiptapEditor; @@ -22,9 +23,12 @@ describe('content_editor/components/toolbar_headings_dropdown', () => { stubs: { GlDropdown, GlDropdownItem, + EditorStateObserver, }, - propsData: { + provide: { tiptapEditor, + }, + propsData: { ...propsData, }, }); @@ -50,7 +54,7 @@ describe('content_editor/components/toolbar_headings_dropdown', () => { describe('when there is an active item ', () => { let activeTextStyle; - beforeEach(() => { + beforeEach(async () => { [, activeTextStyle] = TEXT_STYLE_DROPDOWN_ITEMS; tiptapEditor.isActive.mockImplementation( @@ -59,6 +63,7 @@ describe('content_editor/components/toolbar_headings_dropdown', () => { ); buildWrapper(); + await emitEditorEvent({ event: 'transaction', tiptapEditor }); }); it('displays the active text style label as the dropdown toggle text ', () => { @@ -79,9 +84,10 @@ describe('content_editor/components/toolbar_headings_dropdown', () => { }); describe('when there isn’t an active item', () => { - beforeEach(() => { + beforeEach(async () => { tiptapEditor.isActive.mockReturnValue(false); buildWrapper(); + await emitEditorEvent({ event: 'transaction', tiptapEditor }); }); it('sets dropdown as disabled', () => { diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js index 5411793cd5e..18faf1930e7 100644 --- a/spec/frontend/content_editor/components/top_toolbar_spec.js +++ b/spec/frontend/content_editor/components/top_toolbar_spec.js @@ -6,34 +6,19 @@ import { TOOLBAR_CONTROL_TRACKING_ACTION, CONTENT_EDITOR_TRACKING_LABEL, } from '~/content_editor/constants'; -import { createContentEditor } from '~/content_editor/services/create_content_editor'; describe('content_editor/components/top_toolbar', () => { let wrapper; - let contentEditor; let trackingSpy; - const buildEditor = () => { - contentEditor = createContentEditor({ renderMarkdown: () => true }); - }; const buildWrapper = () => { - wrapper = extendedWrapper( - shallowMount(TopToolbar, { - propsData: { - contentEditor, - }, - }), - ); + wrapper = extendedWrapper(shallowMount(TopToolbar)); }; beforeEach(() => { trackingSpy = mockTracking(undefined, null, jest.spyOn); }); - beforeEach(() => { - buildEditor(); - }); - afterEach(() => { wrapper.destroy(); }); @@ -60,7 +45,6 @@ describe('content_editor/components/top_toolbar', () => { it('renders the toolbar control with the provided properties', () => { expect(wrapper.findByTestId(testId).props()).toEqual({ ...controlProps, - tiptapEditor: contentEditor.tiptapEditor, }); }); diff --git a/spec/frontend/content_editor/test_utils.js b/spec/frontend/content_editor/test_utils.js index 090e1d92218..b5a2abc2389 100644 --- a/spec/frontend/content_editor/test_utils.js +++ b/spec/frontend/content_editor/test_utils.js @@ -4,6 +4,7 @@ import { Paragraph } from '@tiptap/extension-paragraph'; import { Text } from '@tiptap/extension-text'; import { Editor } from '@tiptap/vue-2'; import { builders, eq } from 'prosemirror-test-builder'; +import { nextTick } from 'vue'; export const createDocBuilder = ({ tiptapEditor, names = {} }) => { const docBuilders = builders(tiptapEditor.schema, { @@ -14,6 +15,12 @@ export const createDocBuilder = ({ tiptapEditor, names = {} }) => { return { eq, builders: docBuilders }; }; +export const emitEditorEvent = ({ tiptapEditor, event, params = {} }) => { + tiptapEditor.emit(event, { editor: tiptapEditor, ...params }); + + return nextTick(); +}; + /** * Creates an instance of the Tiptap Editor class * with a minimal configuration for testing purposes. |