mirror of
https://github.com/codex-team/editor.js
synced 2026-03-16 23:55:49 +01:00
fix: lint issues in tests
This commit is contained in:
parent
4e7e384375
commit
4edf334b41
41 changed files with 373 additions and 145 deletions
|
|
@ -22,7 +22,6 @@ export class SimpleHeader implements BaseTool {
|
|||
|
||||
/**
|
||||
* Return Tool's view
|
||||
*
|
||||
* @returns {HTMLHeadingElement}
|
||||
* @public
|
||||
*/
|
||||
|
|
@ -44,7 +43,6 @@ export class SimpleHeader implements BaseTool {
|
|||
|
||||
/**
|
||||
* Extract Tool's data from the view
|
||||
*
|
||||
* @param toolsContent - Text tools rendered view
|
||||
*/
|
||||
public save(toolsContent: HTMLHeadingElement): BlockToolData {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class ToolMock implements BlockTool {
|
|||
public render(): HTMLElement {
|
||||
const contenteditable = document.createElement('div');
|
||||
|
||||
if (this.data && this.data.text) {
|
||||
if (this.data.text) {
|
||||
contenteditable.innerHTML = this.data.text;
|
||||
}
|
||||
|
||||
|
|
|
|||
28
test/cypress/simple-image.d.ts
vendored
Normal file
28
test/cypress/simple-image.d.ts
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Declaration for external JS module @editorjs/simple-image
|
||||
*/
|
||||
declare module '@editorjs/simple-image' {
|
||||
interface SimpleImageConfig {
|
||||
data?: Record<string, unknown>;
|
||||
readOnly?: boolean;
|
||||
api?: unknown;
|
||||
block?: unknown;
|
||||
}
|
||||
|
||||
interface SimpleImageInstance {
|
||||
render(): HTMLElement;
|
||||
save(block: HTMLElement): Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface SimpleImageTool {
|
||||
toolbox?: unknown;
|
||||
pasteConfig?: unknown;
|
||||
conversionConfig?: unknown;
|
||||
isReadOnlySupported?: boolean;
|
||||
new (config: SimpleImageConfig): SimpleImageInstance;
|
||||
}
|
||||
|
||||
const Image: SimpleImageTool;
|
||||
export default Image;
|
||||
}
|
||||
|
||||
8
test/cypress/support/chai-subset.d.ts
vendored
Normal file
8
test/cypress/support/chai-subset.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Type declaration for chai-subset module
|
||||
*/
|
||||
declare module 'chai-subset' {
|
||||
const chaiSubset: (chai: any, utils: any) => void;
|
||||
export default chaiSubset;
|
||||
}
|
||||
|
||||
|
|
@ -13,7 +13,6 @@ import Chainable = Cypress.Chainable;
|
|||
/**
|
||||
* Create a wrapper and initialize the new instance of editor.js
|
||||
* Then return the instance
|
||||
*
|
||||
* @param editorConfig - config to pass to the editor
|
||||
* @returns EditorJS - created instance
|
||||
*/
|
||||
|
|
@ -43,7 +42,6 @@ Cypress.Commands.add('createEditor', (editorConfig: EditorConfig = {}): Chainabl
|
|||
*
|
||||
* Usage
|
||||
* cy.get('div').paste({'text/plain': 'Text', 'text/html': '<b>Text</b>'})
|
||||
*
|
||||
* @param data - map with MIME type as a key and data as value
|
||||
*/
|
||||
Cypress.Commands.add('paste', {
|
||||
|
|
@ -54,7 +52,7 @@ Cypress.Commands.add('paste', {
|
|||
cancelable: true,
|
||||
}), {
|
||||
clipboardData: {
|
||||
getData: (type): string => data[type],
|
||||
getData: (type: string): string => data[type],
|
||||
types: Object.keys(data),
|
||||
},
|
||||
});
|
||||
|
|
@ -70,7 +68,9 @@ Cypress.Commands.add('paste', {
|
|||
* Usage:
|
||||
* cy.get('div').copy().then(data => {})
|
||||
*/
|
||||
Cypress.Commands.add('copy', { prevSubject: true }, (subject) => {
|
||||
Cypress.Commands.add('copy', {
|
||||
prevSubject: ['element'],
|
||||
}, (subject) => {
|
||||
const clipboardData: {[type: string]: any} = {};
|
||||
|
||||
const copyEvent = Object.assign(new Event('copy', {
|
||||
|
|
@ -86,7 +86,7 @@ Cypress.Commands.add('copy', { prevSubject: true }, (subject) => {
|
|||
|
||||
subject[0].dispatchEvent(copyEvent);
|
||||
|
||||
return cy.wrap(clipboardData);
|
||||
return cy.wrap<Record<string, string>>(clipboardData);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +95,7 @@ Cypress.Commands.add('copy', { prevSubject: true }, (subject) => {
|
|||
* Usage:
|
||||
* cy.get('div').cut().then(data => {})
|
||||
*/
|
||||
Cypress.Commands.add('cut', { prevSubject: true }, (subject) => {
|
||||
Cypress.Commands.add('cut', { prevSubject: ['element'] }, (subject) => {
|
||||
const clipboardData: {[type: string]: any} = {};
|
||||
|
||||
const copyEvent = Object.assign(new Event('cut', {
|
||||
|
|
@ -111,12 +111,11 @@ Cypress.Commands.add('cut', { prevSubject: true }, (subject) => {
|
|||
|
||||
subject[0].dispatchEvent(copyEvent);
|
||||
|
||||
return cy.wrap(clipboardData);
|
||||
return cy.wrap<Record<string, string>>(clipboardData);
|
||||
});
|
||||
|
||||
/**
|
||||
* Calls EditorJS API render method
|
||||
*
|
||||
* @param data — data to render
|
||||
*/
|
||||
Cypress.Commands.add('render', { prevSubject: true }, (subject: EditorJS, data: OutputData) => {
|
||||
|
|
@ -133,9 +132,8 @@ Cypress.Commands.add('render', { prevSubject: true }, (subject: EditorJS, data:
|
|||
*
|
||||
* Usage
|
||||
* cy.get('[data-cy=editorjs]')
|
||||
* .find('.ce-paragraph')
|
||||
* .selectText('block te')
|
||||
*
|
||||
* .find('.ce-paragraph')
|
||||
* .selectText('block te')
|
||||
* @param text - text to select
|
||||
*/
|
||||
Cypress.Commands.add('selectText', {
|
||||
|
|
@ -162,9 +160,8 @@ Cypress.Commands.add('selectText', {
|
|||
*
|
||||
* Usage
|
||||
* cy.get('[data-cy=editorjs]')
|
||||
* .find('.ce-paragraph')
|
||||
* .selectTextByOffset([0, 5])
|
||||
*
|
||||
* .find('.ce-paragraph')
|
||||
* .selectTextByOffset([0, 5])
|
||||
* @param offset - offset to select
|
||||
*/
|
||||
Cypress.Commands.add('selectTextByOffset', {
|
||||
|
|
@ -190,9 +187,8 @@ Cypress.Commands.add('selectTextByOffset', {
|
|||
*
|
||||
* Usage
|
||||
* cy.get('[data-cy=editorjs]')
|
||||
* .find('.ce-paragraph')
|
||||
* .getLineWrapPositions()
|
||||
*
|
||||
* .find('.ce-paragraph')
|
||||
* .getLineWrapPositions()
|
||||
* @returns number[] - array of line wrap positions
|
||||
*/
|
||||
Cypress.Commands.add('getLineWrapPositions', {
|
||||
|
|
@ -249,7 +245,6 @@ Cypress.Commands.add('keydown', {
|
|||
* so real-world and Cypress behaviour were different.
|
||||
*
|
||||
* To make it work we need to trigger Cypress event with "eventConstructor: 'KeyboardEvent'",
|
||||
*
|
||||
* @see https://github.com/cypress-io/cypress/issues/5650
|
||||
* @see https://github.com/cypress-io/cypress/pull/8305/files
|
||||
*/
|
||||
|
|
@ -264,15 +259,17 @@ Cypress.Commands.add('keydown', {
|
|||
|
||||
/**
|
||||
* Extract content of pseudo element
|
||||
*
|
||||
* @example cy.get('element').getPseudoElementContent('::before').should('eq', 'my-test-string')
|
||||
*/
|
||||
Cypress.Commands.add('getPseudoElementContent', {
|
||||
prevSubject: true,
|
||||
}, (subject, pseudoElement: 'string') => {
|
||||
prevSubject: ['element'],
|
||||
}, (subject, pseudoElement: string) => {
|
||||
const win = subject[0].ownerDocument.defaultView;
|
||||
if (!win) {
|
||||
throw new Error('defaultView is null');
|
||||
}
|
||||
const computedStyle = win.getComputedStyle(subject[0], pseudoElement);
|
||||
const content = computedStyle.getPropertyValue('content');
|
||||
|
||||
return content.replace(/['"]/g, ''); // Remove quotes around the content
|
||||
return cy.wrap(content.replace(/['"]/g, '')); // Remove quotes around the content
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@ import '@cypress/code-coverage/support';
|
|||
// available to them because the supportFile is bundled and served
|
||||
// prior to any spec files loading
|
||||
|
||||
declare const chai: Chai.ChaiStatic;
|
||||
|
||||
import type PartialBlockMutationEvent from '../fixtures/types/PartialBlockMutationEvent';
|
||||
|
||||
/**
|
||||
* Chai plugin for checking if passed onChange method is called with an array of passed events
|
||||
*
|
||||
* @param _chai - Chai instance
|
||||
*/
|
||||
const beCalledWithBatchedEvents = (_chai): void => {
|
||||
const beCalledWithBatchedEvents = (_chai: typeof chai): void => {
|
||||
/**
|
||||
* Check if passed onChange method is called with an array of passed events
|
||||
*
|
||||
* @param expectedEvents - batched events to check
|
||||
*/
|
||||
function assertToBeCalledWithBatchedEvents(expectedEvents: PartialBlockMutationEvent[]): void {
|
||||
function assertToBeCalledWithBatchedEvents(this: Chai.AssertionStatic, expectedEvents: PartialBlockMutationEvent[]): void {
|
||||
/**
|
||||
* EditorJS API is passed as the first parameter of the onChange callback
|
||||
*/
|
||||
|
|
@ -29,7 +29,8 @@ const beCalledWithBatchedEvents = (_chai): void => {
|
|||
this.assert(
|
||||
$onChange.calledOnce,
|
||||
'expected #{this} to be called once',
|
||||
'expected #{this} to not be called once'
|
||||
'expected #{this} to not be called once',
|
||||
true
|
||||
);
|
||||
|
||||
this.assert(
|
||||
|
|
|
|||
4
test/cypress/support/index.d.ts
vendored
4
test/cypress/support/index.d.ts
vendored
|
|
@ -31,7 +31,7 @@ declare global {
|
|||
* @usage
|
||||
* cy.get('div').copy().then(data => {})
|
||||
*/
|
||||
copy(): Chainable<Subject>;
|
||||
copy(): Chainable<Record<string, string>>;
|
||||
|
||||
/**
|
||||
* Cut command to dispatch cut event on subject
|
||||
|
|
@ -39,7 +39,7 @@ declare global {
|
|||
* @usage
|
||||
* cy.get('div').cut().then(data => {})
|
||||
*/
|
||||
cut(): Chainable<Subject>;
|
||||
cut(): Chainable<Record<string, string>>;
|
||||
|
||||
/**
|
||||
* Calls EditorJS API render method
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import type EditorJS from '../../../../types/index';
|
|||
|
||||
/**
|
||||
* Creates Editor instance with list of Paragraph blocks of passed texts
|
||||
*
|
||||
* @param textBlocks - list of texts for Paragraph blocks
|
||||
* @param editorConfig - config to pass to the editor
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { nanoid } from 'nanoid';
|
|||
|
||||
/**
|
||||
* Creates a paragraph mock
|
||||
*
|
||||
* @param text - text for the paragraph
|
||||
* @returns paragraph mock
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,10 +9,17 @@ export const NESTED_EDITOR_ID = 'nested-editor';
|
|||
export default class NestedEditor implements BlockTool {
|
||||
private data: { text: string };
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value - The constructor options for the block tool
|
||||
*/
|
||||
constructor(value: BlockToolConstructorOptions) {
|
||||
this.data = value.data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public render(): HTMLDivElement {
|
||||
const editorEl = Object.assign(document.createElement('div'), {
|
||||
id: NESTED_EDITOR_ID,
|
||||
|
|
@ -25,6 +32,9 @@ export default class NestedEditor implements BlockTool {
|
|||
return editorEl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public save(): string {
|
||||
return this.data.text;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type EditorJS from '../../../../types';
|
||||
import type { API, BlockMutationEvent, OutputData } from '../../../../types';
|
||||
import { BlockChangedMutationType } from '../../../../types/events/block/BlockChanged';
|
||||
|
||||
/**
|
||||
|
|
@ -25,12 +26,11 @@ describe('BlockAPI', () => {
|
|||
|
||||
/**
|
||||
* Creates Editor instance
|
||||
*
|
||||
* @param [data] - data to render
|
||||
*/
|
||||
function createEditor(data = undefined): void {
|
||||
function createEditor(data?: OutputData): void {
|
||||
const config = {
|
||||
onChange: (api, event): void => {
|
||||
onChange: (_api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
data,
|
||||
|
|
@ -55,6 +55,10 @@ describe('BlockAPI', () => {
|
|||
.then(async (editor) => {
|
||||
const block = editor.blocks.getById(firstBlock.id);
|
||||
|
||||
if (!block) {
|
||||
throw new Error(`Block with id ${firstBlock.id} not found`);
|
||||
}
|
||||
|
||||
block.dispatchChange();
|
||||
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type EditorJS from '../../../../types/index';
|
||||
import type { ConversionConfig, ToolboxConfig, ToolConfig } from '../../../../types';
|
||||
import type { ConversionConfig, ToolboxConfig, ToolConfig, API, BlockAPI } from '../../../../types';
|
||||
import type { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
|
||||
import ToolMock, { type MockToolData } from '../../fixtures/tools/ToolMock';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ describe('api.blocks', () => {
|
|||
const block = editor.blocks.getById(firstBlock.id);
|
||||
|
||||
expect(block).not.to.be.undefined;
|
||||
expect(block.id).to.be.eq(firstBlock.id);
|
||||
expect(block?.id).to.be.eq(firstBlock.id);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -108,18 +109,17 @@ describe('api.blocks', () => {
|
|||
* Example Tune Class
|
||||
*/
|
||||
class ExampleTune {
|
||||
protected data: object;
|
||||
protected data: BlockTuneData | null | undefined;
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param config - Block Tune config
|
||||
*/
|
||||
constructor({ data }) {
|
||||
constructor({ data }: { api: API; config?: ToolConfig; block: BlockAPI; data: BlockTuneData }) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell editor.js that this Tool is a Block Tune
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public static get isTune(): boolean {
|
||||
|
|
@ -128,10 +128,9 @@ describe('api.blocks', () => {
|
|||
|
||||
/**
|
||||
* Create Tunes controls wrapper that will be appended to the Block Tunes panel
|
||||
*
|
||||
* @returns {Element}
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
public render(): Element {
|
||||
public render(): HTMLElement {
|
||||
return document.createElement('div');
|
||||
}
|
||||
|
||||
|
|
@ -144,11 +143,10 @@ describe('api.blocks', () => {
|
|||
|
||||
/**
|
||||
* Returns Tune state
|
||||
*
|
||||
* @returns {string}
|
||||
* @returns {BlockTuneData}
|
||||
*/
|
||||
public save(): object | string {
|
||||
return this.data || '';
|
||||
public save(): BlockTuneData {
|
||||
return this.data ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +176,12 @@ describe('api.blocks', () => {
|
|||
// Check if it is updated
|
||||
cy.get<EditorJS>('@editorInstance')
|
||||
.then(async (editor) => {
|
||||
await editor.blocks.update(editor.blocks.getBlockByIndex(0).id, null, {
|
||||
const block = editor.blocks.getBlockByIndex(0);
|
||||
|
||||
if (!block) {
|
||||
throw new Error('Block at index 0 not found');
|
||||
}
|
||||
await editor.blocks.update(block.id, undefined, {
|
||||
exampleTune: 'test',
|
||||
});
|
||||
const data = await editor.save();
|
||||
|
|
@ -481,6 +484,8 @@ describe('api.blocks', () => {
|
|||
export: (data) => data,
|
||||
/**
|
||||
* Passed config should be returned
|
||||
* @param _content - The content string (unused in this implementation)
|
||||
* @param config - The tool configuration object
|
||||
*/
|
||||
import: (_content, config) => {
|
||||
return { text: JSON.stringify(config) };
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ describe('Caret API', () => {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (!selection) {
|
||||
throw new Error('Selection not found');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -65,6 +69,10 @@ describe('Caret API', () => {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (!selection) {
|
||||
throw new Error('Selection not found');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -83,6 +91,10 @@ describe('Caret API', () => {
|
|||
cy.get<EditorJS>('@editorInstance')
|
||||
.then(async (editor) => {
|
||||
const block = editor.blocks.getById(paragraphDataMock.id);
|
||||
|
||||
if (!block) {
|
||||
throw new Error('Block not found');
|
||||
}
|
||||
const returnedValue = editor.caret.setToBlock(block);
|
||||
|
||||
/**
|
||||
|
|
@ -91,6 +103,10 @@ describe('Caret API', () => {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (!selection) {
|
||||
throw new Error('Selection not found');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ describe('api.toolbar', () => {
|
|||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (this.editorInstance) {
|
||||
if (this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,12 +90,12 @@ describe('Editor Tools Api', () => {
|
|||
cy.get('[data-cy=editorjs]')
|
||||
.get('.ce-popover-item[data-item-name=testTool]')
|
||||
.first()
|
||||
.should('contain.text', TestTool.toolbox[0].title);
|
||||
.should('contain.text', (TestTool.toolbox as ToolboxConfigEntry[])[0].title);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('.ce-popover-item[data-item-name=testTool]')
|
||||
.last()
|
||||
.should('contain.text', TestTool.toolbox[1].title);
|
||||
.should('contain.text', (TestTool.toolbox as ToolboxConfigEntry[])[1].title);
|
||||
});
|
||||
|
||||
it('should insert block with overridden data on entry click in case toolbox entry provides data overrides', () => {
|
||||
|
|
@ -114,10 +114,9 @@ describe('Editor Tools Api', () => {
|
|||
|
||||
/**
|
||||
* Tool constructor
|
||||
*
|
||||
* @param data - previously saved data
|
||||
*/
|
||||
constructor({ data }) {
|
||||
constructor({ data }: { data: { testProp: string } }) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +146,6 @@ describe('Editor Tools Api', () => {
|
|||
|
||||
/**
|
||||
* Extracts Tool's data from the view
|
||||
*
|
||||
* @param el - tool view
|
||||
*/
|
||||
public save(el: HTMLElement): BlockToolData {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { ToolboxConfig } from '../../../types';
|
|||
describe('Editor i18n', () => {
|
||||
context('Toolbox', () => {
|
||||
it('should translate tool title in a toolbox', function () {
|
||||
if (this && this.editorInstance) {
|
||||
if (this != null && this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
const toolNamesDictionary = {
|
||||
|
|
@ -36,7 +36,7 @@ describe('Editor i18n', () => {
|
|||
});
|
||||
|
||||
it('should translate titles of toolbox entries', function () {
|
||||
if (this && this.editorInstance) {
|
||||
if (this != null && this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
const toolNamesDictionary = {
|
||||
|
|
@ -96,7 +96,7 @@ describe('Editor i18n', () => {
|
|||
});
|
||||
|
||||
it('should use capitalized tool name as translation key if toolbox title is missing', function () {
|
||||
if (this && this.editorInstance) {
|
||||
if (this != null && this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ describe('Editor basic initialization', () => {
|
|||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (this.editorInstance) {
|
||||
if (this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
|
@ -30,8 +30,8 @@ describe('Editor basic initialization', () => {
|
|||
|
||||
describe('Configuration', () => {
|
||||
describe('readOnly', () => {
|
||||
beforeEach(() => {
|
||||
if (this && this.editorInstance) {
|
||||
beforeEach(function () {
|
||||
if (this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -52,6 +55,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -82,6 +88,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -112,6 +121,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -143,6 +155,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -174,6 +189,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -266,6 +284,9 @@ describe('Arrow Left', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -57,6 +60,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -89,6 +95,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -121,6 +130,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -154,6 +166,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -187,6 +202,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -279,6 +297,9 @@ describe('Arrow Right', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
|
|||
|
|
@ -239,6 +239,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@firstInput').should(($div) => {
|
||||
|
|
@ -330,13 +333,16 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('.ce-paragraph')
|
||||
.should(($block) => {
|
||||
expect($block[0].contains(range.startContainer)).to.be.true;
|
||||
expect(range.startOffset).to.be.eq($block[0].textContent.length);
|
||||
expect(range.startOffset).to.be.eq($block[0].textContent?.length ?? 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -395,6 +401,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -461,6 +470,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -526,6 +538,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -607,6 +622,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@firstBlock').should(($div) => {
|
||||
|
|
@ -690,6 +708,9 @@ describe('Backspace keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@firstBlock').should(($div) => {
|
||||
|
|
@ -700,7 +721,7 @@ describe('Backspace keydown', function () {
|
|||
|
||||
describe('at the start of the first Block', function () {
|
||||
it('should do nothing if Block is not empty', function () {
|
||||
createEditorWithTextBlocks(['The only block. Not empty']);
|
||||
createEditorWithTextBlocks([ 'The only block. Not empty' ]);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('.ce-paragraph')
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ describe('Delete keydown', function () {
|
|||
* - Firefox merge blocks and with whitespace - "1 2"
|
||||
*
|
||||
* So, we have to check both variants.
|
||||
*
|
||||
* @todo remove this check after fixing the Firefox merge behaviour
|
||||
*/
|
||||
.should(($block) => {
|
||||
|
|
@ -232,6 +231,9 @@ describe('Delete keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@secondInput').should(($div) => {
|
||||
|
|
@ -323,6 +325,9 @@ describe('Delete keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -387,6 +392,9 @@ describe('Delete keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
|
|
@ -466,6 +474,9 @@ describe('Delete keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@secondBlock').should(($div) => {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ describe('Enter keydown', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('Selection is null or has no ranges');
|
||||
}
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
cy.get('@lastBlock').should(($block) => {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,13 @@ describe('Tab keydown', function () {
|
|||
.last()
|
||||
.then(($secondBlock) => {
|
||||
const editorWindow = $secondBlock.get(0).ownerDocument.defaultView;
|
||||
if (!editorWindow) {
|
||||
throw new Error('Window is not available');
|
||||
}
|
||||
const selection = editorWindow.getSelection();
|
||||
if (!selection) {
|
||||
throw new Error('Selection is not available');
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
|
|
@ -127,7 +133,13 @@ describe('Tab keydown', function () {
|
|||
.last()
|
||||
.then(($secondInput) => {
|
||||
const editorWindow = $secondInput.get(0).ownerDocument.defaultView;
|
||||
if (!editorWindow) {
|
||||
throw new Error('Window is not available');
|
||||
}
|
||||
const selection = editorWindow.getSelection();
|
||||
if (!selection) {
|
||||
throw new Error('Selection is not available');
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
|
|
@ -240,7 +252,13 @@ describe('Shift+Tab keydown', function () {
|
|||
.first()
|
||||
.then(($firstBlock) => {
|
||||
const editorWindow = $firstBlock.get(0).ownerDocument.defaultView;
|
||||
if (!editorWindow) {
|
||||
throw new Error('Window is not available');
|
||||
}
|
||||
const selection = editorWindow.getSelection();
|
||||
if (!selection) {
|
||||
throw new Error('Selection is not available');
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
|
|
@ -289,7 +307,13 @@ describe('Shift+Tab keydown', function () {
|
|||
.first()
|
||||
.then(($firstInput) => {
|
||||
const editorWindow = $firstInput.get(0).ownerDocument.defaultView;
|
||||
if (!editorWindow) {
|
||||
throw new Error('Window is not available');
|
||||
}
|
||||
const selection = editorWindow.getSelection();
|
||||
if (!selection) {
|
||||
throw new Error('Selection is not available');
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Header from '@editorjs/header';
|
||||
import NestedEditor, { NESTED_EDITOR_ID } from '../../support/utils/nestedEditorInstance';
|
||||
import type { MenuConfig } from '@/types/tools';
|
||||
import type { MenuConfig, ToolConstructable } from '@/types/tools';
|
||||
|
||||
describe('Inline Toolbar', () => {
|
||||
it('should appear aligned with left coord of selection rect', () => {
|
||||
|
|
@ -25,12 +25,21 @@ describe('Inline Toolbar', () => {
|
|||
.should('be.visible')
|
||||
.then(($toolbar) => {
|
||||
const editorWindow = $toolbar.get(0).ownerDocument.defaultView;
|
||||
|
||||
if (!editorWindow) {
|
||||
throw new Error('Unable to access window from toolbar element');
|
||||
}
|
||||
|
||||
const selection = editorWindow.getSelection();
|
||||
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
throw new Error('No selection available');
|
||||
}
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
const rect = range.getBoundingClientRect();
|
||||
|
||||
expect($toolbar.offset().left).to.be.closeTo(rect.left, 1);
|
||||
expect($toolbar.offset()?.left).to.be.closeTo(rect.left, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -70,10 +79,17 @@ describe('Inline Toolbar', () => {
|
|||
.then(($blockWrapper) => {
|
||||
const blockWrapperRect = $blockWrapper.get(0).getBoundingClientRect();
|
||||
|
||||
const toolbarOffset = $toolbar.offset();
|
||||
const toolbarWidth = $toolbar.width();
|
||||
|
||||
if (!toolbarOffset || toolbarWidth === undefined) {
|
||||
throw new Error('Unable to get toolbar offset or width');
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar should be aligned with right side of text column
|
||||
*/
|
||||
expect($toolbar.offset().left + $toolbar.width()).to.closeTo(blockWrapperRect.right, 10);
|
||||
expect(toolbarOffset.left + toolbarWidth).to.closeTo(blockWrapperRect.right, 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -82,7 +98,7 @@ describe('Inline Toolbar', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as ToolConstructable,
|
||||
inlineToolbar: ['bold', 'testTool'],
|
||||
},
|
||||
testTool: {
|
||||
|
|
@ -90,6 +106,10 @@ describe('Inline Toolbar', () => {
|
|||
public static isInline = true;
|
||||
public static isReadOnlySupported = true;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public constructor() {
|
||||
// Constructor required for InlineToolConstructable
|
||||
}
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public render(): MenuConfig {
|
||||
return {
|
||||
title: 'Test Tool',
|
||||
|
|
@ -98,7 +118,7 @@ describe('Inline Toolbar', () => {
|
|||
onActivate: () => {},
|
||||
};
|
||||
}
|
||||
},
|
||||
} as unknown as ToolConstructable,
|
||||
},
|
||||
},
|
||||
readOnly: true,
|
||||
|
|
@ -154,7 +174,13 @@ describe('Inline Toolbar', () => {
|
|||
doc.body.appendChild(form);
|
||||
|
||||
/* Move editor to form */
|
||||
form.appendChild(doc.getElementById('editorjs'));
|
||||
const editorElement = doc.getElementById('editorjs');
|
||||
|
||||
if (!editorElement) {
|
||||
throw new Error('Editor element not found');
|
||||
}
|
||||
|
||||
form.appendChild(editorElement);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('.ce-paragraph')
|
||||
|
|
@ -172,7 +198,7 @@ describe('Inline Toolbar', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as ToolConstructable,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
@ -207,9 +233,13 @@ describe('Inline Toolbar', () => {
|
|||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
|
||||
expect(selection.rangeCount).to.be.equal(1);
|
||||
expect(selection?.rangeCount).to.be.equal(1);
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
const range = selection?.getRangeAt(0);
|
||||
|
||||
if (!range) {
|
||||
throw new Error('No range available');
|
||||
}
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('.ce-header')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import ToolMock from '../../fixtures/tools/ToolMock';
|
||||
import ToolMock, { type MockToolData } from '../../fixtures/tools/ToolMock';
|
||||
import type EditorJS from '../../../../types/index';
|
||||
import type { BlockToolConstructorOptions } from '../../../../types';
|
||||
|
||||
describe('Renderer module', function () {
|
||||
it('should not cause onChange firing during initial rendering', function () {
|
||||
|
|
@ -90,7 +91,7 @@ describe('Renderer module', function () {
|
|||
/**
|
||||
* @param options - tool options
|
||||
*/
|
||||
constructor(options) {
|
||||
constructor(options: BlockToolConstructorOptions<MockToolData>) {
|
||||
super(options);
|
||||
throw new Error('Tool error');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ describe('Tools module', () => {
|
|||
|
||||
/**
|
||||
* Construct Tools module for testing purposes
|
||||
*
|
||||
* @param config - Editor config
|
||||
*/
|
||||
function constructModule(config: EditorConfig = defaultConfig): Tools {
|
||||
|
|
@ -238,53 +237,53 @@ describe('Tools module', () => {
|
|||
it('Block Tools should contain default tunes if no settings is specified', () => {
|
||||
const tool = module.blockTools.get('blockToolWithoutSettings');
|
||||
|
||||
expect(tool.tunes.has('delete')).to.be.true;
|
||||
expect(tool.tunes.has('moveUp')).to.be.true;
|
||||
expect(tool.tunes.has('moveDown')).to.be.true;
|
||||
expect(tool?.tunes.has('delete')).to.be.true;
|
||||
expect(tool?.tunes.has('moveUp')).to.be.true;
|
||||
expect(tool?.tunes.has('moveDown')).to.be.true;
|
||||
});
|
||||
|
||||
it('Block Tools should contain default tunes', () => {
|
||||
const tool = module.blockTools.get('blockTool');
|
||||
|
||||
expect(tool.tunes.has('delete')).to.be.true;
|
||||
expect(tool.tunes.has('moveUp')).to.be.true;
|
||||
expect(tool.tunes.has('moveDown')).to.be.true;
|
||||
expect(tool?.tunes.has('delete')).to.be.true;
|
||||
expect(tool?.tunes.has('moveUp')).to.be.true;
|
||||
expect(tool?.tunes.has('moveDown')).to.be.true;
|
||||
});
|
||||
|
||||
it('Block Tools should contain tunes in correct order', () => {
|
||||
let tool = module.blockTools.get('blockTool');
|
||||
|
||||
expect(tool.tunes.has('blockTune')).to.be.true;
|
||||
expect(tool.tunes.has('blockTune2')).to.be.true;
|
||||
expect(Array.from(tool.tunes.keys())).to.be.deep.eq(['blockTune2', 'blockTune', 'moveUp', 'delete', 'moveDown']);
|
||||
expect(tool?.tunes.has('blockTune')).to.be.true;
|
||||
expect(tool?.tunes.has('blockTune2')).to.be.true;
|
||||
expect(Array.from(tool?.tunes.keys() ?? [])).to.be.deep.eq(['blockTune2', 'blockTune', 'moveUp', 'delete', 'moveDown']);
|
||||
|
||||
tool = module.blockTools.get('withSuccessfulPrepare');
|
||||
|
||||
expect(tool.tunes.has('blockTune')).to.be.false;
|
||||
expect(tool.tunes.has('blockTune2')).to.be.true;
|
||||
expect(tool?.tunes.has('blockTune')).to.be.false;
|
||||
expect(tool?.tunes.has('blockTune2')).to.be.true;
|
||||
|
||||
tool = module.blockTools.get('withoutPrepare');
|
||||
|
||||
expect(tool.tunes.has('blockTune')).to.be.false;
|
||||
expect(tool.tunes.has('blockTune2')).to.be.false;
|
||||
expect(tool?.tunes.has('blockTune')).to.be.false;
|
||||
expect(tool?.tunes.has('blockTune2')).to.be.false;
|
||||
});
|
||||
|
||||
it('Block Tools should contain inline tools in correct order', () => {
|
||||
let tool = module.blockTools.get('blockTool');
|
||||
|
||||
expect(tool.inlineTools.has('inlineTool')).to.be.true;
|
||||
expect(tool.inlineTools.has('inlineTool2')).to.be.true;
|
||||
expect(Array.from(tool.inlineTools.keys())).to.be.deep.eq(['inlineTool2', 'inlineTool']);
|
||||
expect(tool?.inlineTools.has('inlineTool')).to.be.true;
|
||||
expect(tool?.inlineTools.has('inlineTool2')).to.be.true;
|
||||
expect(Array.from(tool?.inlineTools.keys() ?? [])).to.be.deep.eq(['inlineTool2', 'inlineTool']);
|
||||
|
||||
tool = module.blockTools.get('withSuccessfulPrepare');
|
||||
|
||||
expect(tool.inlineTools.has('inlineTool')).to.be.false;
|
||||
expect(tool.inlineTools.has('inlineTool2')).to.be.true;
|
||||
expect(tool?.inlineTools.has('inlineTool')).to.be.false;
|
||||
expect(tool?.inlineTools.has('inlineTool2')).to.be.true;
|
||||
|
||||
tool = module.blockTools.get('withoutPrepare');
|
||||
|
||||
expect(tool.inlineTools.has('inlineTool')).to.be.false;
|
||||
expect(tool.inlineTools.has('inlineTool2')).to.be.false;
|
||||
expect(tool?.inlineTools.has('inlineTool')).to.be.false;
|
||||
expect(tool?.inlineTools.has('inlineTool2')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { BlockChangedMutationType } from '../../../types/events/block/BlockChang
|
|||
import { BlockRemovedMutationType } from '../../../types/events/block/BlockRemoved';
|
||||
import { BlockMovedMutationType } from '../../../types/events/block/BlockMoved';
|
||||
import type EditorJS from '../../../types/index';
|
||||
import type { API, OutputData, BlockMutationEvent } from '../../../types/index';
|
||||
import { modificationsObserverBatchTimeout } from '../../../src/components/constants';
|
||||
|
||||
/**
|
||||
|
|
@ -22,21 +23,20 @@ const EditorJSApiMock = Cypress.sinon.match.any;
|
|||
describe('onChange callback', () => {
|
||||
/**
|
||||
* Creates Editor instance
|
||||
*
|
||||
* @param blocks - list of blocks to prefill the editor
|
||||
*/
|
||||
function createEditor(blocks = null): void {
|
||||
function createEditor(blocks?: OutputData['blocks']): void {
|
||||
const config = {
|
||||
tools: {
|
||||
header: Header,
|
||||
code: Code,
|
||||
},
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
data: blocks ? {
|
||||
blocks,
|
||||
} : null,
|
||||
} : undefined,
|
||||
};
|
||||
|
||||
cy.spy(config, 'onChange').as('onChange');
|
||||
|
|
@ -46,23 +46,22 @@ describe('onChange callback', () => {
|
|||
|
||||
/**
|
||||
* Creates Editor instance with save inside the onChange event.
|
||||
*
|
||||
* @param blocks - list of blocks to prefill the editor
|
||||
*/
|
||||
function createEditorWithSave(blocks = null): void {
|
||||
function createEditorWithSave(blocks?: OutputData['blocks']): void {
|
||||
const config = {
|
||||
tools: {
|
||||
header: Header,
|
||||
code: Code,
|
||||
delimiter: Delimiter,
|
||||
},
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
api.saver.save();
|
||||
},
|
||||
data: blocks ? {
|
||||
blocks,
|
||||
} : null,
|
||||
} : undefined,
|
||||
};
|
||||
|
||||
cy.spy(config, 'onChange').as('onChange');
|
||||
|
|
@ -515,7 +514,7 @@ describe('onChange callback', () => {
|
|||
tools: {
|
||||
testTool: ToolWithMutationFreeAttribute,
|
||||
},
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
data: {
|
||||
|
|
@ -582,7 +581,7 @@ describe('onChange callback', () => {
|
|||
tools: {
|
||||
testTool: ToolWithMutationFreeAttribute,
|
||||
},
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
data: {
|
||||
|
|
@ -652,7 +651,7 @@ describe('onChange callback', () => {
|
|||
tools: {
|
||||
testTool: ToolWithMutationFreeAttribute,
|
||||
},
|
||||
onChange: function (api, event) {
|
||||
onChange: function (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void {
|
||||
console.log('something changed!!!!!!!!', event);
|
||||
},
|
||||
data: {
|
||||
|
|
@ -767,7 +766,7 @@ describe('onChange callback', () => {
|
|||
block,
|
||||
],
|
||||
},
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
};
|
||||
|
|
@ -852,7 +851,7 @@ describe('onChange callback', () => {
|
|||
it('should not be called when editor is initialized with readOnly mode', () => {
|
||||
const config = {
|
||||
readOnly: true,
|
||||
onChange: (api, event): void => {
|
||||
onChange: (api: API, event: BlockMutationEvent | BlockMutationEvent[]): void => {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import type EditorJS from '../../../types';
|
|||
describe('ReadOnly API spec', () => {
|
||||
/**
|
||||
* Creates the new editor instance
|
||||
*
|
||||
* @param config - Editor Config
|
||||
*/
|
||||
function createEditor(config?: EditorConfig): void {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ describe('Blocks selection', () => {
|
|||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (this.editorInstance) {
|
||||
if (this.editorInstance != null) {
|
||||
this.editorInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ describe('BlockTool', () => {
|
|||
|
||||
public static isReadOnlySupported = true;
|
||||
|
||||
public static reset;
|
||||
public static prepare;
|
||||
public static reset?: () => void | Promise<void>;
|
||||
public static prepare?: (data: {toolName: string, config: ToolSettings}) => void | Promise<void>;
|
||||
|
||||
public static shortcut = 'CTRL+N';
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ describe('BlockTool', () => {
|
|||
public config: ToolSettings;
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
constructor({ data, block, readOnly, api, config }) {
|
||||
constructor({ data, block, readOnly, api, config }: { data: BlockToolData; block: object; readOnly: boolean; api: object; config: ToolSettings }) {
|
||||
this.data = data;
|
||||
this.block = block;
|
||||
this.readonly = readOnly;
|
||||
|
|
@ -157,8 +157,8 @@ describe('BlockTool', () => {
|
|||
|
||||
// tslint:disable-next-line:forin
|
||||
for (const key in expected) {
|
||||
expected[key] = {
|
||||
...expected[key],
|
||||
(expected as Record<string, Record<string, boolean>>)[key] = {
|
||||
...expected[key as keyof typeof expected],
|
||||
b: true,
|
||||
};
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ describe('BlockTool', () => {
|
|||
const expected = userDefinedToolboxConfig.map((item, i) => {
|
||||
const toolToolboxEntry = toolboxEntries[i];
|
||||
|
||||
if (toolToolboxEntry) {
|
||||
if (toolToolboxEntry !== undefined) {
|
||||
return {
|
||||
...toolToolboxEntry,
|
||||
...item,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ describe('BlockTune', () => {
|
|||
const options = {
|
||||
name: 'blockTune',
|
||||
constructable: class {
|
||||
public static reset;
|
||||
public static prepare;
|
||||
public static reset?: () => void | Promise<void>;
|
||||
public static prepare?: (data: {toolName: string, config: ToolSettings}) => void | Promise<void>;
|
||||
|
||||
public api: object;
|
||||
public config: ToolSettings;
|
||||
|
|
@ -21,7 +21,7 @@ describe('BlockTune', () => {
|
|||
public block: object;
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
constructor({ api, config, block, data }) {
|
||||
constructor({ api, config, block, data }: { api: object; config: ToolSettings; block: object; data: BlockTuneData }) {
|
||||
this.api = api;
|
||||
this.config = config;
|
||||
this.block = block;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
import type { ToolSettings } from '@/types';
|
||||
import type { ToolSettings, ToolConfig } from '@/types';
|
||||
import { ToolType } from '@/types/tools/adapters/tool-type';
|
||||
import InlineToolAdapter from '../../../../src/components/tools/inline';
|
||||
|
||||
|
|
@ -17,8 +17,8 @@ describe('InlineTool', () => {
|
|||
|
||||
public static title = 'Title';
|
||||
|
||||
public static reset;
|
||||
public static prepare;
|
||||
public static reset?: () => void | Promise<void>;
|
||||
public static prepare?: (data: {toolName: string, config: ToolConfig}) => void | Promise<void>;
|
||||
|
||||
public static shortcut = 'CTRL+N';
|
||||
public static isReadOnlySupported = true;
|
||||
|
|
@ -31,7 +31,7 @@ describe('InlineTool', () => {
|
|||
* @param options.api - EditorAPI
|
||||
* @param options.config - tool config
|
||||
*/
|
||||
constructor({ api, config }) {
|
||||
constructor({ api, config }: { api: object; config: ToolSettings }) {
|
||||
this.api = api;
|
||||
this.config = config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const FakeBlockTune = {
|
|||
* Unit tests for ToolsCollection class
|
||||
*/
|
||||
describe('ToolsCollection', (): void => {
|
||||
let collection;
|
||||
let collection: ToolsCollection;
|
||||
|
||||
/**
|
||||
* Mock for Tools in collection
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import InlineToolAdapter from '../../../../src/components/tools/inline';
|
|||
import BlockToolAdapter from '../../../../src/components/tools/block';
|
||||
import BlockTuneAdapter from '../../../../src/components/tools/tune';
|
||||
import Paragraph from '@editorjs/paragraph';
|
||||
import type { BlockToolConstructable } from '../../../../types';
|
||||
|
||||
describe('ToolsFactory', (): void => {
|
||||
let factory;
|
||||
let factory: ToolsFactory;
|
||||
const paragraphClass = Paragraph as unknown as BlockToolConstructable;
|
||||
const config = {
|
||||
paragraph: {
|
||||
class: Paragraph,
|
||||
class: paragraphClass,
|
||||
},
|
||||
link: {
|
||||
class: LinkInlineTool,
|
||||
|
|
|
|||
|
|
@ -33,9 +33,13 @@ describe('BlockTunes', function () {
|
|||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
|
||||
expect(selection.rangeCount).to.be.equal(1);
|
||||
expect(selection?.rangeCount).to.be.equal(1);
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
const range = selection?.getRangeAt(0);
|
||||
|
||||
if (!range) {
|
||||
throw new Error('Range is undefined');
|
||||
}
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('[data-cy="block-tunes"] .cdx-search-field')
|
||||
|
|
@ -369,7 +373,11 @@ describe('BlockTunes', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
const range = selection.getRangeAt(0);
|
||||
const range = selection?.getRangeAt(0);
|
||||
|
||||
if (!range) {
|
||||
throw new Error('Range is undefined');
|
||||
}
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('.ce-header')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Header from '@editorjs/header';
|
||||
import type { InlineTool, MenuConfig } from '../../../../types/tools';
|
||||
import type { InlineTool, InlineToolConstructorOptions, MenuConfig, ToolConstructable } from '../../../../types/tools';
|
||||
import { createEditorWithTextBlocks } from '../../support/utils/createEditorWithTextBlocks';
|
||||
|
||||
describe('Inline Toolbar', () => {
|
||||
|
|
@ -8,7 +8,7 @@ describe('Inline Toolbar', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as ToolConstructable,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
@ -46,14 +46,18 @@ describe('Inline Toolbar', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as ToolConstructable,
|
||||
inlineToolbar: ['bold', 'testTool', 'link'],
|
||||
|
||||
},
|
||||
testTool: {
|
||||
class: class {
|
||||
class: class TestTool {
|
||||
public static isInline = true;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public constructor(_config: InlineToolConstructorOptions) {
|
||||
// Constructor required by InlineToolConstructable
|
||||
}
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public render(): MenuConfig {
|
||||
return {
|
||||
icon: 'n',
|
||||
|
|
@ -71,7 +75,7 @@ describe('Inline Toolbar', () => {
|
|||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
} as unknown as ToolConstructable,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
@ -115,14 +119,18 @@ describe('Inline Toolbar', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as ToolConstructable,
|
||||
inlineToolbar: ['bold', 'testTool'],
|
||||
|
||||
},
|
||||
testTool: {
|
||||
class: class {
|
||||
class: class TestTool {
|
||||
public static isInline = true;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public constructor(_config: InlineToolConstructorOptions) {
|
||||
// Constructor required by InlineToolConstructable
|
||||
}
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
public render(): MenuConfig {
|
||||
return {
|
||||
icon: 'n',
|
||||
|
|
@ -140,7 +148,7 @@ describe('Inline Toolbar', () => {
|
|||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
} as unknown as ToolConstructable,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,11 @@ describe('Toolbox', function () {
|
|||
cy.window()
|
||||
.then((window) => {
|
||||
const selection = window.getSelection();
|
||||
const range = selection.getRangeAt(0);
|
||||
const range = selection?.getRangeAt(0);
|
||||
|
||||
if (!range) {
|
||||
throw new Error('Selection range is not available');
|
||||
}
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find(`.ce-block[data-id=${blocks[0].id}]`)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { PopoverDesktop as Popover, PopoverItemType } from '../../../../src/components/utils/popover';
|
||||
import type { PopoverItemParams } from '@/types/utils/popover';
|
||||
import type { MenuConfig } from '../../../../types/tools';
|
||||
import type { BlockToolConstructable } from '../../../../types/tools';
|
||||
import Header from '@editorjs/header';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
|
|
@ -975,7 +976,8 @@ describe('Popover', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as BlockToolConstructable,
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
@ -1020,7 +1022,8 @@ describe('Popover', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as BlockToolConstructable,
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
@ -1080,7 +1083,8 @@ describe('Popover', () => {
|
|||
cy.createEditor({
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
class: Header as unknown as BlockToolConstructable,
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
|
|
|
|||
2
types/api/blocks.d.ts
vendored
2
types/api/blocks.d.ts
vendored
|
|
@ -70,7 +70,7 @@ export interface Blocks {
|
|||
/**
|
||||
* Returns the index of Block by id;
|
||||
*/
|
||||
getBlockIndex(blockId: string): number;
|
||||
getBlockIndex(blockId: string): number | undefined;
|
||||
|
||||
/**
|
||||
* Get Block API object by html element
|
||||
|
|
|
|||
5
types/block-tunes/block-tune.d.ts
vendored
5
types/block-tunes/block-tune.d.ts
vendored
|
|
@ -45,6 +45,11 @@ export interface BlockTuneConstructable {
|
|||
*/
|
||||
sanitize?: SanitizerConfig;
|
||||
|
||||
/**
|
||||
* Shortcut for Tool
|
||||
*/
|
||||
shortcut?: string;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
|
|
|
|||
5
types/tools/tool.d.ts
vendored
5
types/tools/tool.d.ts
vendored
|
|
@ -41,6 +41,11 @@ export interface BaseToolConstructable {
|
|||
*/
|
||||
sanitize?: SanitizerConfig;
|
||||
|
||||
/**
|
||||
* Shortcut for Tool
|
||||
*/
|
||||
shortcut?: string;
|
||||
|
||||
/**
|
||||
* Title of Inline Tool.
|
||||
* @deprecated use {@link MenuConfig} item title instead
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue