summaryrefslogtreecommitdiff
path: root/spec/frontend/content_editor/extensions/image_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/content_editor/extensions/image_spec.js')
-rw-r--r--spec/frontend/content_editor/extensions/image_spec.js193
1 files changed, 193 insertions, 0 deletions
diff --git a/spec/frontend/content_editor/extensions/image_spec.js b/spec/frontend/content_editor/extensions/image_spec.js
new file mode 100644
index 00000000000..922966b813a
--- /dev/null
+++ b/spec/frontend/content_editor/extensions/image_spec.js
@@ -0,0 +1,193 @@
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import { once } from 'lodash';
+import waitForPromises from 'helpers/wait_for_promises';
+import * as Image from '~/content_editor/extensions/image';
+import httpStatus from '~/lib/utils/http_status';
+import { loadMarkdownApiResult } from '../markdown_processing_examples';
+import { createTestEditor, createDocBuilder } from '../test_utils';
+
+describe('content_editor/extensions/image', () => {
+ let tiptapEditor;
+ let eq;
+ let doc;
+ let p;
+ let image;
+ let renderMarkdown;
+ let mock;
+ const uploadsPath = '/uploads/';
+ const validFile = new File(['foo'], 'foo.png', { type: 'image/png' });
+ const invalidFile = new File(['foo'], 'bar.html', { type: 'text/html' });
+
+ beforeEach(() => {
+ renderMarkdown = jest
+ .fn()
+ .mockResolvedValue(loadMarkdownApiResult('project_wiki_attachment_image').body);
+
+ const { tiptapExtension } = Image.configure({ renderMarkdown, uploadsPath });
+
+ tiptapEditor = createTestEditor({ extensions: [tiptapExtension] });
+
+ ({
+ builders: { doc, p, image },
+ eq,
+ } = createDocBuilder({
+ tiptapEditor,
+ names: { image: { nodeType: tiptapExtension.name } },
+ }));
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.reset();
+ });
+
+ it.each`
+ file | valid | description
+ ${validFile} | ${true} | ${'handles paste event when mime type is valid'}
+ ${invalidFile} | ${false} | ${'does not handle paste event when mime type is invalid'}
+ `('$description', ({ file, valid }) => {
+ const pasteEvent = Object.assign(new Event('paste'), {
+ clipboardData: {
+ files: [file],
+ },
+ });
+ let handled;
+
+ tiptapEditor.view.someProp('handlePaste', (eventHandler) => {
+ handled = eventHandler(tiptapEditor.view, pasteEvent);
+ });
+
+ expect(handled).toBe(valid);
+ });
+
+ it.each`
+ file | valid | description
+ ${validFile} | ${true} | ${'handles drop event when mime type is valid'}
+ ${invalidFile} | ${false} | ${'does not handle drop event when mime type is invalid'}
+ `('$description', ({ file, valid }) => {
+ const dropEvent = Object.assign(new Event('drop'), {
+ dataTransfer: {
+ files: [file],
+ },
+ });
+ let handled;
+
+ tiptapEditor.view.someProp('handleDrop', (eventHandler) => {
+ handled = eventHandler(tiptapEditor.view, dropEvent);
+ });
+
+ expect(handled).toBe(valid);
+ });
+
+ it('handles paste event when mime type is correct', () => {
+ const pasteEvent = Object.assign(new Event('paste'), {
+ clipboardData: {
+ files: [new File(['foo'], 'foo.png', { type: 'image/png' })],
+ },
+ });
+ const handled = tiptapEditor.view.someProp('handlePaste', (eventHandler) => {
+ return eventHandler(tiptapEditor.view, pasteEvent);
+ });
+
+ expect(handled).toBe(true);
+ });
+
+ describe('uploadImage command', () => {
+ describe('when file has correct mime type', () => {
+ let initialDoc;
+ const base64EncodedFile = 'data:image/png;base64,Zm9v';
+
+ beforeEach(() => {
+ initialDoc = doc(p(''));
+ tiptapEditor.commands.setContent(initialDoc.toJSON());
+ });
+
+ describe('when uploading image succeeds', () => {
+ const successResponse = {
+ link: {
+ markdown: '[image](/uploads/25265/image.png)',
+ },
+ };
+
+ beforeEach(() => {
+ mock.onPost().reply(httpStatus.OK, successResponse);
+ });
+
+ it('inserts an image with src set to the encoded image file and uploading true', (done) => {
+ const expectedDoc = doc(p(image({ uploading: true, src: base64EncodedFile })));
+
+ tiptapEditor.on(
+ 'update',
+ once(() => {
+ expect(eq(tiptapEditor.state.doc, expectedDoc)).toBe(true);
+ done();
+ }),
+ );
+
+ tiptapEditor.commands.uploadImage({ file: validFile });
+ });
+
+ it('updates the inserted image with canonicalSrc when upload is successful', async () => {
+ const expectedDoc = doc(
+ p(
+ image({
+ canonicalSrc: 'test-file.png',
+ src: base64EncodedFile,
+ alt: 'test file',
+ uploading: false,
+ }),
+ ),
+ );
+
+ tiptapEditor.commands.uploadImage({ file: validFile });
+
+ await waitForPromises();
+
+ expect(eq(tiptapEditor.state.doc, expectedDoc)).toBe(true);
+ });
+ });
+
+ describe('when uploading image request fails', () => {
+ beforeEach(() => {
+ mock.onPost().reply(httpStatus.INTERNAL_SERVER_ERROR);
+ });
+
+ it('resets the doc to orginal state', async () => {
+ const expectedDoc = doc(p(''));
+
+ tiptapEditor.commands.uploadImage({ file: validFile });
+
+ await waitForPromises();
+
+ expect(eq(tiptapEditor.state.doc, expectedDoc)).toBe(true);
+ });
+
+ it('emits an error event that includes an error message', (done) => {
+ tiptapEditor.commands.uploadImage({ file: validFile });
+
+ tiptapEditor.on('error', (message) => {
+ expect(message).toBe('An error occurred while uploading the image. Please try again.');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when file does not have correct mime type', () => {
+ let initialDoc;
+
+ beforeEach(() => {
+ initialDoc = doc(p(''));
+ tiptapEditor.commands.setContent(initialDoc.toJSON());
+ });
+
+ it('does not start the upload image process', () => {
+ tiptapEditor.commands.uploadImage({ file: invalidFile });
+
+ expect(eq(tiptapEditor.state.doc, initialDoc)).toBe(true);
+ });
+ });
+ });
+});