diff --git a/core/events/events_block_create.js b/core/events/events_block_create.js index 2a6afb003fc..67afa54855f 100644 --- a/core/events/events_block_create.js +++ b/core/events/events_block_create.js @@ -18,9 +18,9 @@ const Block = goog.requireType('Blockly.Block'); const BlockBase = goog.require('Blockly.Events.BlockBase'); const Events = goog.require('Blockly.Events'); const Xml = goog.require('Blockly.Xml'); +const blocks = goog.require('Blockly.serialization.blocks'); const object = goog.require('Blockly.utils.object'); const registry = goog.require('Blockly.registry'); -const xml = goog.require('Blockly.utils.xml'); /** @@ -40,12 +40,15 @@ const BlockCreate = function(opt_block) { this.recordUndo = false; } - if (opt_block.workspace.rendered) { - this.xml = Xml.blockToDomWithXY(opt_block); - } else { - this.xml = Xml.blockToDom(opt_block); - } + this.xml = Xml.blockToDomWithXY(opt_block); this.ids = Events.getDescendantIds(opt_block); + + /** + * JSON representation of the block that was just created. + * @type {!blocks.State} + */ + this.json = /** @type {!blocks.State} */ (blocks.save( + opt_block, {addCoordinates: true})); }; object.inherits(BlockCreate, BlockBase); @@ -63,6 +66,7 @@ BlockCreate.prototype.toJson = function() { const json = BlockCreate.superClass_.toJson.call(this); json['xml'] = Xml.domToText(this.xml); json['ids'] = this.ids; + json['json'] = this.json; if (!this.recordUndo) { json['recordUndo'] = this.recordUndo; } @@ -77,6 +81,7 @@ BlockCreate.prototype.fromJson = function(json) { BlockCreate.superClass_.fromJson.call(this, json); this.xml = Xml.textToDom(json['xml']); this.ids = json['ids']; + this.json = /** @type {!blocks.State} */ (json['json']); if (json['recordUndo'] !== undefined) { this.recordUndo = json['recordUndo']; } @@ -89,9 +94,7 @@ BlockCreate.prototype.fromJson = function(json) { BlockCreate.prototype.run = function(forward) { const workspace = this.getEventWorkspace_(); if (forward) { - const xmlEl = xml.createElement('xml'); - xmlEl.appendChild(this.xml); - Xml.domToWorkspace(xmlEl, workspace); + blocks.load(this.json, workspace); } else { for (let i = 0; i < this.ids.length; i++) { const id = this.ids[i]; diff --git a/core/events/events_block_delete.js b/core/events/events_block_delete.js index f44caa7f019..7ab770a0bb6 100644 --- a/core/events/events_block_delete.js +++ b/core/events/events_block_delete.js @@ -18,9 +18,9 @@ const Block = goog.requireType('Blockly.Block'); const BlockBase = goog.require('Blockly.Events.BlockBase'); const Events = goog.require('Blockly.Events'); const Xml = goog.require('Blockly.Xml'); +const blocks = goog.require('Blockly.serialization.blocks'); const object = goog.require('Blockly.utils.object'); const registry = goog.require('Blockly.registry'); -const xml = goog.require('Blockly.utils.xml'); /** @@ -43,12 +43,21 @@ const BlockDelete = function(opt_block) { this.recordUndo = false; } - if (opt_block.workspace.rendered) { - this.oldXml = Xml.blockToDomWithXY(opt_block); - } else { - this.oldXml = Xml.blockToDom(opt_block); - } + this.oldXml = Xml.blockToDomWithXY(opt_block); this.ids = Events.getDescendantIds(opt_block); + + /** + * Was the block that was just deleted a shadow? + * @type {boolean} + */ + this.wasShadow = opt_block.isShadow(); + + /** + * JSON representation of the block that was just deleted. + * @type {!blocks.State} + */ + this.oldJson = /** @type {!blocks.State} */ (blocks.save( + opt_block, {addCoordinates: true})); }; object.inherits(BlockDelete, BlockBase); @@ -66,6 +75,8 @@ BlockDelete.prototype.toJson = function() { const json = BlockDelete.superClass_.toJson.call(this); json['oldXml'] = Xml.domToText(this.oldXml); json['ids'] = this.ids; + json['wasShadow'] = this.wasShadow; + json['oldJson'] = this.oldJson; if (!this.recordUndo) { json['recordUndo'] = this.recordUndo; } @@ -80,6 +91,9 @@ BlockDelete.prototype.fromJson = function(json) { BlockDelete.superClass_.fromJson.call(this, json); this.oldXml = Xml.textToDom(json['oldXml']); this.ids = json['ids']; + this.wasShadow = + json['wasShadow'] || this.oldXml.tagName.toLowerCase() == 'shadow'; + this.oldJson = /** @type {!blocks.State} */ (json['oldJson']); if (json['recordUndo'] !== undefined) { this.recordUndo = json['recordUndo']; } @@ -103,9 +117,7 @@ BlockDelete.prototype.run = function(forward) { } } } else { - const xmlEl = xml.createElement('xml'); - xmlEl.appendChild(this.oldXml); - Xml.domToWorkspace(xmlEl, workspace); + blocks.load(this.oldJson, workspace); } }; diff --git a/core/trashcan.js b/core/trashcan.js index d34cd080382..b68f67e95f2 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -36,7 +36,8 @@ const Size = goog.require('Blockly.utils.Size'); const Svg = goog.require('Blockly.utils.Svg'); /* eslint-disable-next-line no-unused-vars */ const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg'); -const Xml = goog.require('Blockly.Xml'); +/* eslint-disable-next-line no-unused-vars */ +const blocks = goog.requireType('Blockly.serialization.blocks'); const browserEvents = goog.require('Blockly.browserEvents'); const dom = goog.require('Blockly.utils.dom'); const internalConstants = goog.require('Blockly.internalConstants'); @@ -73,7 +74,7 @@ const Trashcan = function(workspace) { this.id = 'trashcan'; /** - * A list of XML (stored as strings) representing blocks in the trashcan. + * A list of JSON (stored as strings) representing blocks in the trashcan. * @type {!Array} * @private */ @@ -393,8 +394,10 @@ Trashcan.prototype.openFlyout = function() { if (this.contentsIsOpen()) { return; } - const xml = this.contents_.map(Xml.textToDom); - this.flyout.show(xml); + const contents = this.contents_.map(function(string) { + return JSON.parse(string); + }); + this.flyout.show(contents); this.fireUiEvent_(true); }; @@ -669,14 +672,12 @@ Trashcan.prototype.onDelete_ = function(event) { if (this.workspace_.options.maxTrashcanContents <= 0) { return; } - // Must check that the tagName exists since oldXml can be a DocumentFragment. - if (event.type == Events.BLOCK_DELETE && event.oldXml.tagName && - event.oldXml.tagName.toLowerCase() != 'shadow') { - const cleanedXML = this.cleanBlockXML_(event.oldXml); - if (this.contents_.indexOf(cleanedXML) != -1) { + if (event.type == Events.BLOCK_DELETE && !event.wasShadow) { + const cleanedJson = this.cleanBlockJson_(event.oldJson); + if (this.contents_.indexOf(cleanedJson) != -1) { return; } - this.contents_.unshift(cleanedXML); + this.contents_.unshift(cleanedJson); while (this.contents_.length > this.workspace_.options.maxTrashcanContents) { this.contents_.pop(); @@ -687,52 +688,51 @@ Trashcan.prototype.onDelete_ = function(event) { }; /** - * Converts XML representing a block into text that can be stored in the - * content array. - * @param {!Element} xml An XML tree defining the block and any - * connected child blocks. - * @return {string} Text representing the XML tree, cleaned of all unnecessary - * attributes. + * Converts JSON representing a block into text that can be stored in the + * content array. + * @param {!blocks.State} json A JSON representation of + * a block's state. + * @return {string} Text representing the JSON, cleaned of all unnecessary + * attributes. * @private */ -Trashcan.prototype.cleanBlockXML_ = function(xml) { - const xmlBlock = xml.cloneNode(true); - let node = xmlBlock; - while (node) { - // Things like text inside tags are still treated as nodes, but they - // don't have attributes (or the removeAttribute function) so we can - // skip removing attributes from them. - if (node.removeAttribute) { - node.removeAttribute('x'); - node.removeAttribute('y'); - node.removeAttribute('id'); - node.removeAttribute('disabled'); - if (node.nodeName == 'comment') { // Future proof just in case. - node.removeAttribute('h'); - node.removeAttribute('w'); - node.removeAttribute('pinned'); - } +Trashcan.prototype.cleanBlockJson_ = function(json) { + // Create a deep copy. + json = /** @type {!blocks.State} */(JSON.parse(JSON.stringify(json))); + + function cleanRec(json) { + if (!json) { + return; } - // Try to go down the tree - let nextNode = node.firstChild || node.nextSibling; - // If we can't go down, try to go back up the tree. - if (!nextNode) { - nextNode = node.parentNode; - while (nextNode) { - // We are valid again! - if (nextNode.nextSibling) { - nextNode = nextNode.nextSibling; - break; - } - // Try going up again. If parentNode is null that means we have - // reached the top, and we will break out of both loops. - nextNode = nextNode.parentNode; - } + delete json['id']; + delete json['x']; + delete json['y']; + delete json['enabled']; + + if (json['icons'] && json['icons']['comment']) { + const comment = json['icons']['comment']; + delete comment['height']; + delete comment['width']; + delete comment['pinned']; + } + + const inputs = json['inputs']; + for (var name in inputs) { + const input = inputs[name]; + cleanRec(input['block']); + cleanRec(input['shadow']); + } + if (json['next']) { + const next = json['next']; + cleanRec(next['block']); + cleanRec(next['shadow']); } - node = nextNode; } - return Xml.domToText(xmlBlock); + + cleanRec(json); + json['kind'] = 'BLOCK'; + return JSON.stringify(json); }; exports = Trashcan; diff --git a/tests/deps.js b/tests/deps.js index 78270370086..711a495914b 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -40,8 +40,8 @@ goog.addDependency('../../core/events/events.js', ['Blockly.Events'], ['Blockly. goog.addDependency('../../core/events/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_base.js', ['Blockly.Events.BlockBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_change.js', ['Blockly.Events.BlockChange'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/events/events_block_create.js', ['Blockly.Events.BlockCreate'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/events/events_block_delete.js', ['Blockly.Events.BlockDelete'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_create.js', ['Blockly.Events.BlockCreate'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_delete.js', ['Blockly.Events.BlockDelete'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_drag.js', ['Blockly.Events.BlockDrag'], ['Blockly.Events', 'Blockly.Events.UiBase', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_move.js', ['Blockly.Events.BlockMove'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.connectionTypes', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_bubble_open.js', ['Blockly.Events.BubbleOpen'], ['Blockly.Events', 'Blockly.Events.UiBase', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); @@ -217,7 +217,7 @@ goog.addDependency('../../core/toolbox/toolbox_item.js', ['Blockly.ToolboxItem'] goog.addDependency('../../core/tooltip.js', ['Blockly.Tooltip'], ['Blockly.browserEvents', 'Blockly.common', 'Blockly.utils.deprecation', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/touch.js', ['Blockly.Touch'], ['Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/touch_gesture.js', ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.TrashcanOpen', 'Blockly.Options', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.TrashcanOpen', 'Blockly.Options', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils.js', ['Blockly.utils'], ['Blockly.Msg', 'Blockly.internalConstants', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.idGenerator', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.string', 'Blockly.utils.style', 'Blockly.utils.svgPaths', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils/aria.js', ['Blockly.utils.aria'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils/colour.js', ['Blockly.utils.colour'], ['Blockly.internalConstants'], {'lang': 'es6', 'module': 'goog'}); diff --git a/tests/deps.mocha.js b/tests/deps.mocha.js index 134e871520e..2dc83a1de0d 100644 --- a/tests/deps.mocha.js +++ b/tests/deps.mocha.js @@ -40,8 +40,8 @@ goog.addDependency('../../core/events/events.js', ['Blockly.Events'], ['Blockly. goog.addDependency('../../core/events/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_base.js', ['Blockly.Events.BlockBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_change.js', ['Blockly.Events.BlockChange'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/events/events_block_create.js', ['Blockly.Events.BlockCreate'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/events/events_block_delete.js', ['Blockly.Events.BlockDelete'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_create.js', ['Blockly.Events.BlockCreate'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_delete.js', ['Blockly.Events.BlockDelete'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_drag.js', ['Blockly.Events.BlockDrag'], ['Blockly.Events', 'Blockly.Events.UiBase', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_move.js', ['Blockly.Events.BlockMove'], ['Blockly.Events', 'Blockly.Events.BlockBase', 'Blockly.connectionTypes', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_bubble_open.js', ['Blockly.Events.BubbleOpen'], ['Blockly.Events', 'Blockly.Events.UiBase', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); @@ -217,7 +217,7 @@ goog.addDependency('../../core/toolbox/toolbox_item.js', ['Blockly.ToolboxItem'] goog.addDependency('../../core/tooltip.js', ['Blockly.Tooltip'], ['Blockly.browserEvents', 'Blockly.common', 'Blockly.utils.deprecation', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/touch.js', ['Blockly.Touch'], ['Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/touch_gesture.js', ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.TrashcanOpen', 'Blockly.Options', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.TrashcanOpen', 'Blockly.Options', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils.js', ['Blockly.utils'], ['Blockly.Msg', 'Blockly.internalConstants', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.idGenerator', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.string', 'Blockly.utils.style', 'Blockly.utils.svgPaths', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils/aria.js', ['Blockly.utils.aria'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/utils/colour.js', ['Blockly.utils.colour'], ['Blockly.internalConstants'], {'lang': 'es6', 'module': 'goog'}); diff --git a/tests/mocha/event_test.js b/tests/mocha/event_test.js index 499ab6b47c0..c0f1711e815 100644 --- a/tests/mocha/event_test.js +++ b/tests/mocha/event_test.js @@ -437,48 +437,118 @@ suite('Events', function() { viewLeft: 0, scale: 1.2, oldScale: 1})}, ]; var blockEventTestCases = [ - {title: 'Block change', class: Blockly.Events.BlockChange, + { + title: 'Block change', + class: Blockly.Events.BlockChange, getArgs: (thisObj) => [thisObj.block, 'collapsed', null, false, true], - getExpectedJson: (thisObj) => ({type: 'change', - blockId: thisObj.block.id, element: 'collapsed', oldValue: false, - newValue: true})}, - {title: 'Block create', class: Blockly.Events.BlockCreate, + getExpectedJson: (thisObj) => ({ + type: 'change', + blockId: thisObj.block.id, + element: 'collapsed', + oldValue: false, + newValue: true + }) + }, + { + title: 'Block create', + class: Blockly.Events.BlockCreate, getArgs: (thisObj) => [thisObj.block], - getExpectedJson: (thisObj) => ({type: 'create', + getExpectedJson: (thisObj) => ({ + type: 'create', blockId: thisObj.block.id, xml: '', - ids: [thisObj.block.id]})}, - {title: 'Block create (shadow)', class: Blockly.Events.BlockCreate, + ' type="simple_test_block" id="testBlockId1" x="0" y="0">' + + '', + ids: [thisObj.block.id], + json: { + 'type': 'simple_test_block', + 'id': 'testBlockId1', + 'x': 0, + 'y': 0, + }, + }) + }, + { + title: 'Block create (shadow)', + class: Blockly.Events.BlockCreate, getArgs: (thisObj) => [thisObj.shadowBlock], - getExpectedJson: (thisObj) => ({type: 'create', + getExpectedJson: (thisObj) => ({ + type: 'create', blockId: thisObj.shadowBlock.id, xml: '', - ids: [thisObj.shadowBlock.id], recordUndo: false})}, - {title: 'Block delete', class: Blockly.Events.BlockDelete, + ' type="simple_test_block" id="testBlockId2" x="0" y="0">' + + '', + ids: [thisObj.shadowBlock.id], + json: { + 'type': 'simple_test_block', + 'id': 'testBlockId2', + 'x': 0, + 'y': 0, + }, + recordUndo: false + }) + }, + { + title: 'Block delete', + class: Blockly.Events.BlockDelete, getArgs: (thisObj) => [thisObj.block], - getExpectedJson: (thisObj) => ({type: 'delete', + getExpectedJson: (thisObj) => ({ + type: 'delete', blockId: thisObj.block.id, oldXml: '', - ids: [thisObj.block.id]})}, - {title: 'Block delete (shadow)', class: Blockly.Events.BlockDelete, + ' type="simple_test_block" id="testBlockId1" x="0" y="0">' + + '', + ids: [thisObj.block.id], + wasShadow: false, + oldJson: { + 'type': 'simple_test_block', + 'id': 'testBlockId1', + 'x': 0, + 'y': 0, + }, + }) + }, + { + title: 'Block delete (shadow)', + class: Blockly.Events.BlockDelete, getArgs: (thisObj) => [thisObj.shadowBlock], - getExpectedJson: (thisObj) => ({type: 'delete', + getExpectedJson: (thisObj) => ({ + type: 'delete', blockId: thisObj.shadowBlock.id, oldXml: '', - ids: [thisObj.shadowBlock.id], recordUndo: false})}, + ' type="simple_test_block" id="testBlockId2" x="0" y="0">' + + '', + ids: [thisObj.shadowBlock.id], + wasShadow: true, + oldJson: { + 'type': 'simple_test_block', + 'id': 'testBlockId2', + 'x': 0, + 'y': 0, + }, + recordUndo: false + }) + }, // TODO(#4577) Test serialization of move event coordinate properties. - {title: 'Block move', class: Blockly.Events.BlockMove, + { + title: 'Block move', + class: Blockly.Events.BlockMove, getArgs: (thisObj) => [thisObj.block], - getExpectedJson: (thisObj) => ({type: 'move', - blockId: thisObj.block.id})}, - {title: 'Block move (shadow)', class: Blockly.Events.BlockMove, + getExpectedJson: (thisObj) => ({ + type: 'move', + blockId: thisObj.block.id + }) + }, + { + title: 'Block move (shadow)', + class: Blockly.Events.BlockMove, getArgs: (thisObj) => [thisObj.shadowBlock], - getExpectedJson: (thisObj) => ({type: 'move', - blockId: thisObj.shadowBlock.id, recordUndo: false})}, + getExpectedJson: (thisObj) => ({ + type: 'move', + blockId: thisObj.shadowBlock.id, + recordUndo: false + }) + }, ]; var workspaceEventTestCases = [ {title: 'Finished Loading', class: Blockly.Events.FinishedLoading, diff --git a/tests/mocha/jso_deserialization_test.js b/tests/mocha/jso_deserialization_test.js index 0ec532062db..d554af810f7 100644 --- a/tests/mocha/jso_deserialization_test.js +++ b/tests/mocha/jso_deserialization_test.js @@ -689,7 +689,11 @@ suite('JSO Deserialization', function() { Blockly.Blocks['test_block'] = { init: function() { }, - mutationToDom: function() { }, + mutationToDom: function() { + var container = Blockly.utils.xml.createElement('mutation'); + container.setAttribute('value', 'some value'); + return container; + }, domToMutation: function(element) { this.someProperty = element.getAttribute('value'); diff --git a/tests/mocha/test_helpers.js b/tests/mocha/test_helpers.js index b93ccd368b3..fe726d19f61 100644 --- a/tests/mocha/test_helpers.js +++ b/tests/mocha/test_helpers.js @@ -481,9 +481,9 @@ function assertNthCallEventArgEquals(spy, n, instanceType, expectedProperties, } exports.assertNthCallEventArgEquals = assertNthCallEventArgEquals; -function defineStackBlock() { +function defineStackBlock(name = 'stack_block') { Blockly.defineBlocksWithJsonArray([{ - "type": "stack_block", + "type": name, "message0": "", "previousStatement": null, "nextStatement": null @@ -491,9 +491,9 @@ function defineStackBlock() { } exports.defineStackBlock = defineStackBlock; -function defineRowBlock() { +function defineRowBlock(name = 'row_block') { Blockly.defineBlocksWithJsonArray([{ - "type": "row_block", + "type": name, "message0": "%1", "args0": [ { @@ -506,9 +506,9 @@ function defineRowBlock() { } exports.defineRowBlock = defineRowBlock; -function defineStatementBlock() { +function defineStatementBlock(name = 'statement_block') { Blockly.defineBlocksWithJsonArray([{ - "type": "statement_block", + "type": name, "message0": "%1", "args0": [ { @@ -525,9 +525,9 @@ function defineStatementBlock() { } exports.defineStatementBlock = defineStatementBlock; -function defineBasicBlockWithField() { +function defineBasicBlockWithField(name = 'test_field_block') { Blockly.defineBlocksWithJsonArray([{ - "type": "test_field_block", + "type": name, "message0": "%1", "args0": [ { diff --git a/tests/mocha/trashcan_test.js b/tests/mocha/trashcan_test.js index b8641292b59..d7373235b53 100644 --- a/tests/mocha/trashcan_test.js +++ b/tests/mocha/trashcan_test.js @@ -6,7 +6,7 @@ goog.module('Blockly.test.trashcan'); -const {assertEventFired, assertEventNotFired, sharedTestSetup, sharedTestTeardown, simulateClick} = goog.require('Blockly.test.helpers'); +const {assertEventFired, assertEventNotFired, defineBasicBlockWithField, defineRowBlock, defineStatementBlock, defineStackBlock, defineMutatorBlocks, sharedTestSetup, sharedTestTeardown, simulateClick} = goog.require('Blockly.test.helpers'); suite("Trashcan", function() { @@ -15,14 +15,13 @@ suite("Trashcan", function() { '' + xmlString + ''); xml = xml.children[0]; - var event = new Blockly.Events.BlockDelete(); - event.oldXml = xml; - event.workspaceId = workspace.id; + var block = Blockly.Xml.domToBlock(xml, workspace); + var event = new Blockly.Events.BlockDelete(block); Blockly.Events.fire(event); } function fireNonDeleteEvent(workspace, oldXml) { var event = new Blockly.Events.Abstract(); - event.type = 'dummy_type'; + event.type = 'test_field_block'; event.workspaceId = workspace.id; if (oldXml) { event.oldXml = oldXml; @@ -32,17 +31,27 @@ suite("Trashcan", function() { setup(function() { sharedTestSetup.call(this); + defineBasicBlockWithField(); + defineRowBlock(); + defineRowBlock('row_block2'); + defineStatementBlock(); + defineStatementBlock('statement_block2'); + defineStackBlock(); + defineStackBlock('stack_block2'); + defineMutatorBlocks(); this.workspace = Blockly.inject('blocklyDiv', {'trashcan': true, 'maxTrashcanContents': Infinity}); this.trashcan = this.workspace.trashcan; }); teardown(function() { sharedTestTeardown.call(this); + Blockly.Extensions.unregister('xml_mutator'); + Blockly.Extensions.unregister('jso_mutator'); }); suite("Events", function() { test("Delete", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 1); }); test("Non-Delete", function() { @@ -52,7 +61,7 @@ suite("Trashcan", function() { test("Non-Delete w/ oldXml", function() { var xml = Blockly.Xml.textToDom( '' + - ' ' + + ' ' + '' ); xml = xml.children[0]; @@ -60,7 +69,7 @@ suite("Trashcan", function() { chai.assert.equal(this.trashcan.contents_.length, 0); }); test("Shadow Delete", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 0); }); test("Click without contents - fires workspace click", function() { @@ -73,7 +82,7 @@ suite("Trashcan", function() { this.workspace.id, null); }); test("Click with contents - fires trashcanOpen", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 1); // Stub flyout interaction. var showFlyoutStub = sinon.stub(this.trashcan.flyout, "show"); @@ -107,56 +116,52 @@ suite("Trashcan", function() { }); suite("Unique Contents", function() { test("Simple", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 1); }); test("Different Coords", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 1); }); test("Different IDs", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); chai.assert.equal(this.trashcan.contents_.length, 1); }); test("No Disabled - Disabled True", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); + fireDeleteEvent( + this.workspace, ''); // Disabled tags get removed because disabled blocks aren't allowed to // be dragged from flyouts. See #2239 and #3243. chai.assert.equal(this.trashcan.contents_.length, 1); }); - test("No Editable - Editable False", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); - chai.assert.equal(this.trashcan.contents_.length, 2); - }); - test("No Movable - Movable False", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); - chai.assert.equal(this.trashcan.contents_.length, 2); - }); test("Different Field Values", function() { fireDeleteEvent(this.workspace, - '' + - ' dummy_value1' + + '' + + ' dummy_value1' + '' ); fireDeleteEvent(this.workspace, - '' + - ' dummy_value2' + + '' + + ' dummy_value2' + '' ); chai.assert.equal(this.trashcan.contents_.length, 2); }); test("No Values - Values", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); @@ -164,27 +169,27 @@ suite("Trashcan", function() { }); test("Different Value Blocks", function() { fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); chai.assert.equal(this.trashcan.contents_.length, 2); }); test("No Statements - Statements", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); @@ -192,27 +197,27 @@ suite("Trashcan", function() { }); test("Different Statement Blocks", function() { fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); fireDeleteEvent(this.workspace, - '' + - ' ' + - ' ' + + '' + + ' ' + + ' ' + ' ' + '' ); chai.assert.equal(this.trashcan.contents_.length, 2); }); test("No Next - Next", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); fireDeleteEvent(this.workspace, - '' + + '' + ' ' + - ' ' + + ' ' + ' ' + '' ); @@ -220,25 +225,25 @@ suite("Trashcan", function() { }); test("Different Next Blocks", function() { fireDeleteEvent(this.workspace, - '' + + '' + ' ' + - ' ' + + ' ' + ' ' + '' ); fireDeleteEvent(this.workspace, - '' + + '' + ' ' + - ' ' + + ' ' + ' ' + '' ); chai.assert.equal(this.trashcan.contents_.length, 2); }); test("No Comment - Comment", function() { - fireDeleteEvent(this.workspace, ''); + fireDeleteEvent(this.workspace, ''); fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text' + '' ); @@ -246,12 +251,12 @@ suite("Trashcan", function() { }); test("Different Comment Text", function() { fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text1' + '' ); fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text2' + '' ); @@ -259,12 +264,12 @@ suite("Trashcan", function() { }); test("Different Comment Size", function() { fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text' + '' ); fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text' + '' ); @@ -273,36 +278,27 @@ suite("Trashcan", function() { }); test("Different Comment Pinned", function() { fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text' + '' ); fireDeleteEvent(this.workspace, - '' + + '' + ' comment_text' + '' ); // pinned tags are removed b/c the blocks appear the same. chai.assert.equal(this.trashcan.contents_.length, 1); }); - test("No Mutator - Mutator", function() { - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, - '' + - ' ' + - '' - ); - chai.assert.equal(this.trashcan.contents_.length, 2); - }); test("Different Mutator", function() { fireDeleteEvent(this.workspace, - '' + - ' ' + + '' + + ' ' + '' ); fireDeleteEvent(this.workspace, - '' + - ' ' + + '' + + ' ' + '' ); chai.assert.equal(this.trashcan.contents_.length, 2); @@ -312,22 +308,10 @@ suite("Trashcan", function() { test("Max 0", function() { this.workspace.options.maxTrashcanContents = 0; fireDeleteEvent(this.workspace, - '' + '' ); chai.assert.equal(this.trashcan.contents_.length, 0); this.workspace.options.maxTrashcanContents = Infinity; }); - test("Last In First Out", function() { - this.workspace.options.maxTrashcanContents = 1; - fireDeleteEvent(this.workspace, ''); - fireDeleteEvent(this.workspace, ''); - chai.assert.equal(this.trashcan.contents_.length, 1); - chai.assert.equal( - Blockly.Xml.textToDom(this.trashcan.contents_[0]) - .getAttribute('type'), - 'dummy_type2' - ); - this.workspace.options.maxTrashcanContents = Infinity; - }); }); });