debian-mirror-gitlab/spec/frontend/content_editor/extensions/attachment_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

274 lines
9 KiB
JavaScript
Raw Normal View History

2021-10-27 15:23:28 +05:30
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
2022-06-21 17:19:12 +05:30
import waitForPromises from 'helpers/wait_for_promises';
2021-10-27 15:23:28 +05:30
import Attachment from '~/content_editor/extensions/attachment';
import Image from '~/content_editor/extensions/image';
2022-06-21 17:19:12 +05:30
import Audio from '~/content_editor/extensions/audio';
import Video from '~/content_editor/extensions/video';
2021-10-27 15:23:28 +05:30
import Link from '~/content_editor/extensions/link';
import Loading from '~/content_editor/extensions/loading';
2022-05-07 20:08:51 +05:30
import { VARIANT_DANGER } from '~/flash';
2023-03-17 16:20:25 +05:30
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
2022-05-07 20:08:51 +05:30
import eventHubFactory from '~/helpers/event_hub_factory';
2021-10-27 15:23:28 +05:30
import { createTestEditor, createDocBuilder } from '../test_utils';
2022-07-16 23:28:13 +05:30
import {
PROJECT_WIKI_ATTACHMENT_IMAGE_HTML,
PROJECT_WIKI_ATTACHMENT_AUDIO_HTML,
PROJECT_WIKI_ATTACHMENT_VIDEO_HTML,
PROJECT_WIKI_ATTACHMENT_LINK_HTML,
} from '../test_constants';
2021-11-11 11:23:49 +05:30
2021-10-27 15:23:28 +05:30
describe('content_editor/extensions/attachment', () => {
let tiptapEditor;
let doc;
let p;
let image;
2022-06-21 17:19:12 +05:30
let audio;
let video;
2021-10-27 15:23:28 +05:30
let loading;
let link;
let renderMarkdown;
let mock;
2022-05-07 20:08:51 +05:30
let eventHub;
2021-10-27 15:23:28 +05:30
const uploadsPath = '/uploads/';
const imageFile = new File(['foo'], 'test-file.png', { type: 'image/png' });
2022-06-21 17:19:12 +05:30
const audioFile = new File(['foo'], 'test-file.mp3', { type: 'audio/mpeg' });
const videoFile = new File(['foo'], 'test-file.mp4', { type: 'video/mp4' });
2021-10-27 15:23:28 +05:30
const attachmentFile = new File(['foo'], 'test-file.zip', { type: 'application/zip' });
2021-11-11 11:23:49 +05:30
const expectDocumentAfterTransaction = ({ number, expectedDoc, action }) => {
return new Promise((resolve) => {
let counter = 1;
2022-06-21 17:19:12 +05:30
const handleTransaction = async () => {
2021-11-11 11:23:49 +05:30
if (counter === number) {
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
tiptapEditor.off('update', handleTransaction);
2022-06-21 17:19:12 +05:30
await waitForPromises();
2021-11-11 11:23:49 +05:30
resolve();
}
counter += 1;
};
tiptapEditor.on('update', handleTransaction);
action();
});
};
2021-10-27 15:23:28 +05:30
beforeEach(() => {
renderMarkdown = jest.fn();
2022-05-07 20:08:51 +05:30
eventHub = eventHubFactory();
2021-10-27 15:23:28 +05:30
tiptapEditor = createTestEditor({
2022-05-07 20:08:51 +05:30
extensions: [
Loading,
Link,
Image,
2022-06-21 17:19:12 +05:30
Audio,
Video,
2022-05-07 20:08:51 +05:30
Attachment.configure({ renderMarkdown, uploadsPath, eventHub }),
],
2021-10-27 15:23:28 +05:30
});
({
2022-06-21 17:19:12 +05:30
builders: { doc, p, image, audio, video, loading, link },
2021-10-27 15:23:28 +05:30
} = createDocBuilder({
tiptapEditor,
names: {
loading: { markType: Loading.name },
image: { nodeType: Image.name },
link: { nodeType: Link.name },
2022-06-21 17:19:12 +05:30
audio: { nodeType: Audio.name },
video: { nodeType: Video.name },
2021-10-27 15:23:28 +05:30
},
}));
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
it.each`
2021-12-11 22:18:48 +05:30
eventType | propName | eventData | output
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { getData: jest.fn(), files: [attachmentFile] } }} | ${true}
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { getData: jest.fn(), files: [] } }} | ${undefined}
${'drop'} | ${'handleDrop'} | ${{ dataTransfer: { getData: jest.fn(), files: [attachmentFile] } }} | ${true}
2021-10-27 15:23:28 +05:30
`('handles $eventType properly', ({ eventType, propName, eventData, output }) => {
const event = Object.assign(new Event(eventType), eventData);
const handled = tiptapEditor.view.someProp(propName, (eventHandler) => {
return eventHandler(tiptapEditor.view, event);
});
expect(handled).toBe(output);
});
describe('uploadAttachment command', () => {
let initialDoc;
beforeEach(() => {
initialDoc = doc(p(''));
tiptapEditor.commands.setContent(initialDoc.toJSON());
});
2022-06-21 17:19:12 +05:30
describe.each`
nodeType | mimeType | html | file | mediaType
${'image'} | ${'image/png'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${imageFile} | ${(attrs) => image(attrs)}
${'audio'} | ${'audio/mpeg'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${audioFile} | ${(attrs) => audio(attrs)}
${'video'} | ${'video/mp4'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${videoFile} | ${(attrs) => video(attrs)}
`('when the file has $nodeType mime type', ({ mimeType, html, file, mediaType }) => {
const base64EncodedFile = `data:${mimeType};base64,Zm9v`;
2021-10-27 15:23:28 +05:30
beforeEach(() => {
2022-06-21 17:19:12 +05:30
renderMarkdown.mockResolvedValue(html);
2021-10-27 15:23:28 +05:30
});
describe('when uploading succeeds', () => {
const successResponse = {
link: {
2022-06-21 17:19:12 +05:30
markdown: `![test-file](${file.name})`,
2021-10-27 15:23:28 +05:30
},
};
beforeEach(() => {
2023-03-17 16:20:25 +05:30
mock.onPost().reply(HTTP_STATUS_OK, successResponse);
2021-10-27 15:23:28 +05:30
});
2022-06-21 17:19:12 +05:30
it('inserts a media content with src set to the encoded content and uploading true', async () => {
const expectedDoc = doc(p(mediaType({ uploading: true, src: base64EncodedFile })));
2021-10-27 15:23:28 +05:30
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 1,
expectedDoc,
2022-06-21 17:19:12 +05:30
action: () => tiptapEditor.commands.uploadAttachment({ file }),
2021-11-11 11:23:49 +05:30
});
2021-10-27 15:23:28 +05:30
});
2022-06-21 17:19:12 +05:30
it('updates the inserted content with canonicalSrc when upload is successful', async () => {
2021-10-27 15:23:28 +05:30
const expectedDoc = doc(
p(
2022-06-21 17:19:12 +05:30
mediaType({
canonicalSrc: file.name,
2021-10-27 15:23:28 +05:30
src: base64EncodedFile,
alt: 'test-file',
uploading: false,
}),
),
);
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
2022-06-21 17:19:12 +05:30
action: () => tiptapEditor.commands.uploadAttachment({ file }),
2021-11-11 11:23:49 +05:30
});
2021-10-27 15:23:28 +05:30
});
});
describe('when uploading request fails', () => {
beforeEach(() => {
2023-03-17 16:20:25 +05:30
mock.onPost().reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
2021-10-27 15:23:28 +05:30
});
2021-11-11 11:23:49 +05:30
it('resets the doc to original state', async () => {
2021-10-27 15:23:28 +05:30
const expectedDoc = doc(p(''));
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
2022-06-21 17:19:12 +05:30
action: () => tiptapEditor.commands.uploadAttachment({ file }),
2021-11-11 11:23:49 +05:30
});
2021-10-27 15:23:28 +05:30
});
2022-06-21 17:19:12 +05:30
it('emits an alert event that includes an error message', () => {
tiptapEditor.commands.uploadAttachment({ file });
2021-10-27 15:23:28 +05:30
2022-06-21 17:19:12 +05:30
return new Promise((resolve) => {
eventHub.$on('alert', ({ message, variant }) => {
expect(variant).toBe(VARIANT_DANGER);
expect(message).toBe('An error occurred while uploading the file. Please try again.');
resolve();
});
2021-10-27 15:23:28 +05:30
});
});
});
});
describe('when the file has a zip (or any other attachment) mime type', () => {
2021-11-11 11:23:49 +05:30
const markdownApiResult = PROJECT_WIKI_ATTACHMENT_LINK_HTML;
2021-10-27 15:23:28 +05:30
beforeEach(() => {
renderMarkdown.mockResolvedValue(markdownApiResult);
});
describe('when uploading succeeds', () => {
const successResponse = {
link: {
markdown: '[test-file](test-file.zip)',
},
};
beforeEach(() => {
2023-03-17 16:20:25 +05:30
mock.onPost().reply(HTTP_STATUS_OK, successResponse);
2021-10-27 15:23:28 +05:30
});
2021-11-11 11:23:49 +05:30
it('inserts a loading mark', async () => {
2021-10-27 15:23:28 +05:30
const expectedDoc = doc(p(loading({ label: 'test-file' })));
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 1,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: attachmentFile }),
});
2021-10-27 15:23:28 +05:30
});
it('updates the loading mark with a link with canonicalSrc and href attrs', async () => {
const [, group, project] = markdownApiResult.match(/\/(group[0-9]+)\/(project[0-9]+)\//);
const expectedDoc = doc(
p(
link(
{
canonicalSrc: 'test-file.zip',
href: `/${group}/${project}/-/wikis/test-file.zip`,
},
'test-file',
),
),
);
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: attachmentFile }),
});
2021-10-27 15:23:28 +05:30
});
});
describe('when uploading request fails', () => {
beforeEach(() => {
2023-03-17 16:20:25 +05:30
mock.onPost().reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
2021-10-27 15:23:28 +05:30
});
it('resets the doc to orginal state', async () => {
const expectedDoc = doc(p(''));
2021-11-11 11:23:49 +05:30
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: attachmentFile }),
});
2021-10-27 15:23:28 +05:30
});
2022-06-21 17:19:12 +05:30
it('emits an alert event that includes an error message', () => {
2021-10-27 15:23:28 +05:30
tiptapEditor.commands.uploadAttachment({ file: attachmentFile });
2022-05-07 20:08:51 +05:30
eventHub.$on('alert', ({ message, variant }) => {
expect(variant).toBe(VARIANT_DANGER);
2021-12-11 22:18:48 +05:30
expect(message).toBe('An error occurred while uploading the file. Please try again.');
2021-10-27 15:23:28 +05:30
});
});
});
});
});
});