import { ToolboxConfig, BlockToolData, ToolboxConfigEntry } from '../../../../types'; import { TunesMenuConfig } from '../../../../types/tools'; /* eslint-disable @typescript-eslint/no-empty-function */ const ICON = ''; describe('Editor Tools Api', () => { context('Toolbox', () => { it('should render a toolbox entry for tool if configured', () => { /** * Tool with single toolbox entry configured */ class TestTool { /** * Returns toolbox config as list of entries */ public static get toolbox(): ToolboxConfigEntry { return { title: 'Entry 1', icon: ICON, }; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .should('have.length', 1); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool] .ce-popover__item-icon') .should('contain.html', TestTool.toolbox.icon); }); it('should render several toolbox entries for one tool if configured', () => { /** * Tool with several toolbox entries configured */ class TestTool { /** * Returns toolbox config as list of entries */ public static get toolbox(): ToolboxConfig { return [ { title: 'Entry 1', icon: ICON, }, { title: 'Entry 2', icon: ICON, }, ]; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .should('have.length', 2); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .first() .should('contain.text', TestTool.toolbox[0].title); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .last() .should('contain.text', TestTool.toolbox[1].title); }); it('should insert block with overriden data on entry click in case toolbox entry provides data overrides', () => { const text = 'Text'; const dataOverrides = { testProp: 'new value', }; /** * Tool with default data to be overriden */ class TestTool { private _data = { testProp: 'default value', } /** * Tool contructor * * @param data - previously saved data */ constructor({ data }) { this._data = data; } /** * Returns toolbox config as list of entries with overriden data */ public static get toolbox(): ToolboxConfig { return [ { title: 'Entry 1', icon: ICON, data: dataOverrides, }, ]; } /** * Return Tool's view */ public render(): HTMLElement { const wrapper = document.createElement('div'); wrapper.setAttribute('contenteditable', 'true'); return wrapper; } /** * Extracts Tool's data from the view * * @param el - tool view */ public save(el: HTMLElement): BlockToolData { return { ...this._data, text: el.innerHTML, }; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-block') .last() .click() .type(text); cy.get('@editorInstance') .then(async (editor: any) => { const editorData = await editor.save(); expect(editorData.blocks[0].data).to.be.deep.eq({ ...dataOverrides, text, }); }); }); it('should not display tool in toolbox if the tool has single toolbox entry configured and it has icon missing', () => { /** * Tool with one of the toolbox entries with icon missing */ class TestTool { /** * Returns toolbox config as list of entries one of which has missing icon */ public static get toolbox(): ToolboxConfig { return { title: 'Entry 2', }; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .should('not.exist'); }); it('should skip toolbox entries that have no icon', () => { const skippedEntryTitle = 'Entry 2'; /** * Tool with one of the toolbox entries with icon missing */ class TestTool { /** * Returns toolbox config as list of entries one of which has missing icon */ public static get toolbox(): ToolboxConfig { return [ { title: 'Entry 1', icon: ICON, }, { title: skippedEntryTitle, }, ]; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-popover__item[data-item-name=testTool]') .should('have.length', 1) .should('not.contain', skippedEntryTitle); }); }); context('Tunes', () => { it('should contain a single block tune configured in tool\'s renderSettings() method', () => { /** Tool with single tunes menu entry configured */ class TestTool { /** Returns toolbox config as list of entries */ public static get toolbox(): ToolboxConfigEntry { return { title: 'Test tool', icon: ICON, }; } /** Returns configuration for block tunes menu */ public renderSettings(): TunesMenuConfig { return { label: 'Test tool tune', icon: ICON, name: 'testToolTune', onActivate: (): void => {}, }; } /** Save method stub */ public save(): void {} /** Renders a block */ public render(): HTMLElement { const element = document.createElement('div'); element.contentEditable = 'true'; element.setAttribute('data-name', 'testBlock'); return element; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); // Insert test tool block cy.get('[data-cy=editorjs]') .get(`[data-item-name="testTool"]`) .click(); cy.get('[data-cy=editorjs]') .get('[data-name=testBlock]') .type('some text') .click(); // Open block tunes cy.get('[data-cy=editorjs]') .get('.ce-toolbar__settings-btn') .click(); // Expect preconfigured tune to exist in tunes menu cy.get('[data-item-name=testToolTune]').should('exist'); }); it('should contain multiple block tunes if configured in tool\'s renderSettings() method', () => { /** Tool with single tunes menu entry configured */ class TestTool { /** Returns toolbox config as list of entries */ public static get toolbox(): ToolboxConfigEntry { return { title: 'Test tool', icon: ICON, }; } /** Returns configuration for block tunes menu */ public renderSettings(): TunesMenuConfig { return [ { label: 'Test tool tune 1', icon: ICON, name: 'testToolTune1', onActivate: (): void => {}, }, { label: 'Test tool tune 2', icon: ICON, name: 'testToolTune2', onActivate: (): void => {}, }, ]; } /** Save method stub */ public save(): void {} /** Renders a block */ public render(): HTMLElement { const element = document.createElement('div'); element.contentEditable = 'true'; element.setAttribute('data-name', 'testBlock'); return element; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); // Insert test tool block cy.get('[data-cy=editorjs]') .get(`[data-item-name="testTool"]`) .click(); cy.get('[data-cy=editorjs]') .get('[data-name=testBlock]') .type('some text') .click(); // Open block tunes cy.get('[data-cy=editorjs]') .get('.ce-toolbar__settings-btn') .click(); // Expect preconfigured tunes to exist in tunes menu cy.get('[data-item-name=testToolTune1]').should('exist'); cy.get('[data-item-name=testToolTune2]').should('exist'); }); it('should contain block tunes represented as custom html if so configured in tool\'s renderSettings() method', () => { const sampleText = 'sample text'; /** Tool with single tunes menu entry configured */ class TestTool { /** Returns toolbox config as list of entries */ public static get toolbox(): ToolboxConfigEntry { return { title: 'Test tool', icon: ICON, }; } /** Returns configuration for block tunes menu */ public renderSettings(): HTMLElement { const element = document.createElement('div'); element.textContent = sampleText; return element; } /** Save method stub */ public save(): void {} /** Renders a block */ public render(): HTMLElement { const element = document.createElement('div'); element.contentEditable = 'true'; element.setAttribute('data-name', 'testBlock'); return element; } } cy.createEditor({ tools: { testTool: TestTool, }, }).as('editorInstance'); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click(); cy.get('[data-cy=editorjs]') .get('div.ce-toolbar__plus') .click(); // Insert test tool block cy.get('[data-cy=editorjs]') .get(`[data-item-name="testTool"]`) .click(); cy.get('[data-cy=editorjs]') .get('[data-name=testBlock]') .type('some text') .click(); // Open block tunes cy.get('[data-cy=editorjs]') .get('.ce-toolbar__settings-btn') .click(); // Expect preconfigured custom html tunes to exist in tunes menu cy.get('[data-cy=editorjs]') .get('.ce-popover') .should('contain.text', sampleText); }); }); });