Skip to content
Merged
36 changes: 18 additions & 18 deletions frontend/core/__tests__/ArticleForm.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

var mockEditor = {
const mockEditor = {
document: [
{ id: 'p1', type: 'paragraph' },
{ id: 'img1', type: 'image' },
Expand All @@ -10,15 +10,15 @@ var mockEditor = {
setTextCursorPosition: vi.fn(),
};

var mousedownHandler = function (e) {
var target = e.target;
const mousedownHandler = (e) => {
let target = e.target;
if (target.nodeType === 3) target = target.parentNode;
if (target?.closest?.('.bn-formatting-toolbar')) return;
if (target?.closest?.('.bn-block-content[data-content-type="image"]')) return;
if (target?.closest?.('.bn-block-content[data-content-type="video"]')) return;
try {
var doc = mockEditor.document;
for (var i = 0; i < doc.length; i++) {
const doc = mockEditor.document;
for (let i = 0; i < doc.length; i++) {
if (doc[i].type !== 'image' && doc[i].type !== 'video') {
mockEditor.setTextCursorPosition(doc[i].id, 'start');
break;
Expand All @@ -28,54 +28,54 @@ var mousedownHandler = function (e) {
};

function createBlock(contentType) {
var el = document.createElement('div');
const el = document.createElement('div');
el.className = 'bn-block-content';
el.setAttribute('data-content-type', contentType);
return el;
}

describe('ArticleForm mousedown handler', function () {
beforeEach(function () {
describe('ArticleForm mousedown handler', () => {
beforeEach(() => {
document.body.innerHTML = '';
mockEditor.setTextCursorPosition.mockClear();
document.addEventListener('mousedown', mousedownHandler, true);
});

afterEach(function () {
afterEach(() => {
document.removeEventListener('mousedown', mousedownHandler, true);
document.body.innerHTML = '';
});

it('moves cursor to first text block when clicking outside image/video', function () {
it('moves cursor to first text block when clicking outside image/video', () => {
document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).toHaveBeenCalledOnce();
expect(mockEditor.setTextCursorPosition).toHaveBeenCalledWith('p1', 'start');
});

it('returns early when clicking on image block', function () {
var block = createBlock('image');
it('returns early when clicking on image block', () => {
const block = createBlock('image');
document.body.appendChild(block);
block.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).not.toHaveBeenCalled();
});

it('returns early when clicking on video block', function () {
var block = createBlock('video');
it('returns early when clicking on video block', () => {
const block = createBlock('video');
document.body.appendChild(block);
block.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).not.toHaveBeenCalled();
});

it('returns early when clicking on formatting toolbar', function () {
var toolbar = document.createElement('div');
it('returns early when clicking on formatting toolbar', () => {
const toolbar = document.createElement('div');
toolbar.className = 'bn-formatting-toolbar';
document.body.appendChild(toolbar);
toolbar.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).not.toHaveBeenCalled();
});

it('moves cursor to paragraph after image blocks when image clicked then outside', function () {
var block = createBlock('video');
it('moves cursor to paragraph after image blocks when image clicked then outside', () => {
const block = createBlock('video');
document.body.appendChild(block);
block.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).not.toHaveBeenCalled();
Expand Down
72 changes: 36 additions & 36 deletions frontend/core/__tests__/ArticleViewer.test.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

function handler(e) {
var block = document.querySelector(
const block = document.querySelector(
'.bn-block-content[data-content-type="image"].ProseMirror-selectednode',
);
if (!block) return;
var imgEl = block.querySelector('img');
const imgEl = block.querySelector('img');
if (!imgEl) return;
e.preventDefault();
e.stopPropagation();
var alt = imgEl.alt || '';
var src = imgEl.getAttribute('src') || '';
var text;
const alt = imgEl.alt || '';
const src = imgEl.getAttribute('src') || '';
let text;
if (src && !src.startsWith('/uploads/')) {
text = src;
} else {
Expand All @@ -21,27 +21,27 @@ function handler(e) {
new ClipboardItem({
'text/plain': new Blob([text], { type: 'text/plain' }),
}),
]).catch(function () {});
]).catch(() => {});
}

function setImageDOM(alt, src, hasSelection) {
var outer = document.createElement('div');
const outer = document.createElement('div');
outer.className = hasSelection
? 'bn-block-content ProseMirror-selectednode'
: 'bn-block-content';
outer.setAttribute('data-content-type', 'image');
var img = document.createElement('img');
const img = document.createElement('img');
if (alt) img.alt = alt;
img.setAttribute('src', src);
outer.appendChild(img);
document.body.appendChild(outer);
return { outer: outer, img: img };
return { outer, img };
}

describe('ArticleViewer copy handler', function () {
var mockWrite;
describe('ArticleViewer copy handler', () => {
let mockWrite;

beforeEach(function () {
beforeEach(() => {
mockWrite = vi.fn().mockResolvedValue(undefined);
vi.stubGlobal('navigator', {
clipboard: { write: mockWrite },
Expand All @@ -53,51 +53,51 @@ describe('ArticleViewer copy handler', function () {
document.addEventListener('copy', handler, true);
});

afterEach(function () {
afterEach(() => {
document.removeEventListener('copy', handler, true);
vi.unstubAllGlobals();
vi.clearAllMocks();
document.body.innerHTML = '';
});

it('writes alt text for selected upload image', async function () {
it('writes alt text for selected upload image', async () => {
setImageDOM('my photo', '/uploads/foo.jpg', true);
document.dispatchEvent(new ClipboardEvent('copy', { cancelable: true, bubbles: true }));
expect(mockWrite).toHaveBeenCalledOnce();
var text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
const text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
expect(text).toBe('my photo');
});

it('writes filename when upload image has no alt', async function () {
it('writes filename when upload image has no alt', async () => {
setImageDOM('', '/uploads/foo.jpg', true);
document.dispatchEvent(new ClipboardEvent('copy', { cancelable: true, bubbles: true }));
expect(mockWrite).toHaveBeenCalledOnce();
var text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
const text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
expect(text).toBe('foo.jpg');
});

it('writes URL for external image', async function () {
it('writes URL for external image', async () => {
setImageDOM('alt text', 'https://example.com/img.jpg', true);
document.dispatchEvent(new ClipboardEvent('copy', { cancelable: true, bubbles: true }));
expect(mockWrite).toHaveBeenCalledOnce();
var text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
const text = await mockWrite.mock.calls[0][0][0].items['text/plain'].text();
expect(text).toBe('https://example.com/img.jpg');
});

it('does nothing when image lacks .ProseMirror-selectednode', function () {
it('does nothing when image lacks .ProseMirror-selectednode', () => {
setImageDOM('alt', '/uploads/foo.jpg', false);
document.dispatchEvent(new ClipboardEvent('copy', { cancelable: true, bubbles: true }));
expect(mockWrite).not.toHaveBeenCalled();
});

it('does nothing when no image DOM at all', function () {
it('does nothing when no image DOM at all', () => {
document.dispatchEvent(new ClipboardEvent('copy', { cancelable: true, bubbles: true }));
expect(mockWrite).not.toHaveBeenCalled();
});

it('stops event propagation for selected image', function () {
var child = document.createElement('div');
child.addEventListener('copy', function (e) {
it('stops event propagation for selected image', () => {
const child = document.createElement('div');
child.addEventListener('copy', (e) => {
expect(e.defaultPrevented).toBe(true);
expect(e.cancelBubble).toBe(true);
});
Expand All @@ -107,7 +107,7 @@ describe('ArticleViewer copy handler', function () {
});
});

var mockEditor = {
const mockEditor = {
document: [
{ id: 'p1', type: 'paragraph' },
{ id: 'img1', type: 'image' },
Expand All @@ -117,13 +117,13 @@ var mockEditor = {
setTextCursorPosition: vi.fn(),
};

var viewerHandler = function (e) {
var target = e.target;
const viewerHandler = (e) => {
let target = e.target;
if (target.nodeType === 3) target = target.parentNode;
if (target?.closest?.('.bn-block-content[data-content-type="image"]')) return;
try {
var doc = mockEditor.document;
for (var i = 0; i < doc.length; i++) {
const doc = mockEditor.document;
for (let i = 0; i < doc.length; i++) {
if (doc[i].type !== 'image' && doc[i].type !== 'video') {
mockEditor.setTextCursorPosition(doc[i].id, 'start');
break;
Expand All @@ -133,32 +133,32 @@ var viewerHandler = function (e) {
};

function createBlock(contentType) {
var el = document.createElement('div');
const el = document.createElement('div');
el.className = 'bn-block-content';
el.setAttribute('data-content-type', contentType);
return el;
}

describe('ArticleViewer mousedown handler', function () {
beforeEach(function () {
describe('ArticleViewer mousedown handler', () => {
beforeEach(() => {
document.body.innerHTML = '';
mockEditor.setTextCursorPosition.mockClear();
document.addEventListener('mousedown', viewerHandler, true);
});

afterEach(function () {
afterEach(() => {
document.removeEventListener('mousedown', viewerHandler, true);
document.body.innerHTML = '';
});

it('moves cursor to first text block when clicking outside image', function () {
it('moves cursor to first text block when clicking outside image', () => {
document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).toHaveBeenCalledOnce();
expect(mockEditor.setTextCursorPosition).toHaveBeenCalledWith('p1', 'start');
});

it('returns early when clicking on image block', function () {
var block = createBlock('image');
it('returns early when clicking on image block', () => {
const block = createBlock('image');
document.body.appendChild(block);
block.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
expect(mockEditor.setTextCursorPosition).not.toHaveBeenCalled();
Expand Down
Loading
Loading