editor.js/test/cypress/tests/modules/Tools.spec.ts

327 lines
10 KiB
TypeScript
Raw Normal View History

/* tslint:disable:max-classes-per-file */
/* eslint-disable @typescript-eslint/no-explicit-any, jsdoc/require-jsdoc */
import Tools from '../../../../src/components/modules/tools';
import { EditorConfig } from '../../../../types';
import BlockTool from '../../../../src/components/tools/block';
describe('Tools module', () => {
const defaultConfig = {
tools: {},
};
/**
* Construct Tools module for testing purposes
*
* @param config - Editor config
*/
function constructModule(config: EditorConfig = defaultConfig): Tools {
const module = new Tools({
config,
eventsDispatcher: {},
} as any);
const APIMethods = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
method(): void {},
};
/**
* Module state should be Editor modules, so we mock required ones only
*/
module.state = {
API: {
getMethodsForTool(): typeof APIMethods {
return APIMethods;
},
},
} as any;
return module;
}
context('.prepare()', () => {
it('should return Promise resolved to void', async () => {
const module = constructModule();
let err;
try {
await module.prepare();
} catch (e) {
err = e;
}
expect(err).to.be.undefined;
});
it('should throw an error if tools config is corrupted', async () => {
const module = constructModule({
tools: {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
corruptedTool: 'value',
},
});
let err;
try {
await module.prepare();
} catch (e) {
err = e;
}
expect(err).to.be.instanceOf(Error);
});
// eslint-disable-next-line cypress/no-async-tests
it('should call Tools prepare method with user config', async () => {
class WithSuccessfulPrepare {
// eslint-disable-next-line @typescript-eslint/no-empty-function
public static prepare = cy.stub();
}
const config = {
property: 'value',
};
const module = constructModule({
defaultBlock: 'withSuccessfulPrepare',
tools: {
withSuccessfulPrepare: {
class: WithSuccessfulPrepare as any,
config,
},
},
});
await module.prepare();
feat(toolbar): toolbar refactored and ui improved (#1815) * chore(block-tune-toggler): toggler moved to the left (draft) * toolbox ui updated * fixd caret jumpling, improved some styles * toolbar moving by block-hover - UI module triggers 'block-hovered' event - Toolbar uses 'block-hovered' for appearing - `currentBlock` setter added to the BlockManager - (reactangle-selection): the throttling added to the mousemove and scroll handlers - `getBlockIndex` method added to the Api - (api-blocks): toolbar moving logic removed from `blocks.move()` and `blocks.swap()` methods. Instead, MoveUp and MoveDown tunes uses Toolbar API * the dark-theme to the example-dev.html * positioning improved * fix(rectangle-selection): first click after RS does not clears selection state * toolbox position fixed * the toolbox module became a standalone class - Toolbox became a standalone class from the editor module. It can be accessed only via the owner (the Toolbar module) - (api.blocks) the insert() method now has the `replace` param. Also, it returns inserted Block API now. * new(api.listeners): `on()` now returns the listener id. The new `offById()` method added * fix bug with Tab pressing on hovered but not focused block * mobile version improved * upd example dev * small updaets * add nested-list * linting * (api.toolbar): `toggleBlockSettings` now fires toggling event with the same state * EventDispatcher used instead of callbacks for the Toolbox * UIApi added * fix ci * git submodules removed from the ci flow * add paragraph submodule to the ci flow * Update CHANGELOG.md * Update package.json * use ubuntu-latest for chrome ci
2021-11-24 19:14:24 +01:00
expect(WithSuccessfulPrepare.prepare).to.be.calledWithExactly({
toolName: 'withSuccessfulPrepare',
config,
});
});
});
context('collection accessors', () => {
let module: Tools;
beforeEach(async () => {
module = constructModule({
defaultBlock: 'withoutPrepare',
tools: {
withSuccessfulPrepare: {
class: class {
// eslint-disable-next-line @typescript-eslint/no-empty-function
public static prepare(): void {}
} as any,
inlineToolbar: [ 'inlineTool2' ],
tunes: [ 'blockTune2' ],
},
withFailedPrepare: class {
public static prepare(): void {
throw new Error();
}
} as any,
withoutPrepare: {
class: class {} as any,
inlineToolbar: false,
tunes: false,
},
blockTool: {
class: class {} as any,
inlineToolbar: true,
},
blockToolWithoutSettings: class {} as any,
inlineTool: class {
public static isInline = true;
// eslint-disable-next-line @typescript-eslint/no-empty-function
public render(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public surround(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public checkState(): void {}
} as any,
inlineTool2: class {
public static isInline = true;
// eslint-disable-next-line @typescript-eslint/no-empty-function
public render(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public surround(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public checkState(): void {}
} as any,
/**
* This tool will be unavailable as it doesn't have required methods
*/
unavailableInlineTool: class {
public static isInline = true;
} as any,
blockTune: class {
public static isTune = true;
} as any,
blockTune2: class {
public static isTune = true;
} as any,
unavailableBlockTune: class {
public static isTune = true;
public static prepare(): void {
throw new Error();
}
} as any,
},
inlineToolbar: ['inlineTool2', 'inlineTool'],
tunes: ['blockTune2', 'blockTune'],
});
await module.prepare();
});
context('.available', () => {
it('should return Map instance', () => {
expect(module.available).to.be.instanceOf(Map);
});
it('should contain only ready to use Tools', () => {
expect(module.available.has('withSuccessfulPrepare')).to.be.true;
expect(module.available.has('withoutPrepare')).to.be.true;
expect(module.available.has('withFailedPrepare')).to.be.false;
expect(module.available.has('unavailableInlineTool')).to.be.false;
});
});
context('.unavailable', () => {
it('should return Map instance', () => {
expect(module.unavailable).to.be.instanceOf(Map);
});
it('should contain unavailable Tools', () => {
expect(module.unavailable.has('withSuccessfulPrepare')).to.be.false;
expect(module.unavailable.has('withoutPrepare')).to.be.false;
expect(module.unavailable.has('withFailedPrepare')).to.be.true;
expect(module.unavailable.has('unavailableInlineTool')).to.be.true;
});
});
context('.inlineTools', () => {
it('should return Map instance', () => {
expect(module.inlineTools).to.be.instanceOf(Map);
});
it('should contain only available Inline Tools', () => {
expect(module.inlineTools.has('inlineTool')).to.be.true;
expect(module.inlineTools.has('unavailableInlineTool')).to.be.false;
expect(Array.from(module.inlineTools.values()).every(tool => tool.isInline())).to.be.true;
});
});
context('.blockTools', () => {
it('should return Map instance', () => {
expect(module.blockTools).to.be.instanceOf(Map);
});
it('should contain only available Block Tools', () => {
expect(module.blockTools.has('withSuccessfulPrepare')).to.be.true;
expect(module.blockTools.has('withoutPrepare')).to.be.true;
expect(module.blockTools.has('withFailedPrepare')).to.be.false;
expect(Array.from(module.blockTools.values()).every(tool => tool.isBlock())).to.be.true;
});
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;
});
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;
});
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']);
tool = module.blockTools.get('withSuccessfulPrepare');
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;
});
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']);
tool = module.blockTools.get('withSuccessfulPrepare');
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;
});
});
context('.blockTunes', () => {
it('should return Map instance', () => {
expect(module.blockTunes).to.be.instanceOf(Map);
});
it('should contain only available Block Tunes', () => {
expect(module.blockTunes.has('blockTune')).to.be.true;
expect(module.blockTunes.has('unavailableBlockTune')).to.be.false;
expect(Array.from(module.blockTunes.values()).every(tool => tool.isTune())).to.be.true;
});
});
context('.internal', () => {
it('should return Map instance', () => {
expect(module.internal).to.be.instanceOf(Map);
});
it('should contain only internal tunes', () => {
expect(Array.from(module.internal.values()).every(tool => tool.isInternal)).to.be.true;
});
});
context('.defaultTools', () => {
/**
* @todo add check if user provided default tool is not Block Tool
*/
it('should return BlockTool instance', () => {
expect(module.defaultTool).to.be.instanceOf(BlockTool);
});
it('should return default Tool', () => {
expect(module.defaultTool.isDefault).to.be.true;
});
});
});
});