mirror of
https://github.com/codex-team/editor.js
synced 2026-03-16 15:45:47 +01:00
736 lines
23 KiB
TypeScript
736 lines
23 KiB
TypeScript
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
import type { EditorConfig } from '../../types';
|
|
import type Core from '../../src/components/core';
|
|
import type { EditorModules } from '../../src/types-internal/editor-modules';
|
|
|
|
// Mock VERSION global variable
|
|
declare global {
|
|
// eslint-disable-next-line no-var -- TypeScript requires 'var' for global declarations
|
|
var VERSION: string;
|
|
}
|
|
|
|
// Define VERSION before importing codex
|
|
(global as { VERSION?: string }).VERSION = '2.31.0-test';
|
|
|
|
// Mock dependencies
|
|
vi.mock('../../src/components/utils/tooltip', () => {
|
|
const mockDestroyTooltip = vi.fn();
|
|
|
|
return {
|
|
destroy: mockDestroyTooltip,
|
|
mockDestroyTooltip,
|
|
};
|
|
});
|
|
|
|
vi.mock('../../src/components/utils', async () => {
|
|
const actual = await vi.importActual('../../src/components/utils');
|
|
const mockIsObject = vi.fn((v: unknown) => typeof v === 'object' && v !== null && !Array.isArray(v));
|
|
const mockIsFunction = vi.fn((fn: unknown) => typeof fn === 'function');
|
|
|
|
return {
|
|
...actual,
|
|
isObject: mockIsObject,
|
|
isFunction: mockIsFunction,
|
|
mockIsObject,
|
|
mockIsFunction,
|
|
};
|
|
});
|
|
|
|
// Mock Core class - use factory function to avoid hoisting issues
|
|
vi.mock('../../src/components/core', () => {
|
|
const createMockModuleInstances = (): Partial<EditorModules> => ({
|
|
API: {
|
|
methods: {
|
|
blocks: {
|
|
clear: vi.fn(),
|
|
render: vi.fn(),
|
|
} as unknown as EditorModules['API']['methods']['blocks'],
|
|
caret: {
|
|
focus: vi.fn(),
|
|
} as unknown as EditorModules['API']['methods']['caret'],
|
|
events: {
|
|
on: vi.fn(),
|
|
off: vi.fn(),
|
|
emit: vi.fn(),
|
|
},
|
|
saver: {
|
|
save: vi.fn(),
|
|
},
|
|
} as unknown as EditorModules['API']['methods'],
|
|
} as unknown as EditorModules['API'],
|
|
Toolbar: {
|
|
blockSettings: undefined,
|
|
inlineToolbar: undefined,
|
|
} as unknown as EditorModules['Toolbar'],
|
|
BlockSettings: {} as unknown as EditorModules['BlockSettings'],
|
|
InlineToolbar: {} as unknown as EditorModules['InlineToolbar'],
|
|
});
|
|
|
|
const mockModuleInstances = createMockModuleInstances();
|
|
const lastInstanceRef = { value: undefined as Core | undefined };
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class MockCore {
|
|
public configuration: Record<string, unknown> = {};
|
|
public moduleInstances: Partial<EditorModules>;
|
|
public isReady: Promise<void>;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
constructor() {
|
|
this.moduleInstances = {
|
|
...mockModuleInstances,
|
|
};
|
|
this.isReady = Promise.resolve();
|
|
// Store the last instance for test access
|
|
lastInstanceRef.value = this as unknown as Core;
|
|
}
|
|
}
|
|
|
|
return {
|
|
default: MockCore,
|
|
mockModuleInstances,
|
|
lastInstance: () => lastInstanceRef.value,
|
|
};
|
|
});
|
|
|
|
// Mock @babel/register
|
|
vi.mock('@babel/register', () => ({}));
|
|
|
|
// Mock polyfills
|
|
vi.mock('../../src/components/polyfills', () => ({}));
|
|
|
|
// Import EditorJS after mocks are set up
|
|
import EditorJS from '../../src/codex';
|
|
|
|
describe('EditorJS', () => {
|
|
// Get mocked instances
|
|
const mocks = {
|
|
mockModuleInstances: undefined as Partial<EditorModules> | undefined,
|
|
mockIsObject: undefined as ReturnType<typeof vi.fn> | undefined,
|
|
mockIsFunction: undefined as ReturnType<typeof vi.fn> | undefined,
|
|
mockDestroyTooltip: undefined as ReturnType<typeof vi.fn> | undefined,
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
// Import the mocked modules to access the mock instances
|
|
const coreModule = await import('../../src/components/core') as {
|
|
default: new (...args: unknown[]) => Core;
|
|
mockModuleInstances?: Partial<EditorModules>;
|
|
};
|
|
|
|
const utilsModule = await import('../../src/components/utils') as {
|
|
mockIsObject?: ReturnType<typeof vi.fn>;
|
|
mockIsFunction?: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
const tooltipModule = await import('../../src/components/utils/tooltip') as {
|
|
mockDestroyTooltip?: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
mocks.mockModuleInstances = coreModule.mockModuleInstances as Partial<EditorModules>;
|
|
mocks.mockIsObject = utilsModule.mockIsObject as ReturnType<typeof vi.fn>;
|
|
mocks.mockIsFunction = utilsModule.mockIsFunction as ReturnType<typeof vi.fn>;
|
|
mocks.mockDestroyTooltip = tooltipModule.mockDestroyTooltip as ReturnType<typeof vi.fn>;
|
|
|
|
vi.clearAllMocks();
|
|
mocks.mockModuleInstances!.API = {
|
|
methods: {
|
|
blocks: {
|
|
clear: vi.fn(),
|
|
render: vi.fn(),
|
|
} as unknown as EditorModules['API']['methods']['blocks'],
|
|
caret: {
|
|
focus: vi.fn(),
|
|
} as unknown as EditorModules['API']['methods']['caret'],
|
|
events: {
|
|
on: vi.fn(),
|
|
off: vi.fn(),
|
|
emit: vi.fn(),
|
|
},
|
|
saver: {
|
|
save: vi.fn(),
|
|
},
|
|
} as unknown as EditorModules['API']['methods'],
|
|
} as unknown as EditorModules['API'];
|
|
mocks.mockModuleInstances!.Toolbar = {
|
|
blockSettings: undefined,
|
|
inlineToolbar: undefined,
|
|
} as unknown as EditorModules['Toolbar'];
|
|
mocks.mockModuleInstances!.BlockSettings = {} as unknown as EditorModules['BlockSettings'];
|
|
mocks.mockModuleInstances!.InlineToolbar = {} as unknown as EditorModules['InlineToolbar'];
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should initialize with no configuration', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
expect(editor.isReady).toBeInstanceOf(Promise);
|
|
expect(editor.destroy).toBeDefined();
|
|
expect(typeof editor.destroy).toBe('function');
|
|
|
|
await editor.isReady;
|
|
});
|
|
|
|
it('should initialize with string configuration (holderId)', async () => {
|
|
const holderId = 'my-editor';
|
|
|
|
const editor = new EditorJS(holderId);
|
|
|
|
expect(editor.isReady).toBeInstanceOf(Promise);
|
|
|
|
await editor.isReady;
|
|
});
|
|
|
|
it('should initialize with EditorConfig object', async () => {
|
|
const config: EditorConfig = {
|
|
holder: 'editorjs',
|
|
placeholder: 'Start typing...',
|
|
};
|
|
|
|
const editor = new EditorJS(config);
|
|
|
|
expect(editor.isReady).toBeInstanceOf(Promise);
|
|
|
|
await editor.isReady;
|
|
});
|
|
|
|
it('should call onReady callback when provided', async () => {
|
|
const onReady = vi.fn();
|
|
const config: EditorConfig = {
|
|
holder: 'editorjs',
|
|
onReady,
|
|
};
|
|
|
|
mocks.mockIsObject!.mockReturnValue(true);
|
|
mocks.mockIsFunction!.mockReturnValue(true);
|
|
|
|
const editor = new EditorJS(config);
|
|
|
|
await editor.isReady;
|
|
|
|
expect(onReady).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should use default empty onReady function when not provided', async () => {
|
|
const config: EditorConfig = {
|
|
holder: 'editorjs',
|
|
};
|
|
|
|
mocks.mockIsObject!.mockReturnValue(true);
|
|
mocks.mockIsFunction!.mockReturnValue(false);
|
|
|
|
const editor = new EditorJS(config);
|
|
|
|
await editor.isReady;
|
|
|
|
// Should not throw
|
|
expect(editor.isReady).toBeInstanceOf(Promise);
|
|
});
|
|
|
|
it('should initialize destroy as no-op before exportAPI', () => {
|
|
const editor = new EditorJS();
|
|
|
|
// Before isReady resolves, destroy should be a no-op
|
|
expect(editor.destroy).toBeDefined();
|
|
expect(() => editor.destroy()).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('isReady promise', () => {
|
|
it('should resolve when Core is ready', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await expect(editor.isReady).resolves.toBeUndefined();
|
|
});
|
|
|
|
it('should call exportAPI when Core is ready', async () => {
|
|
const editor = new EditorJS();
|
|
const exportAPISpy = vi.spyOn(editor, 'exportAPI');
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
|
|
expect(exportAPISpy).toHaveBeenCalled();
|
|
if (lastCall) {
|
|
expect(exportAPISpy).toHaveBeenCalledWith(lastCall);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('exportAPI', () => {
|
|
it('should export configuration field', async () => {
|
|
const config: EditorConfig = {
|
|
holder: 'editorjs',
|
|
placeholder: 'Test placeholder',
|
|
};
|
|
|
|
const editor = new EditorJS(config);
|
|
|
|
await editor.isReady;
|
|
|
|
expect((editor as unknown as Record<string, unknown>).configuration).toEqual(config);
|
|
});
|
|
|
|
it('should set prototype to API methods', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
default: new (...args: unknown[]) => Core;
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
|
|
expect(Object.getPrototypeOf(editor)).toBe(lastCall?.moduleInstances.API?.methods);
|
|
});
|
|
|
|
it('should create module aliases', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
const moduleAliases = (editor as unknown as { module: Record<string, unknown> }).module;
|
|
|
|
expect(moduleAliases).toBeDefined();
|
|
expect(typeof moduleAliases).toBe('object');
|
|
});
|
|
|
|
it('should create lowercase aliases for uppercase module names', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.API = {
|
|
methods: {},
|
|
} as EditorModules['API'];
|
|
instances.Toolbar = {} as EditorModules['Toolbar'];
|
|
|
|
const moduleAliases = (editor as unknown as { module: Record<string, unknown> }).module;
|
|
|
|
// API should become 'api'
|
|
expect(moduleAliases.api).toBe(instances.API);
|
|
// Toolbar should become 'toolbar'
|
|
expect(moduleAliases.toolbar).toBe(instances.Toolbar);
|
|
});
|
|
|
|
it('should create camelCase aliases for PascalCase module names', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.BlockSettings = {} as EditorModules['BlockSettings'];
|
|
instances.InlineToolbar = {} as EditorModules['InlineToolbar'];
|
|
|
|
const moduleAliases = (editor as unknown as { module: Record<string, unknown> }).module;
|
|
|
|
expect(moduleAliases.blockSettings).toBe(instances.BlockSettings);
|
|
expect(moduleAliases.inlineToolbar).toBe(instances.InlineToolbar);
|
|
});
|
|
|
|
it('should skip undefined module instances', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.API = {
|
|
methods: {},
|
|
} as EditorModules['API'];
|
|
instances.Toolbar = undefined as unknown as EditorModules['Toolbar'];
|
|
|
|
const moduleAliases = (editor as unknown as { module: Record<string, unknown> }).module;
|
|
|
|
expect(moduleAliases.toolbar).toBeUndefined();
|
|
});
|
|
|
|
it('should attach blockSettings to toolbar module if not already present', async () => {
|
|
const mockToolbar = {
|
|
blockSettings: undefined,
|
|
};
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.Toolbar = mockToolbar as unknown as EditorModules['Toolbar'];
|
|
instances.BlockSettings = {} as unknown as EditorModules['BlockSettings'];
|
|
|
|
// Re-export API to apply the changes
|
|
(editor as unknown as { exportAPI: (core: Core) => void }).exportAPI(lastCall);
|
|
|
|
expect(mockToolbar.blockSettings).toBe(instances.BlockSettings);
|
|
});
|
|
|
|
it('should not override existing blockSettings on toolbar module', async () => {
|
|
const existingBlockSettings = { existing: true };
|
|
const mockToolbar = {
|
|
blockSettings: existingBlockSettings,
|
|
};
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.Toolbar = mockToolbar as unknown as EditorModules['Toolbar'];
|
|
instances.BlockSettings = {} as unknown as EditorModules['BlockSettings'];
|
|
|
|
// Re-export API to apply the changes
|
|
(editor as unknown as { exportAPI: (core: Core) => void }).exportAPI(lastCall);
|
|
|
|
expect(mockToolbar.blockSettings).toBe(existingBlockSettings);
|
|
expect(mockToolbar.blockSettings).not.toBe(instances.BlockSettings);
|
|
});
|
|
|
|
it('should attach inlineToolbar to toolbar module if not already present', async () => {
|
|
const mockToolbar = {
|
|
inlineToolbar: undefined,
|
|
};
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.Toolbar = mockToolbar as unknown as EditorModules['Toolbar'];
|
|
instances.InlineToolbar = {} as unknown as EditorModules['InlineToolbar'];
|
|
|
|
// Re-export API to apply the changes
|
|
(editor as unknown as { exportAPI: (core: Core) => void }).exportAPI(lastCall);
|
|
|
|
expect(mockToolbar.inlineToolbar).toBe(instances.InlineToolbar);
|
|
});
|
|
|
|
it('should not override existing inlineToolbar on toolbar module', async () => {
|
|
const existingInlineToolbar = { existing: true };
|
|
const mockToolbar = {
|
|
inlineToolbar: existingInlineToolbar,
|
|
};
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
instances.Toolbar = mockToolbar as unknown as EditorModules['Toolbar'];
|
|
instances.InlineToolbar = {} as unknown as EditorModules['InlineToolbar'];
|
|
|
|
// Re-export API to apply the changes
|
|
(editor as unknown as { exportAPI: (core: Core) => void }).exportAPI(lastCall);
|
|
|
|
expect(mockToolbar.inlineToolbar).toBe(existingInlineToolbar);
|
|
expect(mockToolbar.inlineToolbar).not.toBe(instances.InlineToolbar);
|
|
});
|
|
|
|
it('should create shorthands for blocks methods', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
expect((editor as unknown as { clear: unknown }).clear).toBe(instances.API?.methods.blocks.clear);
|
|
expect((editor as unknown as { render: unknown }).render).toBe(instances.API?.methods.blocks.render);
|
|
});
|
|
|
|
it('should create shorthands for caret methods', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
expect((editor as unknown as { focus: unknown }).focus).toBe(instances.API?.methods.caret.focus);
|
|
});
|
|
|
|
it('should create shorthands for events methods', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
expect((editor as unknown as { on: unknown }).on).toBe(instances.API?.methods.events.on);
|
|
expect((editor as unknown as { off: unknown }).off).toBe(instances.API?.methods.events.off);
|
|
expect((editor as unknown as { emit: unknown }).emit).toBe(instances.API?.methods.events.emit);
|
|
});
|
|
|
|
it('should create shorthands for saver methods', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Get the actual Core instance that was created
|
|
const coreModule = await import('../../src/components/core') as {
|
|
lastInstance?: () => Core | undefined;
|
|
};
|
|
const lastCall = coreModule.lastInstance?.();
|
|
const instances = lastCall?.moduleInstances;
|
|
|
|
if (!instances || !lastCall) {
|
|
throw new Error('Core instance not found');
|
|
}
|
|
|
|
expect((editor as unknown as { save: unknown }).save).toBe(instances.API?.methods.saver.save);
|
|
});
|
|
|
|
it('should delete exportAPI method after export', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
expect(Object.prototype.hasOwnProperty.call(editor, 'exportAPI')).toBe(false);
|
|
expect(typeof (editor as unknown as { exportAPI: unknown }).exportAPI).toBe('function');
|
|
});
|
|
|
|
it('should make module property non-enumerable', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
const descriptor = Object.getOwnPropertyDescriptor(editor, 'module');
|
|
|
|
expect(descriptor).toBeDefined();
|
|
expect(descriptor?.enumerable).toBe(false);
|
|
expect(descriptor?.configurable).toBe(true);
|
|
expect(descriptor?.writable).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('destroy', () => {
|
|
it('should call destroy on all module instances that have destroy method', async () => {
|
|
const mockDestroy1 = vi.fn();
|
|
const mockDestroy2 = vi.fn();
|
|
const mockModule1 = { destroy: mockDestroy1 };
|
|
const mockModule2 = { destroy: mockDestroy2 };
|
|
const mockModule3 = { noDestroy: true };
|
|
|
|
mocks.mockModuleInstances!.Toolbar = mockModule1 as unknown as EditorModules['Toolbar'];
|
|
mocks.mockModuleInstances!.BlockSettings = mockModule2 as unknown as EditorModules['BlockSettings'];
|
|
mocks.mockModuleInstances!.InlineToolbar = mockModule3 as unknown as EditorModules['InlineToolbar'];
|
|
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
editor.destroy();
|
|
|
|
expect(mockDestroy1).toHaveBeenCalledTimes(1);
|
|
expect(mockDestroy2).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should remove all listeners from module instances', async () => {
|
|
const mockRemoveAll = vi.fn();
|
|
const mockModule = {
|
|
listeners: {
|
|
removeAll: mockRemoveAll,
|
|
},
|
|
};
|
|
|
|
mocks.mockModuleInstances!.Toolbar = mockModule as unknown as EditorModules['Toolbar'];
|
|
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
editor.destroy();
|
|
|
|
expect(mockRemoveAll).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should call destroyTooltip', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
editor.destroy();
|
|
|
|
expect(mocks.mockDestroyTooltip).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should delete all own properties', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Add some test properties
|
|
const testValue = 123;
|
|
|
|
(editor as unknown as Record<string, unknown>).testProperty = 'test';
|
|
(editor as unknown as Record<string, unknown>).anotherProperty = testValue;
|
|
|
|
expect((editor as unknown as Record<string, unknown>).testProperty).toBe('test');
|
|
expect((editor as unknown as Record<string, unknown>).anotherProperty).toBe(testValue);
|
|
|
|
editor.destroy();
|
|
|
|
expect((editor as unknown as Record<string, unknown>).testProperty).toBeUndefined();
|
|
expect((editor as unknown as Record<string, unknown>).anotherProperty).toBeUndefined();
|
|
});
|
|
|
|
it('should set prototype to null', async () => {
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Before destroy, prototype should be API methods
|
|
expect(Object.getPrototypeOf(editor)).toBe(mocks.mockModuleInstances!.API?.methods);
|
|
|
|
editor.destroy();
|
|
|
|
// After destroy, prototype should be null
|
|
expect(Object.getPrototypeOf(editor)).toBeNull();
|
|
});
|
|
|
|
it('should handle modules without listeners property', async () => {
|
|
const mockModule = {
|
|
destroy: vi.fn(),
|
|
// No listeners property
|
|
};
|
|
|
|
mocks.mockModuleInstances!.Toolbar = mockModule as unknown as EditorModules['Toolbar'];
|
|
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Should not throw
|
|
expect(() => editor.destroy()).not.toThrow();
|
|
});
|
|
|
|
it('should handle modules without destroy method', async () => {
|
|
const mockModule = {
|
|
listeners: {
|
|
removeAll: vi.fn(),
|
|
},
|
|
// No destroy method
|
|
};
|
|
|
|
mocks.mockModuleInstances!.Toolbar = mockModule as unknown as EditorModules['Toolbar'];
|
|
|
|
const editor = new EditorJS();
|
|
|
|
await editor.isReady;
|
|
|
|
// Should not throw
|
|
expect(() => editor.destroy()).not.toThrow();
|
|
expect(mockModule.listeners.removeAll).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('static version', () => {
|
|
it('should expose version as static property', () => {
|
|
expect(EditorJS.version).toBeDefined();
|
|
expect(typeof EditorJS.version).toBe('string');
|
|
});
|
|
});
|
|
});
|
|
|