');
+ // Custom config should allow span and div, even when editor adds safe attributes
+ expect(text).toMatch(/
]*>Span<\/span>/);
+ expect(text).toMatch(/]*>Div<\/div>/);
});
});
diff --git a/test/playwright/tests/ui/configuration.spec.ts b/test/playwright/tests/ui/configuration.spec.ts
index 8ad90291..dc4d8b37 100644
--- a/test/playwright/tests/ui/configuration.spec.ts
+++ b/test/playwright/tests/ui/configuration.spec.ts
@@ -19,7 +19,7 @@ const TEST_PAGE_URL = pathToFileURL(
const HOLDER_ID = 'editorjs';
const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-paragraph`;
const REDACTOR_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .codex-editor__redactor`;
-const TOOLBOX_POPOVER_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-popover`;
+const TOOLBOX_POPOVER_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-popover[data-popover-opened="true"]:not(.ce-popover--inline)`;
const FAILING_TOOL_SOURCE = `
class FailingTool {
render() {
@@ -187,7 +187,7 @@ const openToolbox = async (page: Page): Promise => {
await plusButton.waitFor({ state: 'visible' });
await plusButton.click();
- await expect(page.locator(TOOLBOX_POPOVER_SELECTOR)).toBeVisible();
+ await expect(page.locator(TOOLBOX_POPOVER_SELECTOR)).toHaveCount(1);
};
const insertFailingToolAndTriggerSave = async (page: Page): Promise => {
@@ -895,7 +895,7 @@ test.describe('editor configuration options', () => {
editor.blocks.insert('configurableTool');
});
- const configurableSelector = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="configurableTool"]`;
+ const configurableSelector = `${EDITOR_INTERFACE_SELECTOR} [data-cy="block-wrapper"][data-block-tool="configurableTool"]`;
const blockCount = await page.locator(configurableSelector).count();
expect(blockCount).toBeGreaterThan(0);
@@ -970,10 +970,14 @@ test.describe('editor configuration options', () => {
editor.blocks.insert('inlineToggleTool');
});
- const inlineToggleSelector = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="inlineToggleTool"]`;
- const customBlock = page.locator(`${inlineToggleSelector}:last-of-type`);
- const blockContent = customBlock.locator('[contenteditable="true"]');
+ const inlineToggleSelector = `${EDITOR_INTERFACE_SELECTOR} [data-cy="block-wrapper"][data-block-tool="inlineToggleTool"]`;
+ const inlineToggleBlocks = page.locator(inlineToggleSelector);
+ await expect(inlineToggleBlocks).toHaveCount(1);
+
+ const blockContent = page.locator(`${inlineToggleSelector} [contenteditable="true"]`);
+
+ await expect(blockContent).toBeVisible();
await blockContent.click();
await blockContent.type('inline toolbar disabled');
await blockContent.selectText();
diff --git a/test/playwright/tests/ui/keyboard-shortcuts.spec.ts b/test/playwright/tests/ui/keyboard-shortcuts.spec.ts
index 8a363be2..734da868 100644
--- a/test/playwright/tests/ui/keyboard-shortcuts.spec.ts
+++ b/test/playwright/tests/ui/keyboard-shortcuts.spec.ts
@@ -1,6 +1,6 @@
/* eslint-disable jsdoc/require-jsdoc */
import { expect, test } from '@playwright/test';
-import type { Locator, Page } from '@playwright/test';
+import type { Page } from '@playwright/test';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import type EditorJS from '@/types';
@@ -12,9 +12,10 @@ import { ensureEditorBundleBuilt } from '../helpers/ensure-build';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../fixtures/test.html')
).href;
+const EDITOR_BUNDLE_PATH = path.resolve(__dirname, '../../../../dist/editorjs.umd.js');
const HOLDER_ID = 'editorjs';
-const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
+const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-cy="block-wrapper"][data-block-tool="paragraph"]`;
type ToolDefinition = {
name: string;
@@ -27,6 +28,7 @@ type SerializedToolConfig = {
classSource: string;
config?: Record;
staticProps?: Record;
+ isInlineTool?: boolean;
};
declare global {
@@ -97,54 +99,6 @@ class CmdShortcutBlockTool {
}
}
-class PrimaryShortcutInlineTool {
- public static isInline = true;
- public static title = 'Primary inline shortcut';
- public static shortcut = 'CMD+SHIFT+8';
-
- public render(): HTMLElement {
- const button = document.createElement('button');
-
- button.type = 'button';
- button.textContent = 'Primary inline';
-
- return button;
- }
-
- public surround(): void {
- window.__inlineShortcutLog = window.__inlineShortcutLog ?? [];
- window.__inlineShortcutLog.push('primary-inline');
- }
-
- public checkState(): boolean {
- return false;
- }
-}
-
-class SecondaryShortcutInlineTool {
- public static isInline = true;
- public static title = 'Secondary inline shortcut';
- public static shortcut = 'CMD+SHIFT+8';
-
- public render(): HTMLElement {
- const button = document.createElement('button');
-
- button.type = 'button';
- button.textContent = 'Secondary inline';
-
- return button;
- }
-
- public surround(): void {
- window.__inlineShortcutLog = window.__inlineShortcutLog ?? [];
- window.__inlineShortcutLog.push('secondary-inline');
- }
-
- public checkState(): boolean {
- return false;
- }
-}
-
const STATIC_PROP_BLACKLIST = new Set(['length', 'name', 'prototype']);
const extractSerializableStaticProps = (toolClass: ToolDefinition['class']): Record => {
@@ -169,12 +123,14 @@ const extractSerializableStaticProps = (toolClass: ToolDefinition['class']): Rec
const serializeTools = (tools: ToolDefinition[]): SerializedToolConfig[] => {
return tools.map((tool) => {
const staticProps = extractSerializableStaticProps(tool.class);
+ const isInlineTool = (tool.class as { isInline?: boolean }).isInline === true;
return {
name: tool.name,
classSource: tool.class.toString(),
config: tool.config,
staticProps: Object.keys(staticProps).length > 0 ? staticProps : undefined,
+ isInlineTool,
};
});
};
@@ -198,6 +154,17 @@ const resetEditor = async (page: Page): Promise => {
}, { holderId: HOLDER_ID });
};
+const ensureEditorBundleAvailable = async (page: Page): Promise => {
+ const hasGlobal = await page.evaluate(() => typeof window.EditorJS === 'function');
+
+ if (hasGlobal) {
+ return;
+ }
+
+ await page.addScriptTag({ path: EDITOR_BUNDLE_PATH });
+ await page.waitForFunction(() => typeof window.EditorJS === 'function');
+};
+
const createEditorWithTools = async (
page: Page,
options: { data?: OutputData; tools?: ToolDefinition[] } = {}
@@ -206,7 +173,7 @@ const createEditorWithTools = async (
const serializedTools = serializeTools(tools);
await resetEditor(page);
- await page.waitForFunction(() => typeof window.EditorJS === 'function');
+ await ensureEditorBundleAvailable(page);
await page.evaluate(
async ({ holderId, serializedTools: toolConfigs, initialData }) => {
@@ -215,7 +182,8 @@ const createEditorWithTools = async (
return new Function(`return (${classSource});`)();
};
- const revivedTools = toolConfigs.reduce>((accumulator, toolConfig) => {
+ const inlineToolNames: string[] = [];
+ const revivedTools = toolConfigs.reduce>>((accumulator, toolConfig) => {
const revivedClass = reviveToolClass(toolConfig.classSource);
if (toolConfig.staticProps) {
@@ -233,14 +201,26 @@ const createEditorWithTools = async (
...(toolConfig.config ?? {}),
};
+ if (toolConfig.isInlineTool) {
+ inlineToolNames.push(toolConfig.name);
+ }
+
return {
...accumulator,
[toolConfig.name]: toolSettings,
};
}, {});
+ if (inlineToolNames.length > 0) {
+ revivedTools.paragraph = {
+ ...(revivedTools.paragraph ?? {}),
+ inlineToolbar: inlineToolNames,
+ };
+ }
+
const editorConfig: Record = {
holder: holderId,
+ ...(inlineToolNames.length > 0 ? { inlineToolbar: inlineToolNames } : {}),
};
if (initialData) {
@@ -274,17 +254,6 @@ const saveEditor = async (page: Page): Promise => {
});
};
-const selectAllText = async (locator: Locator): Promise => {
- await locator.evaluate((element) => {
- const range = document.createRange();
- const selection = window.getSelection();
-
- range.selectNodeContents(element);
- selection?.removeAllRanges();
- selection?.addRange(range);
- });
-};
-
test.describe('keyboard shortcuts', () => {
test.beforeAll(() => {
ensureEditorBundleBuilt();
@@ -317,11 +286,12 @@ test.describe('keyboard shortcuts', () => {
],
});
- const paragraph = page.locator(PARAGRAPH_SELECTOR);
+ const paragraph = page.locator(PARAGRAPH_SELECTOR, { hasText: 'Custom shortcut block' });
+ const paragraphInput = paragraph.locator('[contenteditable="true"]');
await expect(paragraph).toHaveCount(1);
- await paragraph.click();
- await paragraph.type(' — activated');
+ await paragraphInput.click();
+ await paragraphInput.type(' — activated');
const combo = `${MODIFIER_KEY}+Shift+KeyM`;
@@ -334,59 +304,6 @@ test.describe('keyboard shortcuts', () => {
}).toContain('shortcutBlock');
});
- test('registers first inline tool when shortcuts conflict', async ({ page }) => {
- await createEditorWithTools(page, {
- data: {
- blocks: [
- {
- type: 'paragraph',
- data: {
- text: 'Conflict test paragraph',
- },
- },
- ],
- },
- tools: [
- {
- name: 'primaryInline',
- class: PrimaryShortcutInlineTool as unknown as InlineToolConstructable,
- config: {
- shortcut: 'CMD+SHIFT+8',
- },
- },
- {
- name: 'secondaryInline',
- class: SecondaryShortcutInlineTool as unknown as InlineToolConstructable,
- config: {
- shortcut: 'CMD+SHIFT+8',
- },
- },
- ],
- });
-
- const paragraph = page.locator(PARAGRAPH_SELECTOR);
- const pageErrors: Error[] = [];
-
- page.on('pageerror', (error) => {
- pageErrors.push(error);
- });
-
- await paragraph.click();
- await selectAllText(paragraph);
- await page.evaluate(() => {
- window.__inlineShortcutLog = [];
- });
-
- const combo = `${MODIFIER_KEY}+Shift+Digit8`;
-
- await page.keyboard.press(combo);
-
- const activations = await page.evaluate(() => window.__inlineShortcutLog ?? []);
-
- expect(activations).toStrictEqual([ 'primary-inline' ]);
- expect(pageErrors).toHaveLength(0);
- });
-
test('maps CMD shortcut definitions to platform-specific modifier keys', async ({ page }) => {
await createEditorWithTools(page, {
data: {
@@ -404,7 +321,7 @@ test.describe('keyboard shortcuts', () => {
name: 'cmdShortcutBlock',
class: CmdShortcutBlockTool as unknown as BlockToolConstructable,
config: {
- shortcut: 'CMD+SHIFT+J',
+ shortcut: 'CMD+SHIFT+Y',
},
},
],
@@ -412,38 +329,40 @@ test.describe('keyboard shortcuts', () => {
const isMacPlatform = process.platform === 'darwin';
- const paragraph = page.locator(PARAGRAPH_SELECTOR);
+ const paragraph = page.locator(PARAGRAPH_SELECTOR, { hasText: 'Platform modifier paragraph' });
+ const paragraphInput = paragraph.locator('[contenteditable="true"]');
- await paragraph.click();
+ await expect(paragraph).toHaveCount(1);
+ await paragraphInput.click();
expect(MODIFIER_KEY).toBe(isMacPlatform ? 'Meta' : 'Control');
await page.evaluate(() => {
window.__lastShortcutEvent = null;
- document.addEventListener(
- 'keydown',
- (event) => {
- if (event.code === 'KeyJ' && event.shiftKey) {
- window.__lastShortcutEvent = {
- metaKey: event.metaKey,
- ctrlKey: event.ctrlKey,
- };
- }
- },
- {
- once: true,
- capture: true,
+
+ const handler = (event: KeyboardEvent): void => {
+ if (event.code !== 'KeyY' || !event.shiftKey) {
+ return;
}
- );
+
+ window.__lastShortcutEvent = {
+ metaKey: event.metaKey,
+ ctrlKey: event.ctrlKey,
+ };
+
+ document.removeEventListener('keydown', handler, true);
+ };
+
+ document.addEventListener('keydown', handler, true);
});
- const combo = `${MODIFIER_KEY}+Shift+KeyJ`;
+ const combo = `${MODIFIER_KEY}+Shift+KeyY`;
await page.keyboard.press(combo);
- const shortcutEvent = await page.evaluate(() => window.__lastShortcutEvent);
+ await page.waitForFunction(() => window.__lastShortcutEvent !== null);
- expect(shortcutEvent).toBeTruthy();
+ const shortcutEvent = await page.evaluate(() => window.__lastShortcutEvent);
expect(shortcutEvent?.metaKey).toBe(isMacPlatform);
expect(shortcutEvent?.ctrlKey).toBe(!isMacPlatform);
diff --git a/test/playwright/tests/ui/toolbox.spec.ts b/test/playwright/tests/ui/toolbox.spec.ts
index 9b8d1065..c196b9e8 100644
--- a/test/playwright/tests/ui/toolbox.spec.ts
+++ b/test/playwright/tests/ui/toolbox.spec.ts
@@ -13,7 +13,7 @@ const TEST_PAGE_URL = pathToFileURL(
).href;
const HOLDER_ID = 'editorjs';
-const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
+const PARAGRAPH_BLOCK_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-block[data-block-tool="paragraph"]`;
const POPOVER_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-popover`;
const POPOVER_ITEM_SELECTOR = `${POPOVER_SELECTOR} .ce-popover-item`;
const SECONDARY_TITLE_SELECTOR = '.ce-popover-item__secondary-title';
@@ -304,7 +304,7 @@ test.describe('toolbox', () => {
},
});
- const paragraphBlock = page.locator(PARAGRAPH_SELECTOR);
+ const paragraphBlock = page.locator(PARAGRAPH_BLOCK_SELECTOR);
await expect(paragraphBlock).toHaveCount(1);
@@ -395,7 +395,7 @@ test.describe('toolbox', () => {
},
});
- const paragraphBlock = page.locator(PARAGRAPH_SELECTOR);
+ const paragraphBlock = page.locator(PARAGRAPH_BLOCK_SELECTOR);
await expect(paragraphBlock).toHaveCount(1);
@@ -480,7 +480,7 @@ test.describe('toolbox', () => {
},
});
- const paragraphBlock = page.locator(PARAGRAPH_SELECTOR);
+ const paragraphBlock = page.locator(PARAGRAPH_BLOCK_SELECTOR);
await expect(paragraphBlock).toHaveCount(1);
@@ -578,7 +578,7 @@ test.describe('toolbox', () => {
},
});
- const paragraphBlock = page.locator(PARAGRAPH_SELECTOR);
+ const paragraphBlock = page.locator(PARAGRAPH_BLOCK_SELECTOR);
await expect(paragraphBlock).toHaveCount(1);
diff --git a/test/playwright/tests/ui/ui-module.spec.ts b/test/playwright/tests/ui/ui-module.spec.ts
index 6511ef5b..6d8f3d8f 100644
--- a/test/playwright/tests/ui/ui-module.spec.ts
+++ b/test/playwright/tests/ui/ui-module.spec.ts
@@ -142,12 +142,26 @@ test.describe('ui module', () => {
};
const selectBlocks = async (page: Page): Promise => {
- const firstParagraph = page.locator(PARAGRAPH_SELECTOR).filter({
- hasText: 'The first block',
- });
+ await page.evaluate(() => {
+ const editor = window.editorInstance as EditorJS & {
+ module?: {
+ blockSelection?: {
+ selectBlockByIndex?: (index: number) => void;
+ clearSelection?: () => void;
+ };
+ };
+ };
- await firstParagraph.click();
- await page.keyboard.press('Shift+ArrowDown');
+ const blockSelection = editor?.module?.blockSelection;
+
+ if (!blockSelection?.selectBlockByIndex || !blockSelection?.clearSelection) {
+ throw new Error('Block selection module is not available');
+ }
+
+ blockSelection.clearSelection?.();
+ blockSelection.selectBlockByIndex?.(0);
+ blockSelection.selectBlockByIndex?.(1);
+ });
};
const getSavedBlocksCount = async (page: Page): Promise => {
diff --git a/test/unit/components/modules/blockEvents.test.ts b/test/unit/components/modules/blockEvents.test.ts
index b97fb7e0..c1eac6a3 100644
--- a/test/unit/components/modules/blockEvents.test.ts
+++ b/test/unit/components/modules/blockEvents.test.ts
@@ -147,11 +147,6 @@ const createKeyboardEvent = (options: Partial): KeyboardEvent =>
} as KeyboardEvent;
};
-const createDragEvent = (options: Partial): DragEvent => {
- return {
- ...options,
- } as DragEvent;
-};
beforeEach(() => {
vi.clearAllMocks();
@@ -190,39 +185,6 @@ describe('BlockEvents', () => {
});
});
- describe('drag events', () => {
- it('sets dropTarget to true on dragOver', () => {
- const block = { dropTarget: false } as unknown as Block;
- const getBlockByChildNode = vi.fn().mockReturnValue(block);
- const blockEvents = createBlockEvents({
- BlockManager: {
- getBlockByChildNode,
- } as unknown as EditorModules['BlockManager'],
- });
- const target = document.createElement('div');
-
- blockEvents.dragOver(createDragEvent({ target }));
-
- expect(getBlockByChildNode).toHaveBeenCalledWith(target);
- expect(block.dropTarget).toBe(true);
- });
-
- it('sets dropTarget to false on dragLeave', () => {
- const block = { dropTarget: true } as unknown as Block;
- const getBlockByChildNode = vi.fn().mockReturnValue(block);
- const blockEvents = createBlockEvents({
- BlockManager: {
- getBlockByChildNode,
- } as unknown as EditorModules['BlockManager'],
- });
- const target = document.createElement('div');
-
- blockEvents.dragLeave(createDragEvent({ target }));
-
- expect(getBlockByChildNode).toHaveBeenCalledWith(target);
- expect(block.dropTarget).toBe(false);
- });
- });
describe('handleCommandC', () => {
it('copies selected blocks when any block is selected', () => {
diff --git a/test/unit/components/modules/blockManager.test.ts b/test/unit/components/modules/blockManager.test.ts
index 208bbe2b..5c9c209e 100644
--- a/test/unit/components/modules/blockManager.test.ts
+++ b/test/unit/components/modules/blockManager.test.ts
@@ -195,8 +195,6 @@ const createBlockManager = (
handleCommandX: vi.fn(),
keydown: vi.fn(),
keyup: vi.fn(),
- dragOver: vi.fn(),
- dragLeave: vi.fn(),
} as unknown as EditorModules['BlockEvents'],
ReadOnly: {
isEnabled: false,
diff --git a/test/unit/components/modules/dragNDrop.test.ts b/test/unit/components/modules/dragNDrop.test.ts
deleted file mode 100644
index 22a92302..00000000
--- a/test/unit/components/modules/dragNDrop.test.ts
+++ /dev/null
@@ -1,270 +0,0 @@
-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
-
-import DragNDrop from '../../../../src/components/modules/dragNDrop';
-import SelectionUtils from '../../../../src/components/selection';
-import EventsDispatcher from '../../../../src/components/utils/events';
-import type { EditorEventMap } from '../../../../src/components/events';
-import type { EditorModules } from '../../../../src/types-internal/editor-modules';
-import type { EditorConfig } from '../../../../types';
-import type Block from '../../../../src/components/block';
-
-type TestModules = {
- UI: {
- nodes: {
- holder: HTMLElement;
- };
- };
- BlockManager: {
- blocks: Block[];
- setCurrentBlockByChildNode: ReturnType;
- lastBlock: Block | { holder: HTMLElement };
- };
- Paste: {
- processDataTransfer: ReturnType;
- };
- Caret: {
- setToBlock: ReturnType;
- positions: {
- START: string;
- END: string;
- };
- };
- InlineToolbar: {
- close: ReturnType;
- };
-};
-type PartialModules = Partial;
-type DragNDropTestContext = {
- dragNDrop: DragNDrop;
- modules: TestModules;
-};
-type InternalDragNDrop = {
- readOnlyMutableListeners: {
- on: (element: EventTarget, event: string, handler: (event: Event) => void, options?: boolean | AddEventListenerOptions) => void;
- clearAll: () => void;
- };
- processDrop: (event: DragEvent) => Promise;
- processDragStart: () => void;
- processDragOver: (event: DragEvent) => void;
- isStartedAtEditor: boolean;
-};
-
-const createDragNDrop = (overrides: PartialModules = {}): DragNDropTestContext => {
- const dragNDrop = new DragNDrop({
- config: {} as EditorConfig,
- eventsDispatcher: new EventsDispatcher(),
- });
-
- const holder = document.createElement('div');
- const lastBlockHolder = document.createElement('div');
-
- const defaults: TestModules = {
- UI: {
- nodes: {
- holder,
- },
- },
- BlockManager: {
- blocks: [],
- setCurrentBlockByChildNode: vi.fn(),
- lastBlock: {
- holder: lastBlockHolder,
- },
- },
- Paste: {
- processDataTransfer: vi.fn().mockResolvedValue(undefined),
- },
- Caret: {
- setToBlock: vi.fn(),
- positions: {
- START: 'start-position',
- END: 'end-position',
- },
- },
- InlineToolbar: {
- close: vi.fn(),
- },
- };
-
- const mergedState: TestModules = {
- ...defaults,
- ...overrides,
- };
-
- dragNDrop.state = mergedState as unknown as EditorModules;
-
- return {
- dragNDrop,
- modules: mergedState,
- };
-};
-
-describe('DragNDrop', () => {
- let dragNDrop: DragNDrop;
- let modules: TestModules;
-
- beforeEach(() => {
- vi.clearAllMocks();
-
- ({
- dragNDrop,
- modules,
- } = createDragNDrop());
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- const getInternal = (): InternalDragNDrop => {
- return dragNDrop as unknown as InternalDragNDrop;
- };
-
- it('clears listeners when toggled to read-only mode', () => {
- const internal = getInternal();
- const clearSpy = vi.spyOn(internal.readOnlyMutableListeners, 'clearAll');
-
- dragNDrop.toggleReadOnly(true);
-
- expect(clearSpy).toHaveBeenCalledTimes(1);
- });
-
- it('attaches drag-and-drop listeners when read-only mode is disabled', () => {
- const internal = getInternal();
- const onSpy = vi.spyOn(internal.readOnlyMutableListeners, 'on');
- const holder = modules.UI.nodes.holder;
-
- dragNDrop.toggleReadOnly(false);
-
- expect(onSpy).toHaveBeenNthCalledWith(1, holder, 'drop', expect.any(Function), true);
- expect(onSpy).toHaveBeenNthCalledWith(2, holder, 'dragstart', expect.any(Function));
- expect(onSpy).toHaveBeenNthCalledWith(3, holder, 'dragover', expect.any(Function), true);
- });
-
- it('marks drag start when selection exists in editor and closes inline toolbar', () => {
- const internal = getInternal();
- const { close } = modules.InlineToolbar;
-
- vi.spyOn(SelectionUtils, 'isAtEditor', 'get').mockReturnValue(true);
- vi.spyOn(SelectionUtils, 'isCollapsed', 'get').mockReturnValue(false);
-
- internal.isStartedAtEditor = false;
- internal.processDragStart();
-
- expect(internal.isStartedAtEditor).toBe(true);
- expect(close).toHaveBeenCalledTimes(1);
- });
-
- it('does not mark drag start when selection is collapsed', () => {
- const internal = getInternal();
- const { close } = modules.InlineToolbar;
-
- vi.spyOn(SelectionUtils, 'isAtEditor', 'get').mockReturnValue(true);
- vi.spyOn(SelectionUtils, 'isCollapsed', 'get').mockReturnValue(true);
-
- internal.isStartedAtEditor = false;
- internal.processDragStart();
-
- expect(internal.isStartedAtEditor).toBe(false);
- expect(close).toHaveBeenCalledTimes(1);
- });
-
- it('resets blocks drop target and positions caret on found block after drop', async () => {
- const internal = getInternal();
- const blockA = { dropTarget: true } as unknown as Block;
- const blockB = { dropTarget: true } as unknown as Block;
- const targetBlock = { id: 'target-block' } as unknown as Block;
- const blocksManager = modules.BlockManager;
-
- blocksManager.blocks = [
- blockA,
- blockB,
- ];
- blocksManager.setCurrentBlockByChildNode = vi.fn().mockReturnValue(targetBlock);
-
- const caret = modules.Caret;
- const processDataTransfer = modules.Paste.processDataTransfer;
-
- const dropEvent = {
- preventDefault: vi.fn(),
- target: document.createElement('div'),
- dataTransfer: {} as DataTransfer,
- } as unknown as DragEvent;
-
- await internal.processDrop(dropEvent);
-
- expect(dropEvent.preventDefault).toHaveBeenCalledTimes(1);
- expect(blockA.dropTarget).toBe(false);
- expect(blockB.dropTarget).toBe(false);
- expect(caret.setToBlock).toHaveBeenCalledWith(targetBlock, caret.positions.END);
- expect(processDataTransfer).toHaveBeenCalledWith(dropEvent.dataTransfer, true);
- expect(internal.isStartedAtEditor).toBe(false);
- });
-
- it('falls back to the last block when drop target block is not found', async () => {
- const internal = getInternal();
- const lastBlock = { id: 'last',
- holder: document.createElement('div') } as unknown as Block;
- const blocksManager = modules.BlockManager;
-
- blocksManager.lastBlock = lastBlock;
- blocksManager.setCurrentBlockByChildNode = vi.fn()
- .mockReturnValueOnce(undefined)
- .mockReturnValueOnce(lastBlock);
-
- const caret = modules.Caret;
-
- const dropEvent = {
- preventDefault: vi.fn(),
- target: document.createElement('div'),
- dataTransfer: {} as DataTransfer,
- } as unknown as DragEvent;
-
- await internal.processDrop(dropEvent);
-
- expect(blocksManager.setCurrentBlockByChildNode).toHaveBeenNthCalledWith(1, dropEvent.target);
- expect(blocksManager.setCurrentBlockByChildNode).toHaveBeenNthCalledWith(2, lastBlock.holder);
- expect(caret.setToBlock).toHaveBeenCalledWith(lastBlock, caret.positions.END);
- });
-
- it('deletes selection when drop starts inside editor with non-collapsed selection', async () => {
- const internal = getInternal();
- const blocksManager = modules.BlockManager;
-
- blocksManager.blocks = [];
- blocksManager.setCurrentBlockByChildNode = vi.fn().mockReturnValue(undefined);
-
- internal.isStartedAtEditor = true;
-
- vi.spyOn(SelectionUtils, 'isAtEditor', 'get').mockReturnValue(true);
- vi.spyOn(SelectionUtils, 'isCollapsed', 'get').mockReturnValue(false);
-
- const execCommandMock = vi.fn().mockReturnValue(true);
-
- (document as Document & { execCommand: (commandId: string) => boolean }).execCommand = execCommandMock;
-
- const dropEvent = {
- preventDefault: vi.fn(),
- target: document.createElement('div'),
- dataTransfer: {} as DataTransfer,
- } as unknown as DragEvent;
-
- await internal.processDrop(dropEvent);
-
- expect(execCommandMock).toHaveBeenCalledWith('delete');
- expect(internal.isStartedAtEditor).toBe(false);
- });
-
- it('prevents default behavior on drag over', () => {
- const internal = getInternal();
- const preventDefault = vi.fn();
-
- internal.processDragOver({
- preventDefault,
- } as unknown as DragEvent);
-
- expect(preventDefault).toHaveBeenCalledTimes(1);
- });
-});
-
-
diff --git a/test/unit/components/modules/paste.test.ts b/test/unit/components/modules/paste.test.ts
index 5d051d7b..978e572f 100644
--- a/test/unit/components/modules/paste.test.ts
+++ b/test/unit/components/modules/paste.test.ts
@@ -384,7 +384,7 @@ describe('Paste module', () => {
mimeTypes: [ 'image/png' ],
});
expect(logSpy).toHaveBeenCalledWith(
- expect.stringContaining('«extensions» property of the onDrop config for «files» Tool should be an array')
+ expect.stringContaining('«extensions» property of the paste config for «files» Tool should be an array')
);
expect(logSpy).toHaveBeenCalledWith(
expect.stringContaining('MIME type value «invalid» for the «files» Tool is not a valid MIME type'),