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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-16 15:09:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-16 15:09:26 +0300
commitb019dc959ec16b15fe42a680dbd729542ff61537 (patch)
treecde5c3d0582bd7542e1e195d83ad7a50079f4e49 /spec/frontend/content_editor/components
parentb7b44de429911864686599ef1643baf525bf75ec (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/content_editor/components')
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/code_block_spec.js48
-rw-r--r--spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js54
-rw-r--r--spec/frontend/content_editor/components/top_toolbar_spec.js29
-rw-r--r--spec/frontend/content_editor/components/wrappers/code_block_spec.js83
4 files changed, 197 insertions, 17 deletions
diff --git a/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js b/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js
index f19bd02443f..646d068e795 100644
--- a/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js
+++ b/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js
@@ -12,6 +12,7 @@ import { stubComponent } from 'helpers/stub_component';
import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block.vue';
import eventHubFactory from '~/helpers/event_hub_factory';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
+import Diagram from '~/content_editor/extensions/diagram';
import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
import { createTestEditor, emitEditorEvent } from '../../test_utils';
@@ -20,11 +21,13 @@ const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jes
describe('content_editor/components/bubble_menus/code_block', () => {
let wrapper;
let tiptapEditor;
+ let contentEditor;
let bubbleMenu;
let eventHub;
const buildEditor = () => {
- tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight] });
+ tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight, Diagram] });
+ contentEditor = { renderDiagram: jest.fn() };
eventHub = eventHubFactory();
};
@@ -32,6 +35,7 @@ describe('content_editor/components/bubble_menus/code_block', () => {
wrapper = mountExtended(CodeBlockBubbleMenu, {
provide: {
tiptapEditor,
+ contentEditor,
eventHub,
},
stubs: {
@@ -85,6 +89,15 @@ describe('content_editor/components/bubble_menus/code_block', () => {
expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Javascript');
});
+ it('selects diagram sytnax for mermaid', async () => {
+ tiptapEditor.commands.insertContent('<pre lang="mermaid">test</pre>');
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Diagram (mermaid)');
+ });
+
it("selects Custom (syntax) if the language doesn't exist in the list", async () => {
tiptapEditor.commands.insertContent('<pre lang="nomnoml">test</pre>');
bubbleMenu = wrapper.findComponent(BubbleMenu);
@@ -116,6 +129,39 @@ describe('content_editor/components/bubble_menus/code_block', () => {
});
});
+ describe('preview button', () => {
+ it('does not appear for a regular code block', async () => {
+ tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
+
+ expect(wrapper.findByTestId('preview-diagram').exists()).toBe(false);
+ });
+
+ it.each`
+ diagramType | diagramCode
+ ${'mermaid'} | ${'<pre lang="mermaid">graph TD;\n A-->B;</pre>'}
+ ${'nomnoml'} | ${'<img data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,WzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybl0=">'}
+ `('toggles preview for a $diagramType diagram', async ({ diagramType, diagramCode }) => {
+ tiptapEditor.commands.insertContent(diagramCode);
+
+ await nextTick();
+ await wrapper.findByTestId('preview-diagram').vm.$emit('click');
+
+ expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
+ isDiagram: true,
+ language: diagramType,
+ showPreview: false,
+ });
+
+ await wrapper.findByTestId('preview-diagram').vm.$emit('click');
+
+ expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
+ isDiagram: true,
+ language: diagramType,
+ showPreview: true,
+ });
+ });
+ });
+
describe('when opened and search is changed', () => {
beforeEach(async () => {
tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
diff --git a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
new file mode 100644
index 00000000000..0334a18c9a1
--- /dev/null
+++ b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
@@ -0,0 +1,54 @@
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdown.vue';
+import Diagram from '~/content_editor/extensions/diagram';
+import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
+import { createTestEditor, mockChainedCommands } from '../test_utils';
+
+describe('content_editor/components/toolbar_more_dropdown', () => {
+ let wrapper;
+ let tiptapEditor;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor({
+ extensions: [Diagram, HorizontalRule],
+ });
+ };
+
+ const buildWrapper = (propsData = {}) => {
+ wrapper = mountExtended(ToolbarMoreDropdown, {
+ provide: {
+ tiptapEditor,
+ },
+ propsData,
+ });
+ };
+
+ beforeEach(() => {
+ buildEditor();
+ buildWrapper();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe.each`
+ label | contentType | data
+ ${'Mermaid diagram'} | ${'diagram'} | ${{ language: 'mermaid' }}
+ ${'PlantUML diagram'} | ${'diagram'} | ${{ language: 'plantuml' }}
+ ${'Horizontal rule'} | ${'horizontalRule'} | ${undefined}
+ `('when option $label is clicked', ({ label, contentType, data }) => {
+ it(`inserts a ${contentType}`, async () => {
+ const commands = mockChainedCommands(tiptapEditor, ['setNode', 'focus', 'run']);
+
+ const btn = wrapper.findByRole('menuitem', { name: label });
+ await btn.trigger('click');
+
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.setNode).toHaveBeenCalledWith(contentType, data);
+ expect(commands.run).toHaveBeenCalled();
+
+ expect(wrapper.emitted('execute')).toEqual([[{ contentType }]]);
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js
index ec58877470c..d98a9a52aff 100644
--- a/spec/frontend/content_editor/components/top_toolbar_spec.js
+++ b/spec/frontend/content_editor/components/top_toolbar_spec.js
@@ -23,20 +23,21 @@ describe('content_editor/components/top_toolbar', () => {
});
describe.each`
- testId | controlProps
- ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
- ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
- ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike' }}
- ${'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' }}
- ${'details'} | ${{ contentType: 'details', iconName: 'details-block', label: 'Add a collapsible section', editorCommand: 'toggleDetails' }}
- ${'horizontal-rule'} | ${{ contentType: 'horizontalRule', iconName: 'dash', label: 'Add a horizontal rule', editorCommand: 'setHorizontalRule' }}
- ${'code-block'} | ${{ contentType: 'codeBlock', iconName: 'doc-code', label: 'Insert a code block', editorCommand: 'toggleCodeBlock' }}
- ${'text-styles'} | ${{}}
- ${'link'} | ${{}}
- ${'image'} | ${{}}
+ testId | controlProps
+ ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
+ ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
+ ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike' }}
+ ${'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' }}
+ ${'details'} | ${{ contentType: 'details', iconName: 'details-block', label: 'Add a collapsible section', editorCommand: 'toggleDetails' }}
+ ${'code-block'} | ${{ contentType: 'codeBlock', iconName: 'doc-code', label: 'Insert a code block', editorCommand: 'toggleCodeBlock' }}
+ ${'text-styles'} | ${{}}
+ ${'link'} | ${{}}
+ ${'image'} | ${{}}
+ ${'table'} | ${{}}
+ ${'more'} | ${{}}
`('given a $testId toolbar control', ({ testId, controlProps }) => {
beforeEach(() => {
buildWrapper();
diff --git a/spec/frontend/content_editor/components/wrappers/code_block_spec.js b/spec/frontend/content_editor/components/wrappers/code_block_spec.js
index 2e59cd9714a..17a365e12bb 100644
--- a/spec/frontend/content_editor/components/wrappers/code_block_spec.js
+++ b/spec/frontend/content_editor/components/wrappers/code_block_spec.js
@@ -1,8 +1,14 @@
import { nextTick } from 'vue';
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2';
-import { shallowMount } from '@vue/test-utils';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import eventHubFactory from '~/helpers/event_hub_factory';
+import SandboxedMermaid from '~/behaviors/components/sandboxed_mermaid.vue';
+import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
+import Diagram from '~/content_editor/extensions/diagram';
import CodeBlockWrapper from '~/content_editor/components/wrappers/code_block.vue';
import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
+import { emitEditorEvent, createTestEditor } from '../../test_utils';
jest.mock('~/content_editor/services/code_block_language_loader');
@@ -10,21 +16,42 @@ describe('content/components/wrappers/code_block', () => {
const language = 'yaml';
let wrapper;
let updateAttributesFn;
+ let tiptapEditor;
+ let contentEditor;
+ let eventHub;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight, Diagram] });
+ contentEditor = { renderDiagram: jest.fn().mockResolvedValue('url/to/some/diagram') };
+ eventHub = eventHubFactory();
+ };
const createWrapper = async (nodeAttrs = { language }) => {
updateAttributesFn = jest.fn();
- wrapper = shallowMount(CodeBlockWrapper, {
+ wrapper = mountExtended(CodeBlockWrapper, {
propsData: {
+ editor: tiptapEditor,
node: {
attrs: nodeAttrs,
},
updateAttributes: updateAttributesFn,
},
+ stubs: {
+ NodeViewContent: stubComponent(NodeViewContent),
+ NodeViewWrapper: stubComponent(NodeViewWrapper),
+ },
+ provide: {
+ contentEditor,
+ tiptapEditor,
+ eventHub,
+ },
});
};
beforeEach(() => {
+ buildEditor();
+
codeBlockLanguageLoader.findOrCreateLanguageBySyntax.mockReturnValue({ syntax: language });
});
@@ -68,4 +95,56 @@ describe('content/components/wrappers/code_block', () => {
expect(updateAttributesFn).toHaveBeenCalledWith({ language });
});
+
+ describe('diagrams', () => {
+ beforeEach(() => {
+ jest.spyOn(tiptapEditor, 'isActive').mockReturnValue(true);
+ });
+
+ it('does not render a preview if showPreview: false', async () => {
+ createWrapper({ language: 'plantuml', isDiagram: true, showPreview: false });
+
+ expect(wrapper.find({ ref: 'diagramContainer' }).exists()).toBe(false);
+ });
+
+ it('does not update preview when diagram is not active', async () => {
+ createWrapper({ language: 'plantuml', isDiagram: true, showPreview: true });
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+ await nextTick();
+
+ expect(wrapper.find('img').attributes('src')).toBe('url/to/some/diagram');
+
+ jest.spyOn(tiptapEditor, 'isActive').mockReturnValue(false);
+
+ const alternateUrl = 'url/to/another/diagram';
+
+ contentEditor.renderDiagram.mockResolvedValue(alternateUrl);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+ await nextTick();
+
+ expect(wrapper.find('img').attributes('src')).toBe('url/to/some/diagram');
+ });
+
+ it('renders an image with preview for a plantuml/kroki diagram', async () => {
+ createWrapper({ language: 'plantuml', isDiagram: true, showPreview: true });
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+ await nextTick();
+
+ expect(wrapper.find('img').attributes('src')).toBe('url/to/some/diagram');
+ expect(wrapper.find(SandboxedMermaid).exists()).toBe(false);
+ });
+
+ it('renders an iframe with preview for a mermaid diagram', async () => {
+ createWrapper({ language: 'mermaid', isDiagram: true, showPreview: true });
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+ await nextTick();
+
+ expect(wrapper.find(SandboxedMermaid).props('source')).toBe('');
+ expect(wrapper.find('img').exists()).toBe(false);
+ });
+ });
});