From e3ab5c5ff5e0b1b6b9bc04cf1e5f0e01473e00a5 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Sat, 3 Feb 2018 00:11:21 +0200 Subject: [PATCH] Add missing unit tests for Draftail JS code --- .../Draftail/__snapshots__/index.test.js.snap | 49 +++ .../components/Draftail/blocks/ImageBlock.js | 2 +- .../Draftail/blocks/ImageBlock.test.js | 15 +- .../Draftail/blocks/MediaBlock.test.js | 2 +- .../Draftail/decorators/TooltipEntity.js | 4 +- .../Draftail/decorators/TooltipEntity.test.js | 85 +++++ .../__snapshots__/TooltipEntity.test.js.snap | 73 ++++ client/src/components/Draftail/index.js | 82 ++--- client/src/components/Draftail/index.test.js | 89 +++++ client/src/components/Draftail/registry.js | 20 +- .../src/components/Draftail/registry.test.js | 39 ++ .../Draftail/sources/ModalWorkflowSource.js | 4 +- .../sources/ModalWorkflowSource.test.js | 343 ++++++++++++++++++ .../ModalWorkflowSource.test.js.snap | 105 ++++++ client/src/components/Portal/Portal.test.js | 6 +- .../Portal/__snapshots__/Portal.test.js.snap | 6 +- client/tests/stubs.js | 17 + .../wagtailadmin/app/draftail.entry.js | 32 +- .../wagtailadmin/app/draftail.entry.test.js | 8 + 19 files changed, 893 insertions(+), 88 deletions(-) create mode 100644 client/src/components/Draftail/__snapshots__/index.test.js.snap create mode 100644 client/src/components/Draftail/decorators/TooltipEntity.test.js create mode 100644 client/src/components/Draftail/decorators/__snapshots__/TooltipEntity.test.js.snap create mode 100644 client/src/components/Draftail/index.test.js create mode 100644 client/src/components/Draftail/registry.test.js create mode 100644 client/src/components/Draftail/sources/ModalWorkflowSource.test.js create mode 100644 client/src/components/Draftail/sources/__snapshots__/ModalWorkflowSource.test.js.snap diff --git a/client/src/components/Draftail/__snapshots__/index.test.js.snap b/client/src/components/Draftail/__snapshots__/index.test.js.snap new file mode 100644 index 000000000..e2046662f --- /dev/null +++ b/client/src/components/Draftail/__snapshots__/index.test.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Draftail #initEditor options 1`] = ` +Object { + "autoCapitalize": null, + "autoComplete": null, + "autoCorrect": null, + "blockTypes": Array [], + "decorators": Array [], + "enableHorizontalRule": Object { + "description": "Horizontal line", + }, + "enableLineBreak": Object { + "description": "Line break", + }, + "entityTypes": Array [ + Object { + "block": [Function], + "decorator": undefined, + "source": [Function], + "type": "Image", + }, + ], + "inlineStyles": Array [], + "maxListNesting": 4, + "onSave": [Function], + "placeholder": "Write here…", + "rawContentState": null, + "showRedoControl": Object { + "description": "Redo", + }, + "showUndoControl": Object { + "description": "Undo", + }, + "spellCheck": true, + "stateSaveInterval": 250, + "stripPastedStyles": false, + "textAlignment": null, + "textDirectionality": null, +} +`; + +exports[`Draftail #wrapWagtailIcon works 1`] = ` + +`; diff --git a/client/src/components/Draftail/blocks/ImageBlock.js b/client/src/components/Draftail/blocks/ImageBlock.js index db49ce41c..25b3b9669 100644 --- a/client/src/components/Draftail/blocks/ImageBlock.js +++ b/client/src/components/Draftail/blocks/ImageBlock.js @@ -21,7 +21,7 @@ class ImageBlock extends Component { const { editorState, onChange } = blockProps; const data = { - alt: e.currentTarget.value, + alt: e.target.value, }; onChange(DraftUtils.updateBlockEntity(editorState, block, data)); diff --git a/client/src/components/Draftail/blocks/ImageBlock.test.js b/client/src/components/Draftail/blocks/ImageBlock.test.js index 70cce73d8..c694bdbce 100644 --- a/client/src/components/Draftail/blocks/ImageBlock.test.js +++ b/client/src/components/Draftail/blocks/ImageBlock.test.js @@ -65,8 +65,7 @@ describe('ImageBlock', () => { ).toMatchSnapshot(); }); - // Alt field is readonly for now. - it.skip('changeAlt', () => { + it('changeAlt', () => { jest.spyOn(DraftUtils, 'updateBlockEntity'); DraftUtils.updateBlockEntity.mockImplementation(e => e); @@ -88,11 +87,17 @@ describe('ImageBlock', () => { /> ); - wrapper.find('[type="text"]').simulate('change', { - currentTarget: { + // // Alt field is readonly for now. + wrapper.instance().changeAlt({ + target: { value: 'new alt', - }, + } }); + // wrapper.find('[type="text"]').simulate('change', { + // target: { + // value: 'new alt', + // }, + // }); expect(onChange).toHaveBeenCalled(); expect(DraftUtils.updateBlockEntity).toHaveBeenCalledWith( diff --git a/client/src/components/Draftail/blocks/MediaBlock.test.js b/client/src/components/Draftail/blocks/MediaBlock.test.js index 6c19a07b3..131448a2d 100644 --- a/client/src/components/Draftail/blocks/MediaBlock.test.js +++ b/client/src/components/Draftail/blocks/MediaBlock.test.js @@ -86,7 +86,7 @@ describe('MediaBlock', () => { ).toMatchSnapshot(); }); - it.skip('large viewport', () => { + it('large viewport', () => { const target = document.createElement('div'); document.body.appendChild(target); target.getBoundingClientRect = () => ({ diff --git a/client/src/components/Draftail/decorators/TooltipEntity.js b/client/src/components/Draftail/decorators/TooltipEntity.js index d80098317..de526bc3e 100644 --- a/client/src/components/Draftail/decorators/TooltipEntity.js +++ b/client/src/components/Draftail/decorators/TooltipEntity.js @@ -61,7 +61,7 @@ class TooltipEntity extends Component { closeOnResize > - {url ? ( + {( {shortenLabel(label)} - ) : null} + )} + + + + +`; + +exports[`TooltipEntity works 1`] = ` + + + test + +`; diff --git a/client/src/components/Draftail/index.js b/client/src/components/Draftail/index.js index c6ca68ae0..a9787a485 100644 --- a/client/src/components/Draftail/index.js +++ b/client/src/components/Draftail/index.js @@ -6,16 +6,18 @@ import { IS_IE11, STRINGS } from '../../config/wagtailConfig'; import Icon from '../Icon/Icon'; -import Link from './decorators/Link'; -import Document from './decorators/Document'; -import ImageBlock from './blocks/ImageBlock'; -import EmbedBlock from './blocks/EmbedBlock'; - -import ModalWorkflowSource from './sources/ModalWorkflowSource'; - import registry from './registry'; -const wrapWagtailIcon = type => { +export { registry }; + +export { default as Link } from './decorators/Link'; +export { default as Document } from './decorators/Document'; +export { default as ImageBlock } from './blocks/ImageBlock'; +export { default as EmbedBlock } from './blocks/EmbedBlock'; + +export { default as ModalWorkflowSource } from './sources/ModalWorkflowSource'; + +export const wrapWagtailIcon = type => { const isIconFont = type.icon && typeof type.icon === 'string'; if (isIconFont) { return Object.assign(type, { @@ -26,7 +28,12 @@ const wrapWagtailIcon = type => { return type; }; -export const initEditor = (fieldName, options = {}) => { +/** + * Initialises the DraftailEditor for a given field. + * @param {string} fieldName + * @param {Object} options + */ +export const initEditor = (fieldName, options) => { const field = document.querySelector(`[name="${fieldName}"]`); const editorWrapper = document.createElement('div'); field.parentNode.appendChild(editorWrapper); @@ -35,29 +42,19 @@ export const initEditor = (fieldName, options = {}) => { field.value = JSON.stringify(rawContentState); }; - let blockTypes; - let inlineStyles; - let entityTypes; + const blockTypes = options.blockTypes || []; + const inlineStyles = options.inlineStyles || []; + let entityTypes = options.entityTypes || []; - if (options && options.blockTypes) { - blockTypes = options.blockTypes.map(wrapWagtailIcon); - } + entityTypes = entityTypes.map(wrapWagtailIcon).map(type => + Object.assign(type, { + source: registry.getSource(type.source), + decorator: registry.getDecorator(type.decorator), + block: registry.getBlock(type.block), + }) + ); - if (options && options.inlineStyles) { - inlineStyles = options.inlineStyles.map(wrapWagtailIcon); - } - - if (options && options.entityTypes) { - entityTypes = options.entityTypes.map(wrapWagtailIcon).map(type => - Object.assign(type, { - source: registry.getSource(type.source), - decorator: registry.getDecorator(type.decorator), - block: registry.getBlock(type.block), - }) - ); - } - - const enableHorizontalRule = options && options.enableHorizontalRule ? { + const enableHorizontalRule = options.enableHorizontalRule ? { description: STRINGS.HORIZONTAL_LINE, } : false; @@ -77,8 +74,8 @@ export const initEditor = (fieldName, options = {}) => { // Draft.js + IE 11 presents some issues with pasting rich text. Disable rich paste there. stripPastedStyles={IS_IE11} {...options} - blockTypes={blockTypes} - inlineStyles={inlineStyles} + blockTypes={blockTypes.map(wrapWagtailIcon)} + inlineStyles={inlineStyles.map(wrapWagtailIcon)} entityTypes={entityTypes} enableHorizontalRule={enableHorizontalRule} /> @@ -89,24 +86,3 @@ export const initEditor = (fieldName, options = {}) => { // Bind editor instance to its field so it can be accessed imperatively elsewhere. field.draftailEditor = draftailEditor; }; - -registry.registerSources({ - ModalWorkflowSource, -}); -registry.registerDecorators({ - Link, - Document, -}); -registry.registerBlocks({ - ImageBlock, - EmbedBlock, -}); - -const draftail = Object.assign( - { - initEditor, - }, - registry -); - -export default draftail; diff --git a/client/src/components/Draftail/index.test.js b/client/src/components/Draftail/index.test.js new file mode 100644 index 000000000..97ca96441 --- /dev/null +++ b/client/src/components/Draftail/index.test.js @@ -0,0 +1,89 @@ +import { + wrapWagtailIcon, + initEditor, + registry, + ModalWorkflowSource, + Link, + Document, + ImageBlock, + EmbedBlock, +} from './index'; + +describe('Draftail', () => { + describe('#initEditor', () => { + beforeEach(() => { + document.body.innerHTML = ''; + }); + + it('works', () => { + const field = document.createElement('input'); + field.name = 'test'; + field.value = 'null'; + document.body.appendChild(field); + + initEditor('test', {}); + + expect(field.draftailEditor).toBeDefined(); + }); + + it('onSave', () => { + const field = document.createElement('input'); + field.name = 'test'; + field.value = 'null'; + document.body.appendChild(field); + + initEditor('test', {}); + + field.draftailEditor.saveState(); + + expect(field.value).toBe('null'); + }); + + it('options', () => { + const field = document.createElement('input'); + field.name = 'test'; + field.value = 'null'; + document.body.appendChild(field); + + registry.registerSources({ + ModalWorkflowSource: () => {}, + }); + + registry.registerBlocks({ + ImageBlock: () => {}, + }); + + initEditor('test', { + entityTypes: [ + { type: 'Image', source: 'ModalWorkflowSource', block: 'ImageBlock' }, + ], + enableHorizontalRule: true, + }); + + expect(field.draftailEditor.props).toMatchSnapshot(); + }); + }); + + describe('#wrapWagtailIcon', () => { + it('works', () => { + expect(wrapWagtailIcon({ icon: 'media' }).icon).toMatchSnapshot(); + }); + + it('no icon', () => { + const type = {}; + expect(wrapWagtailIcon(type)).toBe(type); + }); + + it('array icon', () => { + const type = { icon: ['M10 10 H 90 V 90 H 10 Z'] }; + expect(wrapWagtailIcon(type)).toBe(type); + }); + }); + + it('#registry', () => expect(registry).toBeDefined()); + it('#ModalWorkflowSource', () => expect(ModalWorkflowSource).toBeDefined()); + it('#Link', () => expect(Link).toBeDefined()); + it('#Document', () => expect(Document).toBeDefined()); + it('#ImageBlock', () => expect(ImageBlock).toBeDefined()); + it('#EmbedBlock', () => expect(EmbedBlock).toBeDefined()); +}); diff --git a/client/src/components/Draftail/registry.js b/client/src/components/Draftail/registry.js index ce1732978..75c6577de 100644 --- a/client/src/components/Draftail/registry.js +++ b/client/src/components/Draftail/registry.js @@ -4,29 +4,19 @@ const registry = { sources: {}, }; -const registerDecorators = (decorators) => { - Object.assign(registry.decorators, decorators); -}; +const registerDecorators = (decorators) => Object.assign(registry.decorators, decorators); +const registerBlocks = (blocks) => Object.assign(registry.blocks, blocks); +const registerSources = (sources) => Object.assign(registry.sources, sources); const getDecorator = name => registry.decorators[name]; - -const registerBlocks = (blocks) => { - Object.assign(registry.blocks, blocks); -}; - const getBlock = name => registry.blocks[name]; - -const registerSources = (sources) => { - Object.assign(registry.sources, sources); -}; - const getSource = name => registry.sources[name]; export default { registerDecorators, - getDecorator, registerBlocks, - getBlock, registerSources, + getDecorator, + getBlock, getSource, }; diff --git a/client/src/components/Draftail/registry.test.js b/client/src/components/Draftail/registry.test.js new file mode 100644 index 000000000..983bf52b8 --- /dev/null +++ b/client/src/components/Draftail/registry.test.js @@ -0,0 +1,39 @@ +import registry from './registry'; + +describe('registry', () => { + describe('sources', () => { + it('works', () => { + expect(registry.getSource('UndefinedSource')).not.toBeDefined(); + + registry.registerSources({ + TestSource: null, + }); + + expect(registry.getSource('TestSource')).toBe(null); + }); + }); + + describe('decorators', () => { + it('works', () => { + expect(registry.getDecorator('UndefinedDecorator')).not.toBeDefined(); + + registry.registerDecorators({ + TestDecorator: null, + }); + + expect(registry.getDecorator('TestDecorator')).toBe(null); + }); + }); + + describe('blocks', () => { + it('works', () => { + expect(registry.getBlock('UndefinedBlock')).not.toBeDefined(); + + registry.registerBlocks({ + TestBlock: null, + }); + + expect(registry.getBlock('TestBlock')).toBe(null); + }); + }); +}); diff --git a/client/src/components/Draftail/sources/ModalWorkflowSource.js b/client/src/components/Draftail/sources/ModalWorkflowSource.js index e44835d71..c43d8bb41 100644 --- a/client/src/components/Draftail/sources/ModalWorkflowSource.js +++ b/client/src/components/Draftail/sources/ModalWorkflowSource.js @@ -16,7 +16,7 @@ MUTABILITY[DOCUMENT] = 'MUTABLE'; MUTABILITY[ENTITY_TYPE.IMAGE] = 'IMMUTABLE'; MUTABILITY[EMBED] = 'IMMUTABLE'; -const getChooserConfig = (entityType, entity) => { +export const getChooserConfig = (entityType, entity) => { const chooserURL = {}; chooserURL[ENTITY_TYPE.IMAGE] = `${global.chooserUrls.imageChooser}?select_format=true`; chooserURL[EMBED] = global.chooserUrls.embedsChooser; @@ -59,7 +59,7 @@ const getChooserConfig = (entityType, entity) => { }; }; -const filterEntityData = (entityType, data) => { +export const filterEntityData = (entityType, data) => { switch (entityType.type) { case ENTITY_TYPE.IMAGE: return { diff --git a/client/src/components/Draftail/sources/ModalWorkflowSource.test.js b/client/src/components/Draftail/sources/ModalWorkflowSource.test.js new file mode 100644 index 000000000..0f807b0aa --- /dev/null +++ b/client/src/components/Draftail/sources/ModalWorkflowSource.test.js @@ -0,0 +1,343 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import ModalWorkflowSource, { getChooserConfig, filterEntityData } from './ModalWorkflowSource'; +import { EditorState, convertFromRaw, AtomicBlockUtils, RichUtils, Modifier } from 'draft-js'; + +global.ModalWorkflow = () => {}; + +describe('ModalWorkflowSource', () => { + beforeEach(() => { + jest.spyOn(global, 'ModalWorkflow'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('works', () => { + expect(shallow(( + {}} + onClose={() => {}} + /> + ))).toMatchSnapshot(); + }); + + describe('#getChooserConfig', () => { + it('IMAGE', () => { + expect(getChooserConfig({ type: 'IMAGE' })).toEqual({ + url: '/admin/images/chooser/?select_format=true', + urlParams: {}, + }); + }); + + it('EMBED', () => { + expect(getChooserConfig({ type: 'EMBED' })).toEqual({ + url: '/admin/embeds/chooser/', + urlParams: {}, + }); + }); + + it('DOCUMENT', () => { + expect(getChooserConfig({ type: 'DOCUMENT' })).toEqual({ + url: '/admin/documents/chooser/', + urlParams: {}, + }); + }); + + describe('LINK', () => { + it('no entity', () => { + expect(getChooserConfig({ type: 'LINK' })).toMatchSnapshot(); + }); + + it('page', () => { + expect(getChooserConfig({ type: 'LINK' }, { + getData: () => ({ id: 1, parentId: 0 }) + })).toMatchSnapshot(); + }); + + it('mail', () => { + expect(getChooserConfig({ type: 'LINK' }, { + getData: () => ({ url: 'mailto:test@example.com' }) + })).toMatchSnapshot(); + }); + + it('external', () => { + expect(getChooserConfig({ type: 'LINK' }, { + getData: () => ({ url: 'https://www.example.com/' }) + })).toMatchSnapshot(); + }); + }); + }); + + describe('#filterEntityData', () => { + it('IMAGE', () => { + expect(filterEntityData({ type: 'IMAGE' }, { + id: 53, + title: 'Test', + alt: 'Test', + class: 'richtext-image right', + edit_link: '/admin/images/53/', + format: 'right', + preview: { + url: '/media/images/test.width-500.jpg', + } + })).toMatchSnapshot(); + }); + + it('EMBED', () => { + expect(filterEntityData({ type: 'EMBED' }, { + authorName: 'Test', + embedType: 'video', + providerName: 'YouTube', + thumbnail: 'https://i.ytimg.com/vi/pSlVtxLOYiM/hqdefault.jpg', + title: 'Test', + url: 'https://www.youtube.com/watch?v=pSlVtxLOYiM', + })).toMatchSnapshot(); + }); + + it('DOCUMENT', () => { + expect(filterEntityData({ type: 'DOCUMENT' }, { + edit_link: '/admin/documents/edit/1/', + filename: 'test.pdf', + id: 1, + title: 'Test', + url: '/documents/1/test.pdf', + })).toMatchSnapshot(); + }); + + it('OTHER', () => { + expect(filterEntityData({ type: 'OTHER' }, {})).toEqual({}); + }); + + describe('LINK', () => { + it('page', () => { + expect(filterEntityData({ type: 'LINK' }, { + id: 60, + parentId: 1, + url: '/', + editUrl: '/admin/pages/60/edit/', + title: 'Welcome to the Wagtail Bakery!', + })).toMatchSnapshot(); + }); + + it('mail', () => { + expect(filterEntityData({ type: 'LINK' }, { + prefer_this_title_as_link_text: false, + title: 'test@example.com', + url: 'mailto:test@example.com', + })).toMatchSnapshot(); + }); + + it('external', () => { + expect(filterEntityData({ type: 'LINK' }, { + prefer_this_title_as_link_text: false, + title: 'https://www.example.com/', + url: 'https://www.example.com/', + })).toMatchSnapshot(); + }); + }); + }); + + it('#componentDidMount', () => { + const wrapper = shallow(( + {}} + onClose={() => {}} + /> + )); + + wrapper.instance().onChosen = jest.fn(); + + wrapper.instance().componentDidMount(); + + global.ModalWorkflow.mock.calls[0][0].responses.embedChosen('test', {}); + + expect(global.ModalWorkflow).toHaveBeenCalled(); + expect(global.jQuery().on).toHaveBeenCalled(); + expect(wrapper.instance().onChosen).toHaveBeenCalled(); + }); + + it('#onError', () => { + window.alert = jest.fn(); + const onClose = jest.fn(); + + const wrapper = shallow(( + {}} + onClose={onClose} + /> + )); + + wrapper.instance().componentDidMount(); + + global.ModalWorkflow.mock.calls[0][0].onError(); + + expect(global.ModalWorkflow).toHaveBeenCalled(); + expect(global.jQuery().on).toHaveBeenCalled(); + expect(window.alert).toHaveBeenCalled(); + expect(onClose).toHaveBeenCalled(); + }); + + it('#componentWillUnmount', () => { + const wrapper = shallow(( + {}} + onClose={() => {}} + /> + )); + + wrapper.instance().componentWillUnmount(); + + expect(global.jQuery().off).toHaveBeenCalled(); + }); + + describe('#onChosen', () => { + it('works', () => { + jest.spyOn(RichUtils, 'toggleLink'); + + const onComplete = jest.fn(); + + let editorState = EditorState.createWithContent(convertFromRaw({ + entityMap: {}, + blocks: [ + { + key: 'a', + text: 'test', + } + ] + })); + let selection = editorState.getSelection(); + selection = selection.merge({ + focusOffset: 4, + }); + editorState = EditorState.acceptSelection(editorState, selection); + const wrapper = shallow(( + {}} + /> + )); + + wrapper.instance().onChosen({}); + + expect(onComplete).toHaveBeenCalled(); + expect(RichUtils.toggleLink).toHaveBeenCalled(); + + RichUtils.toggleLink.mockRestore(); + }); + + it('block', () => { + jest.spyOn(AtomicBlockUtils, 'insertAtomicBlock'); + + const onComplete = jest.fn(); + + let editorState = EditorState.createWithContent(convertFromRaw({ + entityMap: {}, + blocks: [ + { + key: 'a', + text: 'test', + } + ] + })); + let selection = editorState.getSelection(); + selection = selection.merge({ + focusOffset: 4, + }); + editorState = EditorState.acceptSelection(editorState, selection); + const wrapper = shallow(( + {}, + }} + entity={{}} + onComplete={onComplete} + onClose={() => {}} + /> + )); + + wrapper.instance().onChosen({}); + + expect(onComplete).toHaveBeenCalled(); + expect(AtomicBlockUtils.insertAtomicBlock).toHaveBeenCalled(); + + AtomicBlockUtils.insertAtomicBlock.mockRestore(); + }); + + it('prefer_this_title_as_link_text', () => { + jest.spyOn(Modifier, 'replaceText'); + + const onComplete = jest.fn(); + + let editorState = EditorState.createWithContent(convertFromRaw({ + entityMap: {}, + blocks: [ + { + key: 'a', + text: 'test', + } + ] + })); + let selection = editorState.getSelection(); + selection = selection.merge({ + focusOffset: 4, + }); + editorState = EditorState.acceptSelection(editorState, selection); + const wrapper = shallow(( + {}} + /> + )); + + wrapper.instance().onChosen({ + url: 'example.com', + prefer_this_title_as_link_text: true, + }); + + expect(onComplete).toHaveBeenCalled(); + expect(Modifier.replaceText).toHaveBeenCalled(); + + Modifier.replaceText.mockRestore(); + }); + }); + + it('#onClose', () => { + const onClose = jest.fn(); + const wrapper = shallow(( + {}} + onClose={onClose} + /> + )); + + wrapper.instance().onClose({ + preventDefault: () => {}, + }); + + expect(onClose).toHaveBeenCalled(); + }); +}); diff --git a/client/src/components/Draftail/sources/__snapshots__/ModalWorkflowSource.test.js.snap b/client/src/components/Draftail/sources/__snapshots__/ModalWorkflowSource.test.js.snap new file mode 100644 index 000000000..6c47ebd38 --- /dev/null +++ b/client/src/components/Draftail/sources/__snapshots__/ModalWorkflowSource.test.js.snap @@ -0,0 +1,105 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalWorkflowSource #filterEntityData DOCUMENT 1`] = ` +Object { + "filename": "test.pdf", + "id": 1, + "url": "/documents/1/test.pdf", +} +`; + +exports[`ModalWorkflowSource #filterEntityData EMBED 1`] = ` +Object { + "authorName": "Test", + "embedType": "video", + "providerName": "YouTube", + "thumbnail": "https://i.ytimg.com/vi/pSlVtxLOYiM/hqdefault.jpg", + "title": "Test", + "url": "https://www.youtube.com/watch?v=pSlVtxLOYiM", +} +`; + +exports[`ModalWorkflowSource #filterEntityData IMAGE 1`] = ` +Object { + "alt": "Test", + "format": "right", + "id": 53, + "src": "/media/images/test.width-500.jpg", +} +`; + +exports[`ModalWorkflowSource #filterEntityData LINK external 1`] = ` +Object { + "url": "https://www.example.com/", +} +`; + +exports[`ModalWorkflowSource #filterEntityData LINK mail 1`] = ` +Object { + "url": "mailto:test@example.com", +} +`; + +exports[`ModalWorkflowSource #filterEntityData LINK page 1`] = ` +Object { + "id": 60, + "parentId": 1, + "url": "/", +} +`; + +exports[`ModalWorkflowSource #getChooserConfig LINK external 1`] = ` +Object { + "url": "/admin/choose-external-link/", + "urlParams": Object { + "allow_email_link": true, + "allow_external_link": true, + "can_choose_root": "false", + "link_text": "", + "link_url": "https://www.example.com/", + "page_type": "wagtailcore.page", + }, +} +`; + +exports[`ModalWorkflowSource #getChooserConfig LINK mail 1`] = ` +Object { + "url": "/admin/choose-email-link/", + "urlParams": Object { + "allow_email_link": true, + "allow_external_link": true, + "can_choose_root": "false", + "link_text": "", + "link_url": "test@example.com", + "page_type": "wagtailcore.page", + }, +} +`; + +exports[`ModalWorkflowSource #getChooserConfig LINK no entity 1`] = ` +Object { + "url": "/admin/choose-page/", + "urlParams": Object { + "allow_email_link": true, + "allow_external_link": true, + "can_choose_root": "false", + "link_text": "", + "page_type": "wagtailcore.page", + }, +} +`; + +exports[`ModalWorkflowSource #getChooserConfig LINK page 1`] = ` +Object { + "url": "/admin/choose-page/0/", + "urlParams": Object { + "allow_email_link": true, + "allow_external_link": true, + "can_choose_root": "false", + "link_text": "", + "page_type": "wagtailcore.page", + }, +} +`; + +exports[`ModalWorkflowSource works 1`] = `""`; diff --git a/client/src/components/Portal/Portal.test.js b/client/src/components/Portal/Portal.test.js index 994707ea7..697c63f85 100644 --- a/client/src/components/Portal/Portal.test.js +++ b/client/src/components/Portal/Portal.test.js @@ -4,7 +4,7 @@ import Portal from './Portal'; const func = expect.any(Function); -describe.skip('Portal', () => { +describe('Portal', () => { beforeEach(() => { document.body.innerHTML = ''; }); @@ -18,8 +18,8 @@ describe.skip('Portal', () => { }); it('component lifecycle', () => { - jest.spyOn(document, 'removeEventListener'); - jest.spyOn(window, 'removeEventListener'); + document.removeEventListener = jest.fn(); + window.removeEventListener = jest.fn(); const wrapper = shallow( {}}>Test!); diff --git a/client/src/components/Portal/__snapshots__/Portal.test.js.snap b/client/src/components/Portal/__snapshots__/Portal.test.js.snap index d4599aa7a..0be76d31a 100644 --- a/client/src/components/Portal/__snapshots__/Portal.test.js.snap +++ b/client/src/components/Portal/__snapshots__/Portal.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Portal #children 1`] = `null`; +exports[`Portal #children 1`] = `""`; -exports[`Portal component lifecycle 1`] = `"
Test!
"`; +exports[`Portal component lifecycle 1`] = `"
Test!
"`; -exports[`Portal empty 1`] = `null`; +exports[`Portal empty 1`] = `""`; diff --git a/client/tests/stubs.js b/client/tests/stubs.js index b291ba79f..133314aef 100644 --- a/client/tests/stubs.js +++ b/client/tests/stubs.js @@ -39,3 +39,20 @@ global.wagtailConfig = { }; global.wagtailVersion = '1.6a1'; + +global.chooserUrls = { + documentChooser: '/admin/documents/chooser/', + emailLinkChooser: '/admin/choose-email-link/', + embedsChooser: '/admin/embeds/chooser/', + externalLinkChooser: '/admin/choose-external-link/', + imageChooser: '/admin/images/chooser/', + pageChooser: '/admin/choose-page/', + snippetChooser: '/admin/snippets/choose/', +}; + +const jQueryObj = { + on: jest.fn(), + off: jest.fn(), +}; + +global.jQuery = () => jQueryObj; diff --git a/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js b/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js index 1434c2ef5..c7924a593 100644 --- a/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js +++ b/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js @@ -1,4 +1,30 @@ -import draftail from '../../../../../client/src/components/Draftail/index'; +import { + initEditor, + registry, + ModalWorkflowSource, + Link, + Document, + ImageBlock, + EmbedBlock, +} from '../../../../../client/src/components/Draftail/index'; -// Expose as a global variable, for integration with other scripts. -window.draftail = draftail; +/** + * Expose as a global, and register the built-in entities. + */ + +window.draftail = registry; +window.draftail.initEditor = initEditor; + +window.draftail.registerSources({ + ModalWorkflowSource, +}); + +window.draftail.registerDecorators({ + Link, + Document, +}); + +window.draftail.registerBlocks({ + ImageBlock, + EmbedBlock, +}); diff --git a/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.test.js b/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.test.js index ed2c5a12e..c5c2197e6 100644 --- a/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.test.js +++ b/wagtail/admin/static_src/wagtailadmin/app/draftail.entry.test.js @@ -4,4 +4,12 @@ describe('draftail.entry', () => { it('exposes global', () => { expect(window.draftail).toBeDefined(); }); + + it('has defaults registered', () => { + expect(window.draftail.getSource('ModalWorkflowSource')).toBeDefined(); + expect(window.draftail.getDecorator('Link')).toBeDefined(); + expect(window.draftail.getDecorator('Document')).toBeDefined(); + expect(window.draftail.getBlock('ImageBlock')).toBeDefined(); + expect(window.draftail.getBlock('EmbedBlock')).toBeDefined(); + }); });