2021-10-27 15:23:28 +05:30
|
|
|
import axios from 'axios';
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
|
|
import Attachment from '~/content_editor/extensions/attachment';
|
|
|
|
import Image from '~/content_editor/extensions/image';
|
|
|
|
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';
|
2021-10-27 15:23:28 +05:30
|
|
|
import httpStatus 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';
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
const PROJECT_WIKI_ATTACHMENT_IMAGE_HTML = `<p data-sourcepos="1:1-1:27" dir="auto">
|
|
|
|
<a class="no-attachment-icon" href="/group1/project1/-/wikis/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="test-file.png">
|
|
|
|
<img alt="test-file" class="lazy" data-src="/group1/project1/-/wikis/test-file.png" data-canonical-src="test-file.png">
|
|
|
|
</a>
|
|
|
|
</p>`;
|
|
|
|
const PROJECT_WIKI_ATTACHMENT_LINK_HTML = `<p data-sourcepos="1:1-1:26" dir="auto">
|
|
|
|
<a href="/group1/project1/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a>
|
|
|
|
</p>`;
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
describe('content_editor/extensions/attachment', () => {
|
|
|
|
let tiptapEditor;
|
|
|
|
let doc;
|
|
|
|
let p;
|
|
|
|
let image;
|
|
|
|
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' });
|
|
|
|
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;
|
|
|
|
const handleTransaction = () => {
|
|
|
|
if (counter === number) {
|
|
|
|
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
|
|
|
|
tiptapEditor.off('update', handleTransaction);
|
|
|
|
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,
|
|
|
|
Attachment.configure({ renderMarkdown, uploadsPath, eventHub }),
|
|
|
|
],
|
2021-10-27 15:23:28 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
({
|
|
|
|
builders: { doc, p, image, loading, link },
|
|
|
|
} = createDocBuilder({
|
|
|
|
tiptapEditor,
|
|
|
|
names: {
|
|
|
|
loading: { markType: Loading.name },
|
|
|
|
image: { nodeType: Image.name },
|
|
|
|
link: { nodeType: Link.name },
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
|
|
|
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());
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the file has image mime type', () => {
|
|
|
|
const base64EncodedFile = 'data:image/png;base64,Zm9v';
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2021-11-11 11:23:49 +05:30
|
|
|
renderMarkdown.mockResolvedValue(PROJECT_WIKI_ATTACHMENT_IMAGE_HTML);
|
2021-10-27 15:23:28 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('when uploading succeeds', () => {
|
|
|
|
const successResponse = {
|
|
|
|
link: {
|
|
|
|
markdown: '![test-file](test-file.png)',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onPost().reply(httpStatus.OK, successResponse);
|
|
|
|
});
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
it('inserts an image with src set to the encoded image file and uploading true', async () => {
|
2021-10-27 15:23:28 +05:30
|
|
|
const expectedDoc = doc(p(image({ uploading: true, src: base64EncodedFile })));
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
await expectDocumentAfterTransaction({
|
|
|
|
number: 1,
|
|
|
|
expectedDoc,
|
|
|
|
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
|
|
|
});
|
2021-10-27 15:23:28 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
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,
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
await expectDocumentAfterTransaction({
|
|
|
|
number: 2,
|
|
|
|
expectedDoc,
|
|
|
|
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
|
|
|
});
|
2021-10-27 15:23:28 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when uploading request fails', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onPost().reply(httpStatus.INTERNAL_SERVER_ERROR);
|
|
|
|
});
|
|
|
|
|
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,
|
|
|
|
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
|
|
|
|
});
|
2021-10-27 15:23:28 +05:30
|
|
|
});
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
it('emits an alert event that includes an error message', (done) => {
|
2021-10-27 15:23:28 +05:30
|
|
|
tiptapEditor.commands.uploadAttachment({ file: imageFile });
|
|
|
|
|
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 image. Please try again.');
|
2021-10-27 15:23:28 +05:30
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
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(() => {
|
|
|
|
mock.onPost().reply(httpStatus.OK, successResponse);
|
|
|
|
});
|
|
|
|
|
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(() => {
|
|
|
|
mock.onPost().reply(httpStatus.INTERNAL_SERVER_ERROR);
|
|
|
|
});
|
|
|
|
|
|
|
|
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
|
|
|
});
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
it('emits an alert event that includes an error message', (done) => {
|
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
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|