'use strict'; import StructureBoard from '../../../static/cms/js/modules/cms.structureboard'; import Plugin from '../../../static/cms/js/modules/cms.plugins'; import CMS, { Helpers, KEYS } from '../../../static/cms/js/modules/cms.base'; import $ from 'jquery'; import keyboard from '../../../static/cms/js/modules/keyboard'; import { rewire, resetRewire } from './helpers/rewire'; vi.mock('../../../static/cms/js/modules/loader', async () => { const { lazyMock, registerActual } = await import('./helpers/rewire'); registerActual('loader', await vi.importActual('../../../static/cms/js/modules/loader')); return lazyMock('loader', { showLoader: 'showLoader', hideLoader: 'hideLoader' }); }); vi.mock('../../../static/cms/js/modules/cms.plugins', async () => { const { lazyMock, registerActual } = await import('./helpers/rewire'); const namespace = lazyMock('cms.plugins', { default: 'Plugin' }); // not awaited on purpose: cms.plugins is part of the plugins/structureboard/ // clipboard import cycle, and awaiting it here deadlocks the mock vi.importActual('../../../static/cms/js/modules/cms.plugins') .then(actual => registerActual('cms.plugins', actual)); return namespace; }); vi.mock('../../../static/cms/js/modules/cms.clipboard', async () => { const { lazyMock, registerActual } = await import('./helpers/rewire'); const namespace = lazyMock('cms.clipboard', { default: 'Clipboard' }); // not awaited on purpose: cms.clipboard is part of the plugins/structureboard/ // clipboard import cycle, and awaiting it here deadlocks the mock vi.importActual('../../../static/cms/js/modules/cms.clipboard') .then(actual => registerActual('cms.clipboard', actual)); return namespace; }); vi.mock('../../../static/cms/js/modules/dom-diff', async () => { const { lazyMock, registerActual } = await import('./helpers/rewire'); registerActual('dom-diff', await vi.importActual('../../../static/cms/js/modules/dom-diff')); return lazyMock('dom-diff', { DiffDOM: 'DiffDOM', nodeToObj: 'nodeToObj' }); }); vi.mock('../../../static/cms/js/modules/preload-images', async () => { const { lazyMock, registerActual } = await import('./helpers/rewire'); registerActual('preload-images', await vi.importActual('../../../static/cms/js/modules/preload-images')); return lazyMock('preload-images', { default: 'preloadImagesFromMarkup' }); }); var showLoader; var hideLoader; window.CMS = CMS; CMS.StructureBoard = StructureBoard; CMS.Plugin = Plugin; CMS.API = CMS.API || {}; CMS.API.Helpers = Helpers; CMS.KEYS = KEYS; CMS.$ = $; const pluginConstructor = jasmine.createSpy(); // the real class, not the rewirable stand-in: FakePlugin._updateClipboard() // delegates to it, which would otherwise recurse into itself const originalPlugin = (await vi.importActual('../../../static/cms/js/modules/cms.plugins')).default; class FakePlugin { constructor(container, opts) { this.options = opts; const el = $('
'); el.data('cms', []); this.ui = { container: el }; pluginConstructor(container, opts); } static _recalculatePluginPositions() {} } FakePlugin._updateRegistry = jasmine.createSpy(); FakePlugin._updateClipboard = jasmine.createSpy().and.callFake(() => { originalPlugin._updateClipboard(); }); FakePlugin.aliasPluginDuplicatesMap = {}; FakePlugin._refreshPlugins = jasmine.createSpy().and.callFake(() => { originalPlugin._refreshPlugins(); }); FakePlugin.prototype._setupUI = jasmine.createSpy(); FakePlugin.prototype._ensureData = jasmine.createSpy(); FakePlugin.prototype._setGeneric = jasmine.createSpy(); FakePlugin.prototype._setPlaceholder = jasmine.createSpy(); FakePlugin.prototype._collapsables = jasmine.createSpy(); FakePlugin.prototype._setPluginContentEvents = jasmine.createSpy(); FakePlugin.prototype._setPluginStructureEvents = jasmine.createSpy(); describe('CMS.StructureBoard', function() { fixture.setBase('cms/tests/frontend/unit/fixtures'); beforeEach(() => { CMS.API.Clipboard = { populate: jasmine.createSpy() }; showLoader = jasmine.createSpy(); hideLoader = jasmine.createSpy(); rewire('showLoader', showLoader); rewire('hideLoader', hideLoader); }); afterEach(() => { FakePlugin.aliasPluginDuplicatesMap = {}; FakePlugin.prototype._setupUI.calls.reset(); FakePlugin.prototype._ensureData.calls.reset(); FakePlugin.prototype._setGeneric.calls.reset(); FakePlugin.prototype._setPlaceholder.calls.reset(); FakePlugin.prototype._collapsables.calls.reset(); FakePlugin.prototype._setPluginContentEvents.calls.reset(); FakePlugin.prototype._setPluginStructureEvents.calls.reset(); FakePlugin._refreshPlugins.calls.reset(); resetRewire('showLoader'); resetRewire('hideLoader'); }); it('creates a StructureBoard class', function() { expect(CMS.StructureBoard).toBeDefined(); }); it('has public API', function() { expect(CMS.StructureBoard.prototype.show).toEqual(jasmine.any(Function)); expect(CMS.StructureBoard.prototype.hide).toEqual(jasmine.any(Function)); expect(CMS.StructureBoard.prototype.getId).toEqual(jasmine.any(Function)); expect(CMS.StructureBoard.prototype.getIds).toEqual(jasmine.any(Function)); }); describe('instance', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: '/structure' }, mode: 'edit' }; $(function() { CMS.StructureBoard._initializeGlobalHandlers(); jasmine.clock().install(); board = new CMS.StructureBoard(); done(); }); }); afterEach(function() { jasmine.clock().uninstall(); fixture.cleanup(); }); it('has ui', function() { expect(board.ui).toEqual(jasmine.any(Object)); expect(Object.keys(board.ui)).toContain('container'); expect(Object.keys(board.ui)).toContain('content'); expect(Object.keys(board.ui)).toContain('doc'); expect(Object.keys(board.ui)).toContain('window'); expect(Object.keys(board.ui)).toContain('html'); expect(Object.keys(board.ui)).toContain('toolbar'); expect(Object.keys(board.ui)).toContain('sortables'); expect(Object.keys(board.ui)).toContain('plugins'); expect(Object.keys(board.ui)).toContain('render_model'); expect(Object.keys(board.ui)).toContain('placeholders'); expect(Object.keys(board.ui)).toContain('dragitems'); expect(Object.keys(board.ui)).toContain('dragareas'); expect(Object.keys(board.ui)).toContain('toolbarModeSwitcher'); expect(Object.keys(board.ui)).toContain('toolbarModeLinks'); expect(Object.keys(board.ui).length).toEqual(14); }); it('has no options', function() { expect(board.options).not.toBeDefined(); }); it('applies correct classes to empty placeholder dragareas', function() { $('.cms-dragarea').removeClass('cms-dragarea-empty'); board = new CMS.StructureBoard(); expect('.cms-dragarea-1').not.toHaveClass('cms-dragarea-empty'); expect('.cms-dragarea-2').toHaveClass('cms-dragarea-empty'); expect('.cms-dragarea-10').toHaveClass('cms-dragarea-empty'); }); it('initially shows or hides board based on settings', function() { spyOn(CMS.StructureBoard.prototype, 'show'); spyOn(CMS.StructureBoard.prototype, 'hide'); expect(CMS.config.settings.mode).toEqual('edit'); board = new CMS.StructureBoard(); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).toHaveBeenCalled(); }); it('initially shows or hides board based on settings 2', function() { spyOn(CMS.StructureBoard.prototype, 'show'); spyOn(CMS.StructureBoard.prototype, 'hide'); CMS.config.settings.mode = 'structure'; board = new CMS.StructureBoard(); expect(board.show).toHaveBeenCalled(); expect(board.hide).not.toHaveBeenCalled(); }); it('does not show or hide structureboard if there are no dragareas', function() { board.ui.dragareas.remove(); board = new CMS.StructureBoard(); spyOn(board, 'show'); spyOn(board, 'hide'); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).not.toHaveBeenCalled(); jasmine.clock().tick(200); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).not.toHaveBeenCalled(); }); it('does not show or hide structureboard if there is no board mode switcher', function() { board.ui.toolbarModeSwitcher.remove(); board = new CMS.StructureBoard(); spyOn(board, 'show'); spyOn(board, 'hide'); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).not.toHaveBeenCalled(); jasmine.clock().tick(200); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).not.toHaveBeenCalled(); }); it('enables board mode switcher if there are placeholders', function() { expect(board.ui.placeholders.length > 0).toEqual(true); board.ui.toolbarModeSwitcher.find('.cms-btn').addClass('cms-btn-disabled'); new CMS.StructureBoard(); jasmine.clock().tick(100); expect(board.ui.toolbarModeSwitcher.find('.cms-btn')).not.toHaveClass('cms-btn-disabled'); }); it('does not enable board mode switcher if there are no placeholders', function() { expect(board.ui.placeholders.length > 0).toEqual(true); expect(board.ui.dragareas.length > 0).toEqual(true); board.ui.placeholders.remove(); board.ui.dragareas.remove(); board.ui.toolbarModeSwitcher.find('.cms-btn').addClass('cms-btn-disabled'); board = new CMS.StructureBoard(); expect(board.ui.placeholders.length).toEqual(0); expect(board.ui.dragareas.length).toEqual(0); jasmine.clock().tick(100); expect(board.ui.toolbarModeSwitcher.find('.cms-btn')).toHaveClass('cms-btn-disabled'); }); it('sets loaded content and structure flags if it is a legacy renderer', () => { CMS.config.settings.legacy_mode = true; board = new CMS.StructureBoard(); expect(board._loadedStructure).toEqual(true); expect(board._loadedContent).toEqual(true); }); }); describe('.show()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.API.Toolbar = { _refreshMarkup: jasmine.createSpy() }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { spyOn(CMS.API.Helpers, 'setSettings').and.callFake(function(input) { return input; }); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, '_loadStructure').and.returnValue(Promise.resolve()); done(); }); }); afterEach(function() { fixture.cleanup(); }); it('shows the board', function(done) { spyOn(board, '_showBoard').and.callThrough(); expect(board.ui.container).not.toBeVisible(); board.show().then(function() { expect(board.ui.container).toBeVisible(); expect(board._showBoard).toHaveBeenCalled(); done(); }); }); it('does not show the board if we are viewing published page', function(done) { CMS.config.mode = 'live'; spyOn(board, '_showBoard').and.callThrough(); expect(board.ui.container).not.toBeVisible(); board.show().then(r => { expect(r).toEqual(false); expect(board.ui.container).not.toBeVisible(); expect(board._showBoard).not.toHaveBeenCalled(); done(); }); }); it('highlights correct trigger', function(done) { expect(board.ui.toolbarModeLinks).not.toHaveClass('cms-btn-active'); board.show().then(() => { expect(board.ui.toolbarModeLinks).toHaveClass('cms-btn-active'); done(); }); }); it('adds correct classes to the root of the document', function(done) { board.ui.html.removeClass('cms-structure-mode-structure'); expect(board.ui.html).not.toHaveClass('cms-structure-mode-structure'); board.show().then(() => { expect(board.ui.html).toHaveClass('cms-structure-mode-structure'); done(); }); }); it('does set state through settings', function(done) { CMS.API.Helpers.setSettings.and.callFake(function(input) { return input; }); expect(CMS.settings.mode).toEqual('edit'); board.show().then(() => { expect(CMS.settings.mode).toEqual('structure'); expect(CMS.API.Helpers.setSettings).toHaveBeenCalled(); done(); }); }); it('saves the state in the url'); }); describe('highlights', function() { var board; beforeEach(function(done) { fixture.load('plugins.html', 'clipboard.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { spyOn(CMS.API.Helpers, 'setSettings').and.callFake(function(input) { return input; }); CMS.API.Tooltip = { domElem: { is: function() { return true; }, data: function() { return 1; } } }; CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, 'show').and.returnValue(Promise.resolve()); spyOn(board, 'hide').and.returnValue(Promise.resolve()); spyOn(Plugin, '_highlightPluginStructure'); spyOn(Plugin, '_highlightPluginContent'); done(); }); }); afterEach(function() { fixture.cleanup(); }); describe('._showAndHighlightPlugin()', function() { it('returns false if tooltip does not exist', function(done) { CMS.API.Tooltip = false; board._showAndHighlightPlugin().then(r => { expect(r).toEqual(false); expect(board.show).not.toHaveBeenCalled(); done(); }); }); it('returns false if in live mode', function(done) { CMS.config.mode = 'live'; board._showAndHighlightPlugin().then(r => { expect(r).toEqual(false); expect(board.show).not.toHaveBeenCalled(); done(); }); }); it('returns false if no plugin is hovered', function(done) { CMS.API.Tooltip.domElem.is = function() { return false; }; board._showAndHighlightPlugin().then(r => { expect(r).toEqual(false); expect(board.show).not.toHaveBeenCalled(); done(); }); }); it('shows board if plugin is hovered', function(done) { jasmine.clock().install(); board._showAndHighlightPlugin().then(() => { expect(board.show).toHaveBeenCalledTimes(1); expect(Plugin._highlightPluginStructure).not.toHaveBeenCalled(); jasmine.clock().tick(201); expect(Plugin._highlightPluginStructure).toHaveBeenCalledTimes(1); jasmine.clock().uninstall(); done(); }); }); }); }); describe('.hide()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); $(function() { CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; spyOn(CMS.API.Helpers, 'setSettings').and.callFake(function(input) { return input; }); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, '_loadContent').and.returnValue(Promise.resolve()); spyOn(board, '_loadStructure').and.returnValue(Promise.resolve()); done(); }); }); afterEach(function() { fixture.cleanup(); }); it('hides the board', function(done) { spyOn(board, '_hideBoard').and.callThrough(); board .show() .then(() => { expect(board.ui.container).toBeVisible(); return board.hide(); }) .then(() => { expect(board.ui.container).not.toBeVisible(); expect(board._hideBoard).toHaveBeenCalled(); done(); }); }); it('does not hide the board if we are viewing published page', function() { CMS.config.mode = 'live'; spyOn(board, '_hideBoard'); expect(board.ui.container).not.toBeVisible(); expect(board.hide()).toEqual(false); expect(board.ui.container).not.toBeVisible(); expect(board._hideBoard).not.toHaveBeenCalled(); }); it('deactivates the button', function(done) { board.hide().then(() => { expect(board.ui.toolbarModeLinks).not.toHaveClass('cms-btn-active'); done(); }); }); it('does not remember the state in localstorage', function() { board.show(); expect(CMS.settings.mode).toEqual('structure'); CMS.API.Helpers.setSettings.and.callFake(function(input) { return input; }); CMS.API.Helpers.setSettings.calls.reset(); board.hide(); expect(CMS.settings.mode).toEqual('edit'); expect(CMS.API.Helpers.setSettings).not.toHaveBeenCalled(); }); it('remembers the state in the url'); it('triggers `resize` event on the window', function(done) { var spy = jasmine.createSpy(); board .show() .then(() => { $(window).on('resize', spy); return board.hide(); }) .then(() => { expect(spy).toHaveBeenCalledTimes(1); $(window).off('resize', spy); done(); }); }); }); describe('.getId()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); done(); }); }); afterEach(function() { fixture.cleanup(); }); it('returns the id of passed element', function() { [ { from: 'cms-plugin cms-plugin-1', result: '1' }, { from: 'cms-plugin cms-plugin-125', result: '125' }, { from: 'cms-draggable cms-draggable-1', result: '1' }, { from: 'cms-draggable cms-draggable-125', result: '125' }, { from: 'cms-placeholder cms-placeholder-1', result: '1' }, { from: 'cms-placeholder cms-placeholder-125', result: '125' }, { from: 'cms-dragbar cms-dragbar-1', result: '1' }, { from: 'cms-dragbar cms-dragbar-125', result: '125' }, { from: 'cms-dragarea cms-dragarea-1', result: '1' }, { from: 'cms-dragarea cms-dragarea-125', result: '125' } ].forEach(function(obj) { expect(board.getId($('
'))).toEqual(obj.result); }); }); it('returns null if element is of non supported "type"', function() { [ { from: 'cannot determine', result: null }, { from: 'cms-not-supported cms-not-supported-1', result: null } ].forEach(function(obj) { expect(board.getId($('
'))).toEqual(obj.result); }); }); it('returns false if element does not exist', function() { expect(board.getId()).toEqual(false); expect(board.getId(null)).toEqual(false); expect(board.getId($('.non-existent'))).toEqual(false); expect(board.getId([])).toEqual(false); }); it('fails if classname string is incorrect', function() { expect(board.getId.bind(board, $('
'))).toThrow(); expect(board.getId($('
'))).toEqual('fail'); }); }); describe('.getIds()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); done(); }); }); afterEach(function() { fixture.cleanup(); }); it('returns the array of ids of passed collection', function() { spyOn(board, 'getId').and.callThrough(); [ { from: ['cms-plugin cms-plugin-1'], result: ['1'] }, { from: ['cms-plugin cms-plugin-125', 'cms-plugin cms-plugin-1'], result: ['125', '1'] }, { from: ['cms-plugin cms-plugin-125', 'cms-plugin cms-plugin-1', 'cms-draggable cms-draggable-12'], result: ['125', '1', '12'] }, { from: ['non-existent', 'cms-plugin cms-plugin-1'], result: [null, '1'] } ].forEach(function(obj) { var collection = $(); obj.from.forEach(function(className) { collection = collection.add($('
')); }); expect(board.getIds(collection)).toEqual(obj.result); }); expect(board.getId).toHaveBeenCalled(); }); }); describe('._setupModeSwitcher()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { spyOn(keyboard, 'bind'); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, 'show').and.callFake(function() { CMS.settings.mode = 'structure'; }); spyOn(board, 'hide').and.callFake(function() { CMS.settings.mode = 'edit'; }); done(); }); }); afterEach(function() { board.ui.doc.off('keydown.cms.structureboard.switcher'); fixture.cleanup(); }); it('sets up click handler to show board', function() { var showTrigger = board.ui.toolbarModeLinks; expect(showTrigger).toHandle(board.click); CMS.settings.mode = 'structure'; showTrigger.trigger(board.click); expect(board.show).not.toHaveBeenCalled(); CMS.settings.mode = 'edit'; showTrigger.trigger(board.click); expect(board.show).toHaveBeenCalledTimes(1); }); it('sets up click handler to hide board', function() { var hideTrigger = board.ui.toolbarModeLinks; expect(hideTrigger).toHandle(board.click); CMS.settings.mode = 'edit'; hideTrigger.trigger(board.click); expect(board.hide).not.toHaveBeenCalled(); CMS.settings.mode = 'structure'; hideTrigger.trigger(board.click); expect(board.hide).toHaveBeenCalledTimes(1); }); it('sets up shortcuts to toggle board', function() { spyOn(board, '_toggleStructureBoard'); var preventDefaultSpySpace = jasmine.createSpy(); var preventDefaultSpyShiftSpace = jasmine.createSpy(); expect(keyboard.bind).toHaveBeenCalledTimes(2); expect(keyboard.bind).toHaveBeenCalledWith('space', jasmine.any(Function)); expect(keyboard.bind).toHaveBeenCalledWith('shift+space', jasmine.any(Function)); var calls = keyboard.bind.calls.all(); calls[0].args[1]({ preventDefault: preventDefaultSpySpace }); expect(board._toggleStructureBoard).toHaveBeenCalledTimes(1); expect(board._toggleStructureBoard).toHaveBeenCalledWith(); expect(preventDefaultSpySpace).toHaveBeenCalledTimes(1); expect(preventDefaultSpyShiftSpace).not.toHaveBeenCalled(); calls[1].args[1]({ preventDefault: preventDefaultSpyShiftSpace }); expect(board._toggleStructureBoard).toHaveBeenCalledTimes(2); expect(board._toggleStructureBoard).toHaveBeenCalledWith({ useHoveredPlugin: true }); expect(preventDefaultSpySpace).toHaveBeenCalledTimes(1); expect(preventDefaultSpyShiftSpace).toHaveBeenCalledTimes(1); }); it('does not setup key binds if toggler is not availabe', function() { keyboard.bind.calls.reset(); board.ui.toolbarModeSwitcher.remove(); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, 'show').and.callFake(function() { CMS.settings.mode = 'structure'; }); spyOn(board, 'hide').and.callFake(function() { CMS.settings.mode = 'edit'; }); expect(keyboard.bind).not.toHaveBeenCalled(); }); it('does not setup key binds if toggler is not visible (there are no placeholders)', function() { keyboard.bind.calls.reset(); board.ui.placeholders.remove(); board.ui.dragareas.remove(); board.ui.toolbarModeSwitcher.find('.cms-btn').addClass('cms-btn-disabled'); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, 'show').and.callFake(function() { CMS.settings.mode = 'structure'; }); spyOn(board, 'hide').and.callFake(function() { CMS.settings.mode = 'edit'; }); expect(keyboard.bind).not.toHaveBeenCalled(); }); }); describe('._toggleStructureBoard()', function() { var board; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'edit' }; CMS.config = { settings: { mode: 'edit', structure: 'structure' }, mode: 'edit' }; $(function() { spyOn(keyboard, 'bind'); CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); spyOn(board, 'show').and.callFake(function() { CMS.settings.mode = 'structure'; }); spyOn(board, 'hide').and.callFake(function() { CMS.settings.mode = 'edit'; }); spyOn(board, '_showAndHighlightPlugin').and.returnValue({ then() {} }); done(); }); }); it('shows structureboard', function() { board._toggleStructureBoard(); expect(board.show).toHaveBeenCalledTimes(1); expect(board.hide).not.toHaveBeenCalled(); }); it('hides strucrueboard', function() { CMS.settings.mode = 'structure'; board._toggleStructureBoard(); expect(board.show).not.toHaveBeenCalled(); expect(board.hide).toHaveBeenCalledTimes(1); }); it('shows structureboard and highlights plugin', function() { CMS.settings.mode = 'edit'; board._toggleStructureBoard({ useHoveredPlugin: true }); expect(board._showAndHighlightPlugin).toHaveBeenCalledTimes(1); }); it('does not show structureboard and highlights plugin if it is open already', function() { CMS.settings.mode = 'structure'; board._toggleStructureBoard({ useHoveredPlugin: true }); expect(board._showAndHighlightPlugin).not.toHaveBeenCalled(); }); }); describe('._drag()', function() { var board; var options; beforeEach(function(done) { fixture.load('plugins.html'); CMS.settings = { mode: 'structure' }; CMS.config = { settings: { mode: 'structure', structure: '/structure' }, mode: 'structure' }; $(function() { CMS.StructureBoard._initializeGlobalHandlers(); board = new CMS.StructureBoard(); board._drag(); options = board.ui.sortables.nestedSortable('option'); board.show().then(() => { done(); }); }); }); afterEach(function() { board.ui.doc.off('keyup.cms.interrupt'); fixture.cleanup(); }); it('initializes nested sortable', function() { options = board.ui.sortables.nestedSortable('option'); expect(options).toEqual( jasmine.objectContaining({ items: '> .cms-draggable:not(.cms-drag-disabled):not(.cms-draggable-disabled .cms-draggable)', placeholder: 'cms-droppable', connectWith: '.cms-draggables:not(.cms-hidden)', appendTo: '.cms-structure-content', listType: 'div.cms-draggables', doNotClear: true, toleranceElement: '> div', disableNestingClass: 'cms-draggable-disabled', errorClass: 'cms-draggable-disallowed', start: jasmine.any(Function), helper: jasmine.any(Function), beforeStop: jasmine.any(Function), update: jasmine.any(Function), isAllowed: jasmine.any(Function) }) ); }); it('adds event handler for cms-structure-update to actualize empty placeholders', function() { if (!CMS.$._data(board.ui.sortables[0]).events['cms-structure-update'][0].handler.name) { pending(); } expect(board.ui.sortables).toHandle('cms-structure-update'); // cheating here a bit expect(CMS.$._data(board.ui.sortables[0]).events['cms-structure-update'][0].handler.name).toEqual( 'actualizePlaceholders' ); }); it('defines how draggable helper is created', function() { options = board.ui.sortables.nestedSortable('option'); var helper = options.helper; var item = $( '
' + '
Only this will be cloned
' + '
' + '
This will not
' + '
' + '
' ); var result = helper(null, item); expect(result).toHaveClass('some'); expect(result).toHaveClass('class'); expect(result).toHaveClass('string'); expect(result).toHaveText('Only this will be cloned'); expect(result).not.toHaveText('This will not'); }); describe('start', function() { it('sets touchAction style property', function() { expect(board.ui.content[0].style.touchAction).toEqual('pan-y'); options.start({}, { item: $('
'), helper: $('
') }); expect(board.ui.content[0].style.touchAction).toEqual('none'); }); it('sets dragging state', function() { expect(board.dragging).toEqual(false); options.start({}, { item: $('
'), helper: $('
') }); expect(board.dragging).toEqual(true); }); it('actualizes empty placeholders', function() { var firstPlaceholder = board.ui.dragareas.eq(0); var firstPlaceholderCopyAll = firstPlaceholder.find( '.cms-dragbar .cms-submenu-item:has(a[data-rel="copy"]):first' ); var secondPlaceholder = board.ui.dragareas.eq(1); var secondPlaceholderCopyAll = secondPlaceholder.find( '.cms-dragbar .cms-submenu-item:has(a[data-rel="copy"]):first' ); expect(firstPlaceholder).toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); secondPlaceholder .find('> .cms-draggables') .contents() .appendTo(firstPlaceholder.find('> .cms-draggables')); options.start({}, { item: $('
'), helper: $('
') }); expect(firstPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); // now check that the plugin currently being dragged does not count // towards "plugins count" firstPlaceholder .find('> .cms-draggables') .contents() .appendTo(secondPlaceholder.find('> .cms-draggables')); firstPlaceholder .find('> .cms-draggables') .append($('
')); options.start({}, { item: $('
'), helper: $('
') }); expect(firstPlaceholder).toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); }); it('hides settings menu', function() { spyOn(CMS.Plugin, '_hideSettingsMenu'); options.start({}, { item: $('
'), helper: $('
') }); expect(CMS.Plugin._hideSettingsMenu).toHaveBeenCalledTimes(1); }); it('shows all the empty sortables', function() { expect($('.cms-draggables.cms-hidden').length).toEqual(1); options.start({}, { item: $('
'), helper: $('
') }); expect($('.cms-draggables.cms-hidden').length).toEqual(0); }); it('adds appropriate classes on item without children and helper', function() { var item = $('
Some plugin
'); var helper = options.helper(null, item); options.start( {}, { item: item, helper: helper } ); expect(item).toHaveClass('cms-is-dragging'); expect(helper).toHaveClass('cms-draggable-is-dragging'); }); it('adds appropriate classes on item with children', function() { var item = $( '
' + '
Some plugin
' + '
' + '
' + '
' + '
' ); var helper = options.helper(null, item); options.start( {}, { item: item, helper: helper } ); expect(helper).toHaveClass('cms-draggable-stack'); }); it('sets up a handler for interrupting dragging with keyboard', function() { expect(board.ui.doc).not.toHandle('keyup.cms.interrupt'); options.start({}, { item: $('
'), helper: $('
') }); expect(board.ui.doc).toHandle('keyup.cms.interrupt'); var spy = jasmine.createSpy(); board.ui.sortables.on('mouseup', spy); spyOn($.ui.sortable.prototype, '_mouseStop'); var wrongEvent = new $.Event('keyup.cms.interrupt', { keyCode: 1287926834 }); var correctEvent = new $.Event('keyup.cms.interrupt', { keyCode: CMS.KEYS.ESC }); board.state = 'mock'; board.ui.doc.trigger(wrongEvent); expect(board.state).toEqual('mock'); expect($.ui.sortable.prototype._mouseStop).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled(); board.state = 'mock'; board.ui.doc.trigger(wrongEvent, [true]); expect(board.state).toEqual(false); expect($.ui.sortable.prototype._mouseStop).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1 + board.ui.sortables.length); board.ui.doc.off('keyup.cms.interrupt'); options.start({}, { item: $('
'), helper: $('
') }); board.state = 'mock'; board.ui.doc.trigger(correctEvent); expect(board.state).toEqual(false); expect($.ui.sortable.prototype._mouseStop).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledTimes((1 + board.ui.sortables.length) * 2); }); }); describe('beforeStop', function() { it('sets dragging state to false', function() { board.dragging = true; options.beforeStop(null, { item: $('
') }); expect(board.dragging).toEqual(false); }); it('removes classes', function() { var item = $('
'); options.beforeStop(null, { item: item }); expect(item).not.toHaveClass('cms-is-dragging'); expect(item).not.toHaveClass('cms-draggable-stack'); }); it('unbinds interrupt event', function() { var spy = jasmine.createSpy(); board.ui.doc.on('keyup.cms.interrupt', spy); options.beforeStop(null, { item: $('
') }); board.ui.doc.trigger('keyup.cms.interrupt'); expect(spy).not.toHaveBeenCalled(); expect(board.ui.doc).not.toHandle('keyup.cms.interrupt'); }); it('resets touchAction style property', function() { board.ui.content[0].style.touchAction = ''; options.beforeStop(null, { item: $('
') }); expect(board.ui.content[0].style.touchAction).toEqual('pan-y'); }); }); describe('update', function() { it('returns false if it is not possible to update', function() { board.state = false; expect(options.update()).toEqual(false); }); it('actualizes collapsible status', function() { var textPlugin = $('.cms-draggable-1'); var randomPlugin = $('.cms-draggable-2'); var helper = options.helper(null, textPlugin); // we need to start first to set a private variable original container options.start(null, { item: textPlugin, helper: helper }); board.state = true; expect(randomPlugin.find('> .cms-dragitem')).not.toHaveClass('cms-dragitem-collapsable'); expect(randomPlugin.find('> .cms-dragitem')).not.toHaveClass('cms-dragitem-expanded'); textPlugin.appendTo(randomPlugin.find('.cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect(randomPlugin.find('> .cms-dragitem')).toHaveClass('cms-dragitem-collapsable'); expect(randomPlugin.find('> .cms-dragitem')).toHaveClass('cms-dragitem-expanded'); // and back options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.appendTo($('.cms-dragarea-1').find('> .cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect(randomPlugin.find('> .cms-dragitem')).not.toHaveClass('cms-dragitem-collapsable'); expect(randomPlugin.find('> .cms-dragitem')).toHaveClass('cms-dragitem-expanded'); }); it('returns false if we moved plugin inside same container and the event is fired on the container', () => { var textPlugin = $('.cms-draggable-1'); var helper = options.helper(null, textPlugin); var placeholderDraggables = $('.cms-dragarea-1').find('> .cms-draggables'); // and one more time options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.prependTo(placeholderDraggables); expect(options.update.bind(textPlugin)(null, { item: textPlugin, helper: helper })).toEqual(false); }); it('triggers event on the plugin when necessary', function() { var textPlugin = $('.cms-draggable-1'); var randomPlugin = $('.cms-draggable-2'); var helper = options.helper(null, textPlugin); var placeholderDraggables = $('.cms-dragarea-1').find('> .cms-draggables'); var spy = jasmine.createSpy(); textPlugin.on('cms-plugins-update', spy); // we need to start first to set a private variable original container options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.appendTo(randomPlugin.find('.cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect(spy).toHaveBeenCalledTimes(1); // and back options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.appendTo($('.cms-dragarea-1').find('> .cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect(spy).toHaveBeenCalledTimes(2); // and one more time options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.prependTo(placeholderDraggables); options.update.bind(placeholderDraggables)(null, { item: textPlugin, helper: helper }); expect(spy).toHaveBeenCalledTimes(3); }); it('triggers event on the plugin in clipboard', function() { $(fixture.el).prepend( '
' + '
' + '
' ); var textPlugin = $('.cms-draggable-1'); var randomPlugin = $('.cms-draggable-2'); var helper = options.helper(null, textPlugin); textPlugin.prependTo('.cms-clipboard-containers'); var pluginSpy = jasmine.createSpy(); var clipboardSpy = jasmine.createSpy(); textPlugin.on('cms-plugins-update', pluginSpy); textPlugin.on('cms-paste-plugin-update', clipboardSpy); // we need to start first to set a private variable original container options.start(null, { item: textPlugin, helper: helper }); board.state = true; textPlugin.appendTo(randomPlugin.find('.cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect(pluginSpy).not.toHaveBeenCalled(); expect(clipboardSpy).toHaveBeenCalledTimes(1); }); it('actualizes empty placeholders', function() { var firstPlaceholder = board.ui.dragareas.eq(0); var firstPlaceholderCopyAll = firstPlaceholder.find( '.cms-dragbar .cms-submenu-item:has(a[data-rel="copy"]):first' ); var secondPlaceholder = board.ui.dragareas.eq(1); var secondPlaceholderCopyAll = secondPlaceholder.find( '.cms-dragbar .cms-submenu-item:has(a[data-rel="copy"]):first' ); expect(firstPlaceholder).toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); options.start({}, { item: $('
'), helper: $('
') }); secondPlaceholder .find('> .cms-draggables') .contents() .appendTo(firstPlaceholder.find('> .cms-draggables')); board.state = true; options.update({}, { item: $('
'), helper: $('
') }); expect(firstPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); // now check that the plugin currently being dragged does not count // towards "plugins count" firstPlaceholder .find('> .cms-draggables') .contents() .appendTo(secondPlaceholder.find('> .cms-draggables')); firstPlaceholder .find('> .cms-draggables') .append($('
')); options.update({}, { item: $('
'), helper: $('
') }); expect(firstPlaceholder).toHaveClass('cms-dragarea-empty'); expect(firstPlaceholderCopyAll).toHaveClass('cms-submenu-item-disabled'); expect(secondPlaceholder).not.toHaveClass('cms-dragarea-empty'); expect(secondPlaceholderCopyAll).not.toHaveClass('cms-submenu-item-disabled'); }); it('hides empty sortables', function() { var textPlugin = $('.cms-draggable-1'); var randomPlugin = $('.cms-draggable-2'); var helper = options.helper(null, textPlugin); options.start(null, { item: textPlugin, helper: helper }); board.state = true; expect($('.cms-draggables.cms-hidden').length).toEqual(0); textPlugin.appendTo(randomPlugin.find('.cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect($('.cms-draggables.cms-hidden').length).toEqual(0); options.start(null, { item: textPlugin, helper: helper }); board.state = true; expect($('.cms-draggables.cms-hidden').length).toEqual(0); textPlugin.appendTo($('.cms-dragarea-1').find('> .cms-draggables')); options.update(null, { item: textPlugin, helper: helper }); expect($('.cms-draggables.cms-hidden').length).toEqual(1); }); }); describe('isAllowed', function() { it('returns false if CMS.API is locked', function() { CMS.API.locked = true; board.state = 'mock'; expect(options.isAllowed()).toEqual(false); expect(board.state).toEqual('mock'); }); it('returns false if there is no item', function() { CMS.API.locked = false; board.state = 'mock'; expect(options.isAllowed()).toEqual(false); expect(board.state).toEqual('mock'); }); it('returns false if item has no settings', function() { board.state = 'mock'; expect(options.isAllowed(null, null, $('.cms-draggable-1'))).toEqual(false); expect(board.state).toEqual('mock'); }); it('returns false if parent cannot have children', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var pluginEdit = $('.cms-plugin-1'); var placeholder = $('.cms-draggables').eq(0); placeholder.parent().addClass('cms-draggable-disabled'); $('.cms-placeholder-1').remove(); pluginEdit.data('cms', {}); expect(options.isAllowed(placeholder, null, pluginStructure)).toEqual(false); expect(board.state).toEqual('mock'); }); it('returns false if parent is a clipboard', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var pluginEdit = $('.cms-plugin-1'); var placeholder = $('.cms-draggables').eq(0); placeholder.parent().addClass('cms-clipboard-containers'); $('.cms-placeholder-1').remove(); pluginEdit.data('cms', {}); expect(options.isAllowed(placeholder, null, pluginStructure)).toEqual(false); expect(board.state).toEqual('mock'); }); describe('bounds of a place we put current plugin in', function() { it('uses placeholder bounds', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var placeholder = $('.cms-dragarea-1 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.data('cms', {}); placeholderEdit.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); expect(options.isAllowed(placeholder, null, pluginStructure)).toEqual(false); expect(board.state).toEqual(false); }); it('uses placeholder bounds', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var placeholder = $('.cms-dragarea-1 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.data('cms', { plugin_type: 'OnlyThisPlugin' }); placeholderEdit.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); expect(options.isAllowed(placeholder, null, pluginStructure)).toEqual(true); expect(board.state).toEqual(true); }); it('uses plugin bounds if pasted into the plugin', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var parentPluginStructure = $('.cms-draggable-2'); var placeholder = $('.cms-draggable-2 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.appendTo(parentPluginStructure.find('> .cms-draggables')); pluginStructure.data('cms', { plugin_type: 'OtherPlugin' }); parentPluginStructure.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); placeholderEdit.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); expect(options.isAllowed(placeholder, null, $('.cms-draggable-1'))).toEqual(false); expect(board.state).toEqual(false); }); it('uses plugin bounds if pasted into the plugin', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var parentPluginStructure = $('.cms-draggable-2'); var placeholder = $('.cms-draggable-2 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.appendTo(parentPluginStructure.find('> .cms-draggables')); pluginStructure.data('cms', { plugin_type: 'OtherPlugin' }); parentPluginStructure.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); placeholderEdit.data('cms', { plugin_restriction: ['OtherPlugin'] }); expect(options.isAllowed(placeholder, null, $('.cms-draggable-1'))).toEqual(false); expect(board.state).toEqual(false); }); it('uses plugin bounds if pasted into the plugin', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var parentPluginStructure = $('.cms-draggable-2'); var placeholder = $('.cms-draggable-2 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.appendTo(parentPluginStructure.find('> .cms-draggables')); pluginStructure.data('cms', { plugin_type: 'OtherPlugin' }); parentPluginStructure.data('cms', { plugin_restriction: [] }); placeholderEdit.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); expect(options.isAllowed(placeholder, null, $('.cms-draggable-1'))).toEqual(true); expect(board.state).toEqual(true); }); it('uses placeholderParent bounds', function() { board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var parentPluginStructure = $('.cms-draggable-2'); var placeholder = $('.cms-draggable-2 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.appendTo(parentPluginStructure.find('> .cms-draggables')); pluginStructure.data('cms', { plugin_type: 'OtherPlugin' }); parentPluginStructure.data('cms', { plugin_restriction: [] }); placeholderEdit.data('cms', { plugin_restriction: ['OnlyThisPlugin'] }); // it's important that placeholder is used, and not .cms-draggable-1 expect(options.isAllowed($('.cms-draggable-1'), placeholder, $('.cms-draggable-1'))).toEqual(true); expect(board.state).toEqual(true); }); }); it('blocks a plugin that the target placeholder does not allow at its root', function() { // A plugin that requires a parent is excluded from the placeholder's root list // server-side, so it is blocked at the root purely by the bounds (child list) check. board.state = 'mock'; var pluginStructure = $('.cms-draggable-1'); var placeholder = $('.cms-dragarea-1 > .cms-draggables'); var placeholderEdit = $('.cms-placeholder-1'); pluginStructure.data('cms', { plugin_type: 'OtherPlugin' }); placeholderEdit.data('cms', { plugin_restriction: ['OnlyRootPlugin'] }); expect(options.isAllowed(placeholder, null, pluginStructure)).toEqual(false); expect(board.state).toEqual(false); }); }); }); describe('invalidateState', () => { let board; beforeEach(done => { fixture.load('plugins.html'); board = new CMS.StructureBoard(); rewire('Plugin', FakePlugin); spyOn(StructureBoard, 'actualizePluginCollapseStatus'); spyOn(StructureBoard, 'actualizePlaceholders'); spyOn(StructureBoard, 'actualizePluginsCollapsibleStatus'); spyOn(Plugin, '_updateClipboard'); spyOn(board, '_drag'); setTimeout(() => { done(); }, 20); }); afterEach(() => { fixture.cleanup(); resetRewire('Plugin'); }); describe('itself', () => { beforeEach(() => { spyOn(CMS.API.Helpers, 'reloadBrowser'); CMS.API.Toolbar = { _refreshMarkup: jasmine.createSpy() }; spyOn(board, 'handleAddPlugin'); spyOn(board, 'handleEditPlugin'); spyOn(board, 'handleDeletePlugin'); spyOn(board, 'handleClearPlaceholder'); spyOn(board, 'handleCopyPlugin'); spyOn(board, 'handleMovePlugin'); spyOn(board, 'handleCutPlugin'); spyOn(board, '_loadToolbar').and.returnValue({ done() { return { fail() {} }; } }); spyOn(board, '_requestMode').and.returnValue({ done() { return { fail() {} }; } }); spyOn(board, 'refreshContent'); }); it('delegates to correct methods', () => { board.invalidateState('ADD', { randomData: 1 }); expect(board.handleAddPlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('MOVE', { randomData: 1 }); expect(board.handleMovePlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('COPY', { randomData: 1 }); expect(board.handleCopyPlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('PASTE', { randomData: 1 }); expect(board.handleMovePlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('CUT', { randomData: 1 }); expect(board.handleCutPlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('EDIT', { randomData: 1 }); expect(board.handleEditPlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('DELETE', { randomData: 1 }); expect(board.handleDeletePlugin).toHaveBeenCalledWith({ randomData: 1 }); board.invalidateState('CLEAR_PLACEHOLDER', { randomData: 1 }); expect(board.handleClearPlaceholder).toHaveBeenCalledWith({ randomData: 1 }); expect(board.handleAddPlugin).toHaveBeenCalledTimes(1); expect(board.handleCopyPlugin).toHaveBeenCalledTimes(1); expect(board.handleMovePlugin).toHaveBeenCalledTimes(2); expect(board.handleCutPlugin).toHaveBeenCalledTimes(1); expect(board.handleEditPlugin).toHaveBeenCalledTimes(1); expect(board.handleDeletePlugin).toHaveBeenCalledTimes(1); expect(board.handleClearPlaceholder).toHaveBeenCalledTimes(1); }); it('reloads browser if there was no action', () => { board.invalidateState(); expect(CMS.API.Helpers.reloadBrowser).toHaveBeenCalledWith(); expect(board._loadToolbar).not.toHaveBeenCalled(); }); it('reloads toolbar if there is an action', () => { board.invalidateState('ADD', { randomData: 1 }); board.invalidateState('MOVE', { randomData: 1 }); board.invalidateState('COPY', { randomData: 1 }); board.invalidateState('PASTE', { randomData: 1 }); board.invalidateState('CUT', { randomData: 1 }); board.invalidateState('EDIT', { randomData: 1 }); board.invalidateState('DELETE', { randomData: 1 }); board.invalidateState('CLEAR_PLACEHOLDER', { randomData: 1 }); expect(board._loadToolbar).toHaveBeenCalledTimes(8); board.invalidateState('x'); expect(board._loadToolbar).toHaveBeenCalledTimes(9); }); it('refreshes markup if loading markup succeeds', () => { board._loadToolbar.and.returnValue({ done(fn) { fn(); return { fail() {} }; } }); board.invalidateState('x'); expect(CMS.API.Toolbar._refreshMarkup).toHaveBeenCalled(); }); it('reloads if fails', () => { board._loadToolbar.and.returnValue({ done() { return { fail(fn) { fn(); } }; } }); board.invalidateState('x'); expect(CMS.API.Helpers.reloadBrowser).toHaveBeenCalled(); }); it('resets the requested content', () => { const args = ['random', 'stuff']; expect(CMS.settings.mode).toEqual('structure'); board._loadedContent = false; board._requestMode.and.callFake(mode => { expect(mode).toEqual('content'); return { done(fn) { fn(...args); return { fail() {} }; } }; }); board._requestcontent = '1'; board.invalidateState('x'); expect(board._requestcontent).toEqual(null); expect(board.refreshContent).not.toHaveBeenCalled(); }); it('refreshes content mode if needed', () => { const args = ['random', 'stuff']; expect(CMS.settings.mode).toEqual('structure'); board._loadedContent = true; board._requestMode.and.callFake(mode => { expect(mode).toEqual('content'); return { done(fn) { fn(...args); return { fail() {} }; } }; }); board.invalidateState('x'); expect(board.refreshContent).toHaveBeenCalledWith('random'); }); it('reloads if it cannot refresh content', () => { const args = ['random', 'stuff']; expect(CMS.settings.mode).toEqual('structure'); board._loadedContent = true; board._requestMode.and.callFake(mode => { expect(mode).toEqual('content'); return { done() { return { fail(fn) { fn(...args); } }; } }; }); board.invalidateState('x'); expect(CMS.API.Helpers.reloadBrowser).toHaveBeenCalledWith(); }); it('refreshes content if in content mode', () => { const args = ['random', 'stuff']; CMS.settings.mode = 'edit'; board._loadedContent = true; board._requestMode.and.callFake(mode => { expect(mode).toEqual('content'); expect(board._requestcontent).toEqual(null); return { done(fn) { fn(...args); return { fail() {} }; } }; }); board.invalidateState('x'); expect(board._requestMode).toHaveBeenCalledWith('content'); expect(board.refreshContent).toHaveBeenCalledWith('random'); }); it('reloads if cant refresh content when in content mode', () => { const args = ['random', 'stuff']; CMS.settings.mode = 'edit'; board._loadedContent = false; board._requestMode.and.callFake(mode => { expect(mode).toEqual('content'); expect(board._requestcontent).toEqual(null); return { done() { return { fail(fn) { fn(...args); } }; } }; }); board.invalidateState('x'); expect(board._requestMode).toHaveBeenCalledWith('content'); expect(CMS.API.Helpers.reloadBrowser).toHaveBeenCalledWith(); }); }); describe('handleMovePlugin', () => { it('replaces markup with given one', () => { const data = { plugin_parent: false, plugin_id: 1, html: '
', plugins: [{ plugin_id: 1, otherStuff: true }] }; board.handleMovePlugin(data); expect($('.cms-draggable-1')).not.toExist(); expect($('.new-draggable')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(1); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); it('replaces markup with given one when parent is also provided', () => { const data = { plugin_parent: 2, plugin_id: 1, html: `
`, plugins: [{ plugin_id: 1, otherStuff: true }] }; board.handleMovePlugin(data); expect($('.cms-draggable-1')).toExist(); expect($('.cms-draggable-2')).not.toExist(); expect($('.new-draggable')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(1); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); it('replaces markup with given one when it is copying from language', () => { const data = { target_placeholder_id: 2, html: '
', plugins: [{ plugin_id: 1, otherStuff: true }] }; board.handleMovePlugin(data); expect($('.cms-draggable-1')).toExist(); expect($('.cms-draggable-2')).toExist(); expect($('.new-draggable')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(1); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.cms-dragarea-2 .new-draggable .cms-draggables')).toExist(); }); it('handles top level move update when in same placeholder', () => { const data = { placeholder_id: 1, plugin_id: 2, plugin_order: ['2', '1'], html: `
`, plugins: [{ plugin_id: 2, otherStuff: true }, { plugin_id: 1, otherStuff: true }] }; board.handleMovePlugin(data); expect($('.cms-draggable-1')).toExist(); expect($('.cms-draggable-2')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(2); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(2); expect(board._drag).toHaveBeenCalled(); expect($('.new-cms-draggable-2')).toExist(); expect($('.cms-draggable-2').index()).toEqual(0); // account for "empty" message expect($('.cms-draggable-1').index()).toEqual(2); }); it('handles top level move update when in same placeholder', () => { const data = { placeholder_id: 1, plugin_id: 1, plugin_order: ['2', '1'], html: `
`, plugins: [{ plugin_id: 2, otherStuff: true }, { plugin_id: 1, otherStuff: true }] }; board.handleMovePlugin(data); expect($('.cms-draggable-1')).toExist(); expect($('.cms-draggable-2')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(2); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(2); expect(board._drag).toHaveBeenCalled(); expect($('.new-cms-draggable-1')).toExist(); expect($('.cms-draggable-2').index()).toEqual(1); expect($('.cms-draggable-1').index()).toEqual(2); }); }); describe('handleAddPlugin', () => { it('replaces parent if provided', () => { const data = { plugin_parent: 2, plugin_id: 3, structure: { html: `
`, plugins: [{ plugin_id: 2, otherStuff: true }, { plugin_id: 3, other_stuff: false }] } }; board.handleAddPlugin(data); expect($('.cms-draggable-2')).toHaveClass('new'); expect($('.new-draggable')).toExist(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.structure.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(2); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(2); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(3); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); it('appends to placeholder if no parent', () => { const data = { plugin_parent: null, placeholder_id: 2, plugin_id: 3, structure: { html: `
`, plugins: [{ plugin_id: 3, other_stuff: false }] } }; board.handleAddPlugin(data); expect($('.cms-draggable-2')).not.toHaveClass('new'); expect($('.cms-dragarea-2 > .cms-draggables > .new-draggable')).toBeInDOM(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.structure.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(3); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); }); describe('handleEditPlugin', () => { it('replaces edited plugin with new markup', () => { const data = { plugin_parent: null, plugin_id: 2, structure: { html: `
`, plugins: [{ plugin_id: 3, other_stuff: false }] } }; board.handleEditPlugin(data); expect($('.cms-draggable-2')).not.toExist(); expect($('.new-draggable')).toBeInDOM(); expect(StructureBoard.actualizePlaceholders).not.toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.structure.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(3); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); it('replaces parent of edited plugin if it was passed', () => { const data = { plugin_parent: 2, plugin_id: 3, structure: { html: `
`, plugins: [{ plugin_id: 3, other_stuff: false }] } }; board.handleEditPlugin(data); expect($('.cms-draggable-2')).not.toExist(); expect($('.new-draggable')).toBeInDOM(); expect(StructureBoard.actualizePlaceholders).not.toHaveBeenCalled(); expect(FakePlugin._updateRegistry).toHaveBeenCalledWith(data.structure.plugins); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledTimes(1); expect(StructureBoard.actualizePluginCollapseStatus).toHaveBeenCalledWith(3); expect(board._drag).toHaveBeenCalled(); expect(board.ui.sortables.filter('.new-draggable .cms-draggables')).toExist(); }); }); describe('handleDeletePlugin', () => { it('removes plugin', () => { CMS._plugins = [['cms-plugin-1', { plugin_id: 1 }], ['cms-plugin-2']]; CMS._instances = [{ options: { plugin_id: 1 } }, { options: { plugin_id: 2 } }]; board.handleDeletePlugin({ plugin_id: 1 }); expect($('.cms-draggable-1')).not.toBeInDOM(); expect(StructureBoard.actualizePluginsCollapsibleStatus).toHaveBeenCalledWith( $('.cms-dragarea-1 > .cms-draggables') ); expect(CMS._plugins).toEqual([['cms-plugin-2']]); expect(CMS._instances).toEqual([{ options: { plugin_id: 2 } }]); }); it('removes plugin and its children', () => { CMS._plugins = [['cms-plugin-3', { plugin_id: 3 }], ['cms-plugin-4']]; CMS._instances = [{ options: { plugin_id: 3 } }, { options: { plugin_id: 4 } }]; $('.cms-draggable-2').find('.cms-draggables').append(`
`); expect($('.cms-draggable-3')).toBeInDOM(); expect($('.cms-draggable-4')).toBeInDOM(); board.handleDeletePlugin({ plugin_id: 3 }); expect($('.cms-draggable-3')).not.toBeInDOM(); expect($('.cms-draggable-4')).not.toBeInDOM(); expect(StructureBoard.actualizePluginsCollapsibleStatus).toHaveBeenCalledWith( $('.cms-draggable-2 > .cms-draggables') ); expect(CMS._plugins).toEqual([]); expect(CMS._instances).toEqual([]); }); }); describe('handleClearPlaceholder', () => { it('clears placeholder', () => { CMS._plugins = [ ['cms-plugin-3'], ['cms-plugin-1', { plugin_id: 1, placeholder_id: 1 }], ['cms-plugin-2', { plugin_id: '2', placeholder_id: 1 }] ]; CMS._instances = [ { options: { plugin_id: 3 } }, { options: { plugin_id: 1, placeholder_id: 1 } }, { options: { plugin_id: '2', placeholder_id: '1' } } ]; expect($('.cms-draggable-1')).toBeInDOM(); expect($('.cms-draggable-2')).toBeInDOM(); board.handleClearPlaceholder({ placeholder_id: 1 }); expect($('.cms-draggable-1')).not.toBeInDOM(); expect($('.cms-draggable-2')).not.toBeInDOM(); expect(StructureBoard.actualizePluginsCollapsibleStatus).not.toHaveBeenCalled(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(CMS._plugins).toEqual([['cms-plugin-3']]); expect(CMS._instances).toEqual([{ options: { plugin_id: 3 } }]); }); }); describe('handleCopyPlugin', () => { const clipboardConstructor = jasmine.createSpy(); const close = jasmine.createSpy(); // Use a function constructor instead of class to allow prototype mutation in tests function FakeClipboard(...args) { clipboardConstructor(...args); } // Assign methods without replacing the non-writable ES6 class prototype Object.assign(FakeClipboard.prototype, { _isClipboardModalOpen: jasmine.createSpy(), modal: { close: jasmine.createSpy() }, populate: jasmine.createSpy(), _enableTriggers: jasmine.createSpy() }); beforeEach(() => { CMS.API.Clipboard = { _isClipboardModalOpen: jasmine.createSpy(), modal: { close }, populate: jasmine.createSpy(), _enableTriggers: jasmine.createSpy() }; rewire('Clipboard', FakeClipboard); rewire('Plugin', FakePlugin); fixture.load('clipboard.html'); }); afterEach(() => { resetRewire('Clipboard'); resetRewire('Plugin'); }); it('updates the clipboard', () => { const data = { plugins: [{ plugin_id: 10, stuff: true }], html: '
' }; CMS._plugins = []; CMS._instances = []; board.handleCopyPlugin(data); expect(CMS._plugins).toEqual([['cms-plugin-10', data.plugins[0]]]); expect(CMS._instances).toEqual([jasmine.any(FakePlugin)]); expect(FakePlugin._updateClipboard).toHaveBeenCalled(); expect(clipboardConstructor).toHaveBeenCalled(); expect(FakeClipboard.prototype.populate).toHaveBeenCalled(); expect(FakeClipboard.prototype._enableTriggers).toHaveBeenCalled(); expect(board.ui.sortables.find('.new-clipboard-draggable')).toExist(); expect(board._drag).toHaveBeenCalled(); expect(pluginConstructor).toHaveBeenCalledWith('cms-plugin-10', data.plugins[0]); expect(CMS.API.Clipboard.modal.close).not.toHaveBeenCalled(); }); it('closes clipboard modal if needed', () => { CMS.API.Clipboard._isClipboardModalOpen.and.returnValue(true); const data = { plugins: [{ plugin_id: 10, stuff: true }], html: '
' }; board.handleCopyPlugin(data); expect(close).toHaveBeenCalled(); }); }); describe('handleCutPlugin', () => { it('calls delete and copy', () => { spyOn(board, 'handleDeletePlugin'); spyOn(board, 'handleCopyPlugin'); board.handleCutPlugin({ randomData: true }); expect(board.handleDeletePlugin).toHaveBeenCalledWith({ randomData: true }); expect(board.handleCopyPlugin).toHaveBeenCalledWith({ randomData: true }); }); }); }); describe('_loadToolbar', () => { it('loads toolbar url', () => { const board = new StructureBoard(); spyOn($, 'ajax'); CMS.config.settings.structure = '/structure'; CMS.config.settings.edit = '/edit'; CMS.config.settings.mode = 'edit'; CMS.config.request = { toolbar: 'TOOLBAR_URL', pk: 100, model: 'cms.page', language: 'en' }; board._loadToolbar(); expect($.ajax).toHaveBeenCalledWith({ url: jasmine.stringMatching( /TOOLBAR_URL\?obj_id=100&obj_type=cms.page&language=en&cms_path=%2Fstructure*/ ) }); }); }); describe('_requestMode', () => { let board; let request; const preloadImages = jasmine.createSpy(); beforeEach(() => { board = new StructureBoard(); rewire('preloadImagesFromMarkup', preloadImages); }); afterEach(() => { resetRewire('preloadImagesFromMarkup'); preloadImages.calls.reset(); }); it('requests content', () => { request = { then(fn) { fn('markup'); return request; } }; spyOn($, 'ajax').and.callFake(req => { expect(req.method).toEqual('GET'); expect(req.url).toEqual('/edit'); return request; }); expect(board._requestcontent).not.toBeDefined(); board._requestMode('content'); expect(preloadImages).toHaveBeenCalledWith('markup'); expect(board._requestcontent).toBe(request); }); it('requests structure', () => { request = { then(fn) { fn('markup'); return request; } }; spyOn($, 'ajax').and.callFake(req => { expect(req.method).toEqual('GET'); expect(req.url).toEqual('/structure'); return request; }); expect(board._requeststructure).not.toBeDefined(); board._requestMode('structure'); expect(preloadImages).toHaveBeenCalledWith('markup'); expect(board._requeststructure).toBe(request); }); it('reuses same promise if it was not reset', () => { request = { then(fn) { fn('markup'); return request; } }; spyOn($, 'ajax').and.callFake(req => { expect(req.method).toEqual('GET'); expect(req.url).toEqual('/edit'); return request; }); expect(board._requestcontent).not.toBeDefined(); board._requestMode('content'); expect(preloadImages).toHaveBeenCalledWith('markup'); expect(board._requestcontent).toBe(request); board._requestMode('content'); expect(preloadImages).toHaveBeenCalledTimes(1); expect(board._requestcontent).toBe(request); }); }); describe('_loadContent', () => { let board; let response = ''; let requestSucceeded; beforeEach(() => { requestSucceeded = jasmine.createSpy(); CMS.API.Toolbar = { _refreshMarkup: jasmine.createSpy() }; board = new StructureBoard(); CMS.config = { settings: { mode: 'structure' } }; spyOn(board, '_requestMode').and.returnValue({ done(fn) { requestSucceeded(); fn(response); return { fail() { return { then(callback) { callback(); } }; } }; } }); rewire('Plugin', FakePlugin); rewire('Plugin', FakePlugin); }); afterEach(() => { resetRewire('Plugin'); resetRewire('Plugin', FakePlugin); }); it('resolves immediately when content mode is already loaded', done => { CMS.config.settings.mode = 'edit'; board._loadContent().then(() => { expect(requestSucceeded).not.toHaveBeenCalled(); done(); }); CMS.config.settings.mode = 'structure'; }); it('resolves immediately when content mode is already loaded', done => { board._loadedContent = true; board._loadContent().then(() => { expect(requestSucceeded).not.toHaveBeenCalled(); done(); }); CMS.config.settings.mode = 'structure'; }); it('requests content', done => { response = ` i am a new title

New body content yay

`; CMS._instances = [ new FakePlugin('cms-plugin-1', { plugin_id: 1, type: 'plugin' }), new FakePlugin('cms-placeholder-1', { placeholder_id: 1, type: 'placeholder' }), new FakePlugin('cms-plugin-existing-generic-1', { plugin_id: '1', type: 'generic' }) ]; CMS._plugins = [ ['cms-plugin-1', { plugin_id: 1, type: 'plugin' }], ['cms-plugin-1', { plugin_id: 1, type: 'plugin' }], ['cms-placeholder-1', { placeholder_id: 1, type: 'placeholder' }], ['cms-plugin-existing-generic-1', { plugin_id: 1, type: 'generic' }], ['cms-plugin-new-generic-2', { plugin_id: 2, type: 'generic' }] ]; expect(board._loadedContent).not.toBeDefined(); pluginConstructor.calls.reset(); // FakePlugin._refreshPlugins() delegates to the real implementation, // and that one instantiates cms.plugins' own reference to itself - // something vi.mock cannot swap, so the instances it creates are real // plugins and the spies have to sit on the real prototype. spyOn(originalPlugin.prototype, '_setupUI').and.callFake(function() { const el = $('
'); el.data('cms', []); this.ui = { container: el }; }); spyOn(originalPlugin.prototype, '_ensureData'); spyOn(originalPlugin.prototype, '_setGeneric'); spyOn(originalPlugin.prototype, '_setPluginContentEvents'); const instancesBefore = CMS._instances.length; board._loadContent().then(() => { expect(showLoader).toHaveBeenCalled(); expect(hideLoader).toHaveBeenCalled(); expect(CMS.API.Toolbar._refreshMarkup).toHaveBeenCalled(); expect($('html').attr('attr0')).toEqual('a'); expect($('html').attr('attr1')).toEqual('b'); expect($('body').attr('attr2')).toEqual('x'); expect($('body').attr('attr3')).toEqual('y'); expect($('.new-content')).toBeInDOM(); expect(FakePlugin.prototype._ensureData).toHaveBeenCalledTimes(3); expect(originalPlugin.prototype._ensureData).toHaveBeenCalledTimes(1); expect(originalPlugin.prototype._setGeneric).toHaveBeenCalledTimes(1); expect(FakePlugin.prototype._setPluginContentEvents).toHaveBeenCalledTimes(1); expect(CMS._instances.length).toEqual(instancesBefore + 1); $('.new-content').remove(); expect(board._loadedContent).toBe(true); done(); }); }); }); describe('_loadStructure', () => { let board; let response = ''; let requestSucceeded; beforeEach(() => { requestSucceeded = jasmine.createSpy(); CMS.API.Toolbar = { _refreshMarkup: jasmine.createSpy() }; board = new StructureBoard(); CMS.config = { settings: { mode: 'edit' } }; spyOn(StructureBoard, '_initializeGlobalHandlers'); spyOn(StructureBoard, 'actualizePlaceholders'); spyOn(StructureBoard, '_initializeDragItemsStates'); spyOn(board, '_drag'); spyOn(board, '_requestMode').and.returnValue({ done(fn) { requestSucceeded(); fn(response); return { fail() { return { then(callback) { callback(); } }; } }; } }); rewire('Plugin', FakePlugin); }); afterEach(() => { resetRewire('Plugin'); }); it('resolves immediately when structure mode is already loaded', done => { CMS.config.settings.mode = 'structure'; board._loadStructure().then(() => { expect(requestSucceeded).not.toHaveBeenCalled(); done(); }); CMS.config.settings.mode = 'edit'; }); it('resolves immediately when structure mode is already loaded', done => { board._loadedStructure = true; board._loadStructure().then(() => { expect(requestSucceeded).not.toHaveBeenCalled(); done(); }); CMS.config.settings.mode = 'edit'; }); it('requests structure', done => { response = ` i am a new title

New body content yay

`; CMS._instances = [ new FakePlugin('cms-plugin-1', { plugin_id: 1, type: 'plugin' }), new FakePlugin('cms-placeholder-1', { placeholder_id: 1, type: 'placeholder' }), new FakePlugin('cms-plugin-existing-generic-1', { plugin_id: '1', type: 'generic' }) ]; expect(board._loadedStructure).not.toBeDefined(); pluginConstructor.calls.reset(); board._loadStructure().then(() => { expect(showLoader).toHaveBeenCalled(); expect(hideLoader).toHaveBeenCalled(); expect(CMS.API.Toolbar._refreshMarkup).toHaveBeenCalled(); expect(FakePlugin.prototype._setPlaceholder).toHaveBeenCalledTimes(1); expect(FakePlugin.prototype._setPluginStructureEvents).toHaveBeenCalledTimes(1); expect(FakePlugin.prototype._setGeneric).not.toHaveBeenCalled(); expect(pluginConstructor).not.toHaveBeenCalled(); expect(board._drag).toHaveBeenCalled(); expect(StructureBoard._initializeDragItemsStates).toHaveBeenCalled(); expect(StructureBoard._initializeGlobalHandlers).toHaveBeenCalled(); expect(StructureBoard.actualizePlaceholders).toHaveBeenCalled(); expect(board._loadedStructure).toBe(true); done(); }); }); }); describe('_getPluginDataFromMarkup()', () => { const wrap = markup => $(`
${markup}
`)[0]; [ { args: [wrap(''), [1, 2, 3]], expected: [] }, { args: [wrap('whatever'), []], expected: [] }, { args: [wrap(''), [1, 2, 3]], expected: [] }, { args: [wrap(''), [1, 2, 4]], expected: [{ plugin_id: '4' }] }, { args: [ wrap('' + ''), [1, 2, 10]], expected: [{ plugin_id: 'meh' }] }, { args: [wrap(''), [4]], expected: [] }, { args: [wrap(''), [4]], expected: [] }, { args: [wrap(''), [4]], expected: [{ something: 1 }] } ].forEach((test, i) => { it(`extracts plugin data from markup ${i}`, () => { expect(StructureBoard._getPluginDataFromMarkup(...test.args)).toEqual(test.expected); }); }); }); describe('_extractMessages()', () => { let board; beforeEach(() => { board = new StructureBoard(); }); it('extracts messages', () => { expect( board._extractMessages( $(`
Error
Normal
`) ) ).toEqual([{ message: 'Error', error: true }, { message: 'Normal', error: false }]); expect( board._extractMessages( $(`
Error1
Normal1
`) ) ).toEqual([{ message: 'Error1', error: true }, { message: 'Normal1', error: false }]); expect( board._extractMessages( $(`
`) ) ).toEqual([{ message: 'normal message', error: false }, { message: 'error message', error: true }]); expect( board._extractMessages( $(`
`) ) ).toEqual([]); }); }); describe('_refreshContent()', () => { let board; const diffDOMConstructor = jasmine.createSpy(); const newDoc = `
new markup
`; class DiffDOM { constructor() { diffDOMConstructor(); } } DiffDOM.prototype.diff = jasmine.createSpy(); DiffDOM.prototype.apply = jasmine.createSpy(); let originalDOMParser; class FakeDOMParser {} FakeDOMParser.prototype.parseFromString = jasmine.createSpy().and.returnValue({ head: 'fakeNewHead', body: 'fakeNewBody' }); beforeEach(() => { rewire('DiffDOM', DiffDOM); // DOMParser is a browser global in the module under test originalDOMParser = window.DOMParser; window.DOMParser = FakeDOMParser; board = new StructureBoard(); spyOn(board, '_replaceBodyWithHTML'); CMS.API.Messages = { open: jasmine.createSpy() }; CMS.API.Toolbar = { _refreshMarkup: jasmine.createSpy() }; }); afterEach(() => { resetRewire('DiffDOM'); window.DOMParser = originalDOMParser; }); it('resets loaded content flag', () => { CMS._instances = []; board._requestcontent = {}; board._loadedContent = false; board.refreshContent(newDoc); expect(board._requestcontent).toBe(null); expect(board._loadedContent).toBe(true); }); it('shows messages', done => { CMS._instances = []; spyOn(board, '_extractMessages').and.returnValue([{ message: 'hello' }]); board.refreshContent(newDoc); setTimeout(() => { expect(CMS.API.Messages.open).toHaveBeenCalledWith({ message: 'hello' }); done(); }, 20); }); it('resets plugin instances', () => { CMS._instances = [ new FakePlugin('cms-plugin-1', { plugin_id: 1, type: 'plugin' }), new FakePlugin('cms-placeholder-1', { placeholder_id: 1, type: 'placeholder' }) ]; board.refreshContent(newDoc); expect(FakePlugin.prototype._setupUI).toHaveBeenCalledTimes(2); expect(FakePlugin.prototype._setupUI).toHaveBeenCalledWith('cms-plugin-1'); expect(FakePlugin.prototype._setupUI).toHaveBeenCalledWith('cms-placeholder-1'); expect(FakePlugin.prototype._ensureData).toHaveBeenCalledTimes(2); expect(FakePlugin.prototype._setPlaceholder).toHaveBeenCalledTimes(1); expect(FakePlugin.prototype._setPluginContentEvents).toHaveBeenCalledTimes(1); }); }); describe('_updateContentFromDataBridge', () => { beforeEach(() => { CMS.API.StructureBoard = new StructureBoard(); }); it('should return true if data is missing or incomplete', () => { const incompleteData = {}; expect(CMS.API.StructureBoard._updateContentFromDataBridge(incompleteData)).toBe(true); const missingContent = { content: [] }; expect(CMS.API.StructureBoard._updateContentFromDataBridge(missingContent)).toBe(true); const missingPluginIds = { content: [{ html: '
' }] }; expect(CMS.API.StructureBoard._updateContentFromDataBridge(missingPluginIds)).toBe(true); }); it('should return true if source placeholder is not found', () => { const data = { source_placeholder_id: 1, content: { pluginIds: [1], html: '
' } }; spyOn(CMS._instances, 'some').and.returnValue(false); expect(CMS.API.StructureBoard._updateContentFromDataBridge(data)).toBe(true); }); it('should update content and return false if data is valid', () => { const data = { content: [{ pluginIds: [1], html: '' + '
My new plugin
' + '', placeholder_id: 1, position: 0 }] }; spyOn(CMS.API.StructureBoard, '_findNextElement').and.returnValue($('
')); spyOn(CMS.API.StructureBoard, '_updateSekizai'); spyOn(CMS.API.StructureBoard, '_contentChanged'); expect(CMS.API.StructureBoard._updateContentFromDataBridge(data)).toBe(false); expect(CMS.API.StructureBoard._updateSekizai).toHaveBeenCalledWith(data.content[0], 'css'); expect(CMS.API.StructureBoard._updateSekizai).toHaveBeenCalledWith(data.content[0], 'js'); expect(CMS.API.StructureBoard._contentChanged).toHaveBeenCalledWith(undefined); }); it('should remove previous content and update Sekizai blocks', () => { const data = { content: [{ pluginIds: [1, 2], html: '
', placeholder_id: 1, position: 0, css: '', js: '' }] }; spyOn(CMS.API.StructureBoard, '_findNextElement').and.returnValue($('
')); spyOn(CMS.API.StructureBoard, '_updateSekizai'); spyOn(CMS.API.StructureBoard, '_contentChanged'); $('body').append('
'); $('body').append('
'); $('body').append(''); $('body').append(''); expect(CMS.API.StructureBoard._updateContentFromDataBridge(data)).toBe(false); expect($('.cms-plugin.cms-plugin-1').length).toBe(0); expect($('.cms-plugin.cms-plugin-2').length).toBe(0); expect($('script[data-cms-plugin]#cms-plugin-1').length).toBe(0); expect($('script[data-cms-plugin]#cms-plugin-2').length).toBe(0); expect(CMS.API.StructureBoard._updateSekizai).toHaveBeenCalledWith(data.content[0], 'css'); expect(CMS.API.StructureBoard._updateSekizai).toHaveBeenCalledWith(data.content[0], 'js'); expect(CMS.API.StructureBoard._contentChanged).toHaveBeenCalledWith(undefined); }); }); describe('_preloadOppositeMode', () => { ['content', 'structure'].forEach(mode => { it(`preloads the opposite mode (${mode})`, () => { const div = document.createElement('div'); const _getWindow = jasmine.createSpy().and.returnValue(div); spyOn(Helpers, '_getWindow').and.callFake(_getWindow); const board = new StructureBoard(); spyOn(board, '_requestMode'); jasmine.clock().install(); if (mode === 'content') { board._loadedStructure = false; } else { board._loadedStructure = true; } board._preloadOppositeMode(); expect(board._requestMode).not.toHaveBeenCalled(); $(div).trigger('load'); expect(board._requestMode).not.toHaveBeenCalled(); jasmine.clock().tick(2001); expect(board._requestMode).toHaveBeenCalledTimes(1); expect(board._requestMode).toHaveBeenCalledWith(mode === 'content' ? 'structure' : 'content'); jasmine.clock().uninstall(); }); }); it('does not preload the opposite mode (legacy renderer)', () => { const div = document.createElement('div'); const _getWindow = jasmine.createSpy().and.returnValue(div); spyOn(Helpers, '_getWindow').and.callFake(_getWindow); const board = new StructureBoard(); spyOn(board, '_requestMode'); jasmine.clock().install(); CMS.config.settings.legacy_mode = true; board._loadedContent = true; board._loadedStructure = true; board._preloadOppositeMode(); expect(board._requestMode).not.toHaveBeenCalled(); $(div).trigger('load'); expect(board._requestMode).not.toHaveBeenCalled(); jasmine.clock().tick(2001); expect(board._requestMode).not.toHaveBeenCalled(); jasmine.clock().uninstall(); }); }); describe('actualizePluginCollapseStatus', () => { it('works', () => { $('#fixture_container').append(`
`); CMS.settings = { states: ['1\n'] }; StructureBoard.actualizePluginCollapseStatus(1); expect('.cms-draggable-1 .cms-collapsable-container').not.toHaveClass('cms-hidden'); expect('.cms-draggable-1 .cms-dragitem').toHaveClass('cms-dragitem-expanded'); $('#fixture_container').empty(); }); }); });