Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/content_editor/components')
-rw-r--r--spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap36
-rw-r--r--spec/frontend/content_editor/components/content_editor_spec.js9
-rw-r--r--spec/frontend/content_editor/components/toolbar_link_button_spec.js151
-rw-r--r--spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js131
-rw-r--r--spec/frontend/content_editor/components/top_toolbar_spec.js19
5 files changed, 335 insertions, 11 deletions
diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
new file mode 100644
index 00000000000..e56c37b0dc9
--- /dev/null
+++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`content_editor/components/toolbar_link_button renders dropdown component 1`] = `
+"<div class=\\"dropdown b-dropdown gl-new-dropdown btn-group\\" aria-label=\\"Insert link\\" title=\\"Insert link\\">
+ <!----><button aria-haspopup=\\"true\\" aria-expanded=\\"false\\" type=\\"button\\" class=\\"btn dropdown-toggle btn-default btn-sm gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only\\">
+ <!----> <svg data-testid=\\"link-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"dropdown-icon gl-icon s16\\">
+ <use href=\\"#link\\"></use>
+ </svg> <span class=\\"gl-new-dropdown-button-text\\"></span> <svg data-testid=\\"chevron-down-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"gl-button-icon dropdown-chevron gl-icon s16\\">
+ <use href=\\"#chevron-down\\"></use>
+ </svg></button>
+ <ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\">
+ <div class=\\"gl-new-dropdown-inner\\">
+ <!---->
+ <div class=\\"gl-new-dropdown-contents\\">
+ <li role=\\"presentation\\" class=\\"gl-px-3!\\">
+ <form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\">
+ <div placeholder=\\"Link URL\\">
+ <div role=\\"group\\" class=\\"input-group\\">
+ <!---->
+ <!----> <input type=\\"text\\" placeholder=\\"Link URL\\" class=\\"gl-form-input form-control\\">
+ <div class=\\"input-group-append\\"><button type=\\"button\\" class=\\"btn btn-confirm btn-md gl-button\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">Apply</span></button></div>
+ <!---->
+ </div>
+ </div>
+ </form>
+ </li>
+ <!---->
+ <!---->
+ </div>
+ <!---->
+ </div>
+ </ul>
+</div>"
+`;
diff --git a/spec/frontend/content_editor/components/content_editor_spec.js b/spec/frontend/content_editor/components/content_editor_spec.js
index e3741032bf4..59c4190ad3a 100644
--- a/spec/frontend/content_editor/components/content_editor_spec.js
+++ b/spec/frontend/content_editor/components/content_editor_spec.js
@@ -27,7 +27,10 @@ describe('ContentEditor', () => {
it('renders editor content component and attaches editor instance', () => {
createWrapper(editor);
- expect(wrapper.findComponent(EditorContent).props().editor).toBe(editor.tiptapEditor);
+ const editorContent = wrapper.findComponent(EditorContent);
+
+ expect(editorContent.props().editor).toBe(editor.tiptapEditor);
+ expect(editorContent.classes()).toContain('md');
});
it('renders top toolbar component and attaches editor instance', () => {
@@ -38,8 +41,8 @@ describe('ContentEditor', () => {
it.each`
isFocused | classes
- ${true} | ${['md', 'md-area', 'is-focused']}
- ${false} | ${['md', 'md-area']}
+ ${true} | ${['md-area', 'is-focused']}
+ ${false} | ${['md-area']}
`(
'has $classes class selectors when tiptapEditor.isFocused = $isFocused',
({ isFocused, classes }) => {
diff --git a/spec/frontend/content_editor/components/toolbar_link_button_spec.js b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
new file mode 100644
index 00000000000..812e769c891
--- /dev/null
+++ b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
@@ -0,0 +1,151 @@
+import { GlDropdown, GlDropdownDivider, GlFormInputGroup, GlButton } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import ToolbarLinkButton from '~/content_editor/components/toolbar_link_button.vue';
+import { tiptapExtension as Link } from '~/content_editor/extensions/link';
+import { hasSelection } from '~/content_editor/services/utils';
+import { createTestEditor, mockChainedCommands } from '../test_utils';
+
+jest.mock('~/content_editor/services/utils');
+
+describe('content_editor/components/toolbar_link_button', () => {
+ let wrapper;
+ let editor;
+
+ const buildWrapper = () => {
+ wrapper = mountExtended(ToolbarLinkButton, {
+ propsData: {
+ tiptapEditor: editor,
+ },
+ stubs: {
+ GlFormInputGroup,
+ },
+ });
+ };
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
+ const findLinkURLInput = () => wrapper.findComponent(GlFormInputGroup).find('input[type="text"]');
+ const findApplyLinkButton = () => wrapper.findComponent(GlButton);
+ const findRemoveLinkButton = () => wrapper.findByText('Remove link');
+
+ beforeEach(() => {
+ editor = createTestEditor({
+ extensions: [Link],
+ });
+ });
+
+ afterEach(() => {
+ editor.destroy();
+ wrapper.destroy();
+ });
+
+ it('renders dropdown component', () => {
+ buildWrapper();
+
+ expect(findDropdown().html()).toMatchSnapshot();
+ });
+
+ describe('when there is an active link', () => {
+ beforeEach(() => {
+ jest.spyOn(editor, 'isActive');
+ editor.isActive.mockReturnValueOnce(true);
+ buildWrapper();
+ });
+
+ it('sets dropdown as active when link extension is active', () => {
+ expect(findDropdown().props('toggleClass')).toEqual({ active: true });
+ });
+
+ it('displays a remove link dropdown option', () => {
+ expect(findDropdownDivider().exists()).toBe(true);
+ expect(wrapper.findByText('Remove link').exists()).toBe(true);
+ });
+
+ it('executes removeLink command when the remove link option is clicked', async () => {
+ const commands = mockChainedCommands(editor, ['focus', 'unsetLink', 'run']);
+
+ await findRemoveLinkButton().trigger('click');
+
+ expect(commands.unsetLink).toHaveBeenCalled();
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.run).toHaveBeenCalled();
+ });
+
+ it('updates the link with a new link when "Apply" button is clicked', async () => {
+ const commands = mockChainedCommands(editor, ['focus', 'unsetLink', 'setLink', 'run']);
+
+ await findLinkURLInput().setValue('https://example');
+ await findApplyLinkButton().trigger('click');
+
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.unsetLink).toHaveBeenCalled();
+ expect(commands.setLink).toHaveBeenCalledWith({ href: 'https://example' });
+ expect(commands.run).toHaveBeenCalled();
+ });
+ });
+
+ describe('when there is not an active link', () => {
+ beforeEach(() => {
+ jest.spyOn(editor, 'isActive');
+ editor.isActive.mockReturnValueOnce(false);
+ buildWrapper();
+ });
+
+ it('does not set dropdown as active', () => {
+ expect(findDropdown().props('toggleClass')).toEqual({ active: false });
+ });
+
+ it('does not display a remove link dropdown option', () => {
+ expect(findDropdownDivider().exists()).toBe(false);
+ expect(wrapper.findByText('Remove link').exists()).toBe(false);
+ });
+
+ it('sets the link to the value in the URL input when "Apply" button is clicked', async () => {
+ const commands = mockChainedCommands(editor, ['focus', 'unsetLink', 'setLink', 'run']);
+
+ await findLinkURLInput().setValue('https://example');
+ await findApplyLinkButton().trigger('click');
+
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.setLink).toHaveBeenCalledWith({ href: 'https://example' });
+ expect(commands.run).toHaveBeenCalled();
+ });
+ });
+
+ describe('when the user displays the dropdown', () => {
+ let commands;
+
+ beforeEach(() => {
+ commands = mockChainedCommands(editor, ['focus', 'extendMarkRange', 'run']);
+ });
+
+ describe('given the user has not selected text', () => {
+ beforeEach(() => {
+ hasSelection.mockReturnValueOnce(false);
+ });
+
+ it('the editor selection is extended to the current mark extent', () => {
+ buildWrapper();
+
+ findDropdown().vm.$emit('show');
+ expect(commands.extendMarkRange).toHaveBeenCalledWith(Link.name);
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.run).toHaveBeenCalled();
+ });
+ });
+
+ describe('given the user has selected text', () => {
+ beforeEach(() => {
+ hasSelection.mockReturnValueOnce(true);
+ });
+
+ it('the editor does not modify the current selection', () => {
+ buildWrapper();
+
+ findDropdown().vm.$emit('show');
+ expect(commands.extendMarkRange).not.toHaveBeenCalled();
+ expect(commands.focus).not.toHaveBeenCalled();
+ expect(commands.run).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
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
new file mode 100644
index 00000000000..8c54f6bb8bb
--- /dev/null
+++ b/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js
@@ -0,0 +1,131 @@
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ToolbarTextStyleDropdown from '~/content_editor/components/toolbar_text_style_dropdown.vue';
+import { TEXT_STYLE_DROPDOWN_ITEMS } from '~/content_editor/constants';
+import { createTestContentEditorExtension, createTestEditor } from '../test_utils';
+
+describe('content_editor/components/toolbar_headings_dropdown', () => {
+ let wrapper;
+ let tiptapEditor;
+ let commandMocks;
+
+ const buildEditor = () => {
+ const testExtension = createTestContentEditorExtension({
+ commands: TEXT_STYLE_DROPDOWN_ITEMS.map((item) => item.editorCommand),
+ });
+
+ commandMocks = testExtension.commandMocks;
+ tiptapEditor = createTestEditor({
+ extensions: [testExtension.tiptapExtension],
+ });
+
+ jest.spyOn(tiptapEditor, 'isActive');
+ };
+
+ const buildWrapper = (propsData = {}) => {
+ wrapper = shallowMountExtended(ToolbarTextStyleDropdown, {
+ stubs: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ propsData: {
+ tiptapEditor,
+ ...propsData,
+ },
+ });
+ };
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+
+ beforeEach(() => {
+ buildEditor();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders all text styles as dropdown items', () => {
+ buildWrapper();
+
+ TEXT_STYLE_DROPDOWN_ITEMS.forEach((textStyle) => {
+ expect(wrapper.findByText(textStyle.label).exists()).toBe(true);
+ });
+ });
+
+ describe('when there is an active item ', () => {
+ let activeTextStyle;
+
+ beforeEach(() => {
+ [, activeTextStyle] = TEXT_STYLE_DROPDOWN_ITEMS;
+
+ tiptapEditor.isActive.mockImplementation(
+ (contentType, params) =>
+ activeTextStyle.contentType === contentType && activeTextStyle.commandParams === params,
+ );
+
+ buildWrapper();
+ });
+
+ it('displays the active text style label as the dropdown toggle text ', () => {
+ expect(findDropdown().props().text).toBe(activeTextStyle.label);
+ });
+
+ it('sets dropdown as enabled', () => {
+ expect(findDropdown().props().disabled).toBe(false);
+ });
+
+ it('sets active item as active', () => {
+ const activeItem = wrapper
+ .findAllComponents(GlDropdownItem)
+ .filter((item) => item.text() === activeTextStyle.label)
+ .at(0);
+ expect(activeItem.props().isChecked).toBe(true);
+ });
+ });
+
+ describe('when there isn’t an active item', () => {
+ beforeEach(() => {
+ tiptapEditor.isActive.mockReturnValue(false);
+ buildWrapper();
+ });
+
+ it('sets dropdown as disabled', () => {
+ expect(findDropdown().props().disabled).toBe(true);
+ });
+
+ it('sets dropdown toggle text to Text style', () => {
+ expect(findDropdown().props().text).toBe('Text style');
+ });
+ });
+
+ describe('when a text style is selected', () => {
+ it('executes the tiptap command related to that text style', () => {
+ buildWrapper();
+
+ TEXT_STYLE_DROPDOWN_ITEMS.forEach((textStyle, index) => {
+ const { editorCommand, commandParams } = textStyle;
+
+ wrapper.findAllComponents(GlDropdownItem).at(index).vm.$emit('click');
+ expect(commandMocks[editorCommand]).toHaveBeenCalledWith(commandParams || {});
+ });
+ });
+
+ it('emits execute event with contentType and value params that indicates the heading level', () => {
+ TEXT_STYLE_DROPDOWN_ITEMS.forEach((textStyle, index) => {
+ buildWrapper();
+ const { contentType, commandParams } = textStyle;
+
+ wrapper.findAllComponents(GlDropdownItem).at(index).vm.$emit('click');
+ expect(wrapper.emitted('execute')).toEqual([
+ [
+ {
+ contentType,
+ value: commandParams?.level,
+ },
+ ],
+ ]);
+ wrapper.destroy();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js
index 8f47be3f489..0a1405a1774 100644
--- a/spec/frontend/content_editor/components/top_toolbar_spec.js
+++ b/spec/frontend/content_editor/components/top_toolbar_spec.js
@@ -39,32 +39,35 @@ describe('content_editor/components/top_toolbar', () => {
});
describe.each`
- testId | buttonProps
+ testId | controlProps
${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
${'blockquote'} | ${{ contentType: 'blockquote', iconName: 'quote', label: 'Insert a quote', editorCommand: 'toggleBlockquote' }}
${'bullet-list'} | ${{ contentType: 'bulletList', iconName: 'list-bulleted', label: 'Add a bullet list', editorCommand: 'toggleBulletList' }}
${'ordered-list'} | ${{ contentType: 'orderedList', iconName: 'list-numbered', label: 'Add a numbered list', editorCommand: 'toggleOrderedList' }}
- `('given a $testId toolbar control', ({ testId, buttonProps }) => {
+ ${'code-block'} | ${{ contentType: 'codeBlock', iconName: 'doc-code', label: 'Insert a code block', editorCommand: 'toggleCodeBlock' }}
+ ${'text-styles'} | ${{}}
+ ${'link'} | ${{}}
+ `('given a $testId toolbar control', ({ testId, controlProps }) => {
beforeEach(() => {
buildWrapper();
});
it('renders the toolbar control with the provided properties', () => {
expect(wrapper.findByTestId(testId).props()).toEqual({
- ...buttonProps,
+ ...controlProps,
tiptapEditor: contentEditor.tiptapEditor,
});
});
it.each`
- control | eventData
- ${'bold'} | ${{ contentType: 'bold' }}
- ${'blockquote'} | ${{ contentType: 'blockquote', value: 1 }}
- `('tracks the execution of toolbar controls', ({ control, eventData }) => {
+ eventData
+ ${{ contentType: 'bold' }}
+ ${{ contentType: 'blockquote', value: 1 }}
+ `('tracks the execution of toolbar controls', ({ eventData }) => {
const { contentType, value } = eventData;
- wrapper.findByTestId(control).vm.$emit('execute', eventData);
+ wrapper.findByTestId(testId).vm.$emit('execute', eventData);
expect(trackingSpy).toHaveBeenCalledWith(undefined, TOOLBAR_CONTROL_TRACKING_ACTION, {
label: CONTENT_EDITOR_TRACKING_LABEL,