From 1d4cbd1ab6df6380de6b51eb3fe9852c6c8ef23a Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 19 Aug 2021 13:50:48 +0000 Subject: [PATCH] Add serializing shadows as JSOs for the JSO system (#5246) * Move existing tests into new suite * Add tests for setShadowState * Add assertions for serialization * Unskip serialization tests * Add logic to handle shadows in both systems * Uncomment tests * fix: add access modifiers to new comment funcs * fix: fixup types * fix: remove addNextBlocks = true * feat: add real child of shadow errors * fix: types --- core/connection.js | 183 +- core/rendered_connection.js | 1 - core/serialization/blocks.js | 23 +- tests/deps.js | 4 +- tests/deps.mocha.js | 15 +- tests/mocha/connection_test.js | 3100 ++++++++++++++++++----- tests/mocha/jso_deserialization_test.js | 6 +- tests/mocha/jso_serialization_test.js | 20 +- 8 files changed, 2610 insertions(+), 742 deletions(-) diff --git a/core/connection.js b/core/connection.js index f2f8c3fd8..510ed90a2 100644 --- a/core/connection.js +++ b/core/connection.js @@ -23,6 +23,7 @@ const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker'); /* eslint-disable-next-line no-unused-vars */ const Input = goog.requireType('Blockly.Input'); const Xml = goog.require('Blockly.Xml'); +const blocks = goog.require('Blockly.serialization.blocks'); const connectionTypes = goog.require('Blockly.connectionTypes'); const deprecation = goog.require('Blockly.utils.deprecation'); /** @suppress {extraRequire} */ @@ -121,16 +122,15 @@ Connection.prototype.connect_ = function(childConnection) { // Make sure the parentConnection is available. let orphan; if (parentConnection.isConnected()) { - const shadowDom = parentConnection.getShadowDom(true); - parentConnection.shadowDom_ = null; // Set to null so it doesn't respawn. - const target = parentConnection.targetBlock(); + let shadowState = parentConnection.stashShadowState_(); + var target = parentConnection.targetBlock(); if (target.isShadow()) { target.dispose(false); } else { parentConnection.disconnect(); orphan = target; } - parentConnection.shadowDom_ = shadowDom; + parentConnection.applyShadowState_(shadowState); } // Connect the new connection to the parent. @@ -169,7 +169,7 @@ Connection.prototype.dispose = function() { // isConnected returns true for shadows and non-shadows. if (this.isConnected()) { // Destroy the attached shadow block & its children (if it exists). - this.setShadowDom(null); + this.setShadowStateInternal_(); const targetBlock = this.targetBlock(); if (targetBlock) { @@ -464,18 +464,8 @@ Connection.prototype.disconnectInternal_ = function(parentBlock, childBlock) { * @protected */ Connection.prototype.respawnShadow_ = function() { - const parentBlock = this.getSourceBlock(); - const shadow = this.getShadowDom(); - if (parentBlock.workspace && shadow) { - const blockShadow = Xml.domToBlock(shadow, parentBlock.workspace); - if (blockShadow.outputConnection) { - this.connect(blockShadow.outputConnection); - } else if (blockShadow.previousConnection) { - this.connect(blockShadow.previousConnection); - } else { - throw Error('Child block does not have output or previous statement.'); - } - } + // Have to keep respawnShadow_ for backwards compatibility. + this.createShadowBlock_(true); }; /** @@ -569,18 +559,10 @@ Connection.prototype.getCheck = function() { /** * Changes the connection's shadow block. - * @param {?Element} shadow DOM representation of a block or null. + * @param {?Element} shadowDom DOM representation of a block or null. */ -Connection.prototype.setShadowDom = function(shadow) { - this.shadowDom_ = shadow; - const target = this.targetBlock(); - if (!target) { - this.respawnShadow_(); - } else if (target.isShadow()) { - // The disconnect from dispose will automatically generate the new shadow. - target.dispose(false); - this.respawnShadow_(); - } +Connection.prototype.setShadowDom = function(shadowDom) { + this.setShadowStateInternal_({shadowDom: shadowDom}); }; /** @@ -598,6 +580,32 @@ Connection.prototype.getShadowDom = function(returnCurrent) { this.shadowDom_; }; +/** + * Changes the connection's shadow block. + * @param {?blocks.State} shadowState An state represetation of the block or + * null. + */ +Connection.prototype.setShadowState = function(shadowState) { + this.setShadowStateInternal_({shadowState: shadowState}); +}; + +/** + * Returns the serialized object representation of the connection's shadow + * block. + * @param {boolean=} returnCurrent If true, and the shadow block is currently + * attached to this connection, this serializes the state of that block + * and returns it (so that field values are correct). Otherwise the saved + * state is just returned. + * @return {?blocks.State} Serialized object representation of the block, or + * null. + */ +Connection.prototype.getShadowState = function(returnCurrent) { + if (returnCurrent && this.targetBlock() && this.targetBlock().isShadow()) { + return blocks.save(/** @type {!Block} */ (this.targetBlock())); + } + return this.shadowState_; +}; + /** * Find all nearby compatible connections to this connection. * Type checking does not apply, since this function is used for bumping. @@ -667,4 +675,123 @@ Connection.prototype.toString = function() { return msg + block.toDevString(); }; +/** + * Returns the state of the shadowDom_ and shadowState_ properties, then + * temporarily sets those properties to null so no shadow respawns. + * @return {{shadowDom: ?Element, shadowState: ?blocks.State}} The state of both + * the shadowDom_ and shadowState_ properties. + * @private + */ +Connection.prototype.stashShadowState_ = function() { + const shadowDom = this.getShadowDom(true); + const shadowState = this.getShadowState(true); + // Set to null so it doesn't respawn. + this.shadowDom_ = null; + this.shadowState_ = null; + return {shadowDom, shadowState}; +}; + +/** + * Reapplies the stashed state of the shadowDom_ and shadowState_ properties. + * @param {{shadowDom: ?Element, shadowState: ?blocks.State}} param0 The state + * to reapply to the shadowDom_ and shadowState_ properties. + * @private + */ +Connection.prototype.applyShadowState_ = + function({shadowDom, shadowState}) { + this.shadowDom_ = shadowDom; + this.shadowState_ = shadowState; + }; + +/** + * Sets the state of the shadow of this connection. + * @param {{shadowDom: (?Element|undefined), shadowState: + * (?blocks.State|undefined)}=} param0 The state to set the shadow of this + * connection to. + * @private + */ +Connection.prototype.setShadowStateInternal_ = + function({shadowDom = null, shadowState = null} = {}) { + // One or both of these should always be null. + // If neither is null, the shadowState will get priority. + this.shadowDom_ = shadowDom; + this.shadowState_ = shadowState; + + var target = this.targetBlock(); + if (!target) { + this.respawnShadow_(); + if (this.targetBlock() && this.targetBlock().isShadow()) { + this.serializeShadow_(this.targetBlock()); + } + } else if (target.isShadow()) { + target.dispose(false); + this.respawnShadow_(); + if (this.targetBlock() && this.targetBlock().isShadow()) { + this.serializeShadow_(this.targetBlock()); + } + } else { + var shadow = this.createShadowBlock_(false); + this.serializeShadow_(shadow); + if (shadow) { + shadow.dispose(false); + } + } + }; + +/** + * Creates a shadow block based on the current shadowState_ or shadowDom_. + * shadowState_ gets priority. + * @param {boolean} attemptToConnect Whether to try to connect the shadow block + * to this connection or not. + * @return {?Block} The shadow block that was created, or null if both the + * shadowState_ and shadowDom_ are null. + * @private + */ +Connection.prototype.createShadowBlock_ = function(attemptToConnect) { + var parentBlock = this.getSourceBlock(); + var shadowState = this.getShadowState(); + var shadowDom = this.getShadowDom(); + if (!parentBlock.workspace || (!shadowState && !shadowDom)) { + return null; + } + + if (shadowState) { + var blockShadow = blocks.loadInternal( + shadowState, + parentBlock.workspace, + attemptToConnect ? this : undefined, + true); + return blockShadow; + } + + if (shadowDom) { + blockShadow = Xml.domToBlock(shadowDom, parentBlock.workspace); + if (attemptToConnect) { + if (blockShadow.outputConnection) { + this.connect(blockShadow.outputConnection); + } else if (blockShadow.previousConnection) { + this.connect(blockShadow.previousConnection); + } else { + throw Error('Shadow block does not have output or previous statement.'); + } + } + return blockShadow; + } + return null; +}; + +/** + * Saves the given shadow block to both the shadowDom_ and shadowState_ + * properties, in their respective serialized forms. + * @param {?Block} shadow The shadow to serialize, or null. + * @private + */ +Connection.prototype.serializeShadow_ = function(shadow) { + if (!shadow) { + return; + } + this.shadowDom_ = /** @type {!Element} */ (Xml.blockToDom(shadow)); + this.shadowState_ = blocks.save(shadow); +}; + exports = Connection; diff --git a/core/rendered_connection.js b/core/rendered_connection.js index 2e172e6b9..a4958c0f9 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -506,7 +506,6 @@ RenderedConnection.prototype.respawnShadow_ = function() { RenderedConnection.superClass_.respawnShadow_.call(this); const blockShadow = this.targetBlock(); if (!blockShadow) { - // This connection must not have a shadowDom_. return; } blockShadow.initSvg(); diff --git a/core/serialization/blocks.js b/core/serialization/blocks.js index 60a54021a..531b7cb77 100644 --- a/core/serialization/blocks.js +++ b/core/serialization/blocks.js @@ -13,17 +13,15 @@ goog.module('Blockly.serialization.blocks'); goog.module.declareLegacyNamespace(); +const {BadConnectionCheck, MissingBlockType, MissingConnection, RealChildOfShadow} = goog.require('Blockly.serialization.exceptions'); // eslint-disable-next-line no-unused-vars const Block = goog.requireType('Blockly.Block'); // eslint-disable-next-line no-unused-vars const Connection = goog.requireType('Blockly.Connection'); const Events = goog.require('Blockly.Events'); -const {MissingBlockType, MissingConnection, BadConnectionCheck} = - goog.require('Blockly.serialization.exceptions'); const Size = goog.require('Blockly.utils.Size'); // eslint-disable-next-line no-unused-vars const Workspace = goog.requireType('Blockly.Workspace'); -const Xml = goog.require('Blockly.Xml'); const inputTypes = goog.require('Blockly.inputTypes'); @@ -266,15 +264,14 @@ const saveNextBlocks = function(block, state) { * shadow block, or any connected real block. */ const saveConnection = function(connection) { - const shadow = connection.getShadowDom(); + const shadow = connection.getShadowState(); const child = connection.targetBlock(); if (!shadow && !child) { return null; } var state = Object.create(null); if (shadow) { - state['shadow'] = Xml.domToText(shadow) - .replace('xmlns="https://developers.google.com/blockly/xml"', ''); + state['shadow'] = shadow; } if (child && !child.isShadow()) { state['block'] = save(child); @@ -332,14 +329,18 @@ exports.load = load; * @param {!Workspace} workspace The workspace to add the block to. * @param {!Connection=} parentConnection The optional parent connection to * attach the block to. + * @param {boolean} isShadow Whether the block we are loading is a shadow block + * or not. * @return {!Block} The block that was just loaded. */ -const loadInternal = function(state, workspace, parentConnection = undefined) { +const loadInternal = function( + state, workspace, parentConnection = undefined, isShadow = false) { if (!state['type']) { throw new MissingBlockType(state); } const block = workspace.newBlock(state['type'], state['id']); + block.setShadow(isShadow); loadCoords(block, state); loadAttributes(block, state); loadExtraState(block, state); @@ -351,6 +352,8 @@ const loadInternal = function(state, workspace, parentConnection = undefined) { initBlock(block, workspace.rendered); return block; }; +/** @package */ +exports.loadInternal = loadInternal; /** * Applies any coordinate information available on the state object to the @@ -417,6 +420,10 @@ const tryToConnectParent = function(parentConnection, child, state) { if (!parentConnection) { return; } + + if (parentConnection.getSourceBlock().isShadow() && !child.isShadow()) { + throw new RealChildOfShadow(state); + } let connected = false; let childConnection; @@ -542,7 +549,7 @@ const loadNextBlocks = function(block, state) { */ const loadConnection = function(connection, connectionState) { if (connectionState['shadow']) { - connection.setShadowDom(Blockly.Xml.textToDom(connectionState['shadow'])); + connection.setShadowState(connectionState['shadow']); } if (connectionState['block']) { loadInternal( diff --git a/tests/deps.js b/tests/deps.js index 13a8463ad..32bf81b39 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -23,7 +23,7 @@ goog.addDependency('../../core/clipboard.js', ['Blockly.clipboard'], ['Blockly.E goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.browserEvents', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/common.js', ['Blockly.common'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/component_manager.js', ['Blockly.ComponentManager'], [], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.serialization.blocks', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], ['Blockly.Connection', 'Blockly.common', 'Blockly.connectionTypes', 'Blockly.internalConstants', 'Blockly.registry'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.connectionTypes', 'Blockly.constants'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_types.js', ['Blockly.connectionTypes'], [], {'lang': 'es6', 'module': 'goog'}); @@ -196,7 +196,7 @@ goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Ren goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.serialization.workspaces', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']); goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar'], ['Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/scrollbar_pair.js', ['Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Scrollbar', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.inputTypes', 'Blockly.serialization.exceptions', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.inputTypes', 'Blockly.serialization.exceptions', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/exceptions.js', ['Blockly.serialization.exceptions'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events', 'Blockly.Workspace', 'Blockly.serialization.blocks', 'Blockly.serialization.variables', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); diff --git a/tests/deps.mocha.js b/tests/deps.mocha.js index f9300ac27..85364266c 100644 --- a/tests/deps.mocha.js +++ b/tests/deps.mocha.js @@ -1,10 +1,10 @@ goog.addDependency('../../blocks/colour.js', ['Blockly.Blocks.colour', 'Blockly.Constants.Colour'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldColour', 'Blockly.FieldLabel']); -goog.addDependency('../../blocks/lists.js', ['Blockly.Constants.Lists'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.Mutator']); +goog.addDependency('../../blocks/lists.js', ['Blockly.Constants.Lists'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.Mutator'], {'lang': 'es5'}); goog.addDependency('../../blocks/logic.js', ['Blockly.Blocks.logic', 'Blockly.Constants.Logic'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.Mutator']); goog.addDependency('../../blocks/loops.js', ['Blockly.Blocks.loops', 'Blockly.Constants.Loops'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.Warning']); -goog.addDependency('../../blocks/math.js', ['Blockly.Blocks.math', 'Blockly.Constants.Math'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.FieldNumber', 'Blockly.FieldVariable']); -goog.addDependency('../../blocks/procedures.js', ['Blockly.Blocks.procedures'], ['Blockly', 'Blockly.Blocks', 'Blockly.Comment', 'Blockly.FieldCheckbox', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Mutator', 'Blockly.Warning'], {'lang': 'es5'}); -goog.addDependency('../../blocks/text.js', ['Blockly.Blocks.texts', 'Blockly.Constants.Text'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldMultilineInput', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.Mutator']); +goog.addDependency('../../blocks/math.js', ['Blockly.Blocks.math', 'Blockly.Constants.Math'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.FieldNumber', 'Blockly.FieldVariable'], {'lang': 'es5'}); +goog.addDependency('../../blocks/procedures.js', ['Blockly.Blocks.procedures'], ['Blockly', 'Blockly.Blocks', 'Blockly.Comment', 'Blockly.FieldCheckbox', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Mutator', 'Blockly.Warning'], {'lang': 'es6'}); +goog.addDependency('../../blocks/text.js', ['Blockly.Blocks.texts', 'Blockly.Constants.Text'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldMultilineInput', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.Mutator'], {'lang': 'es5'}); goog.addDependency('../../blocks/variables.js', ['Blockly.Blocks.variables', 'Blockly.Constants.Variables'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldLabel', 'Blockly.FieldVariable']); goog.addDependency('../../blocks/variables_dynamic.js', ['Blockly.Constants.VariablesDynamic'], ['Blockly', 'Blockly.Blocks', 'Blockly.FieldLabel', 'Blockly.FieldVariable']); goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.ASTNode', 'Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.Input', 'Blockly.Tooltip', 'Blockly.common', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.fieldRegistry', 'Blockly.inputTypes', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.idGenerator', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); @@ -23,7 +23,7 @@ goog.addDependency('../../core/clipboard.js', ['Blockly.clipboard'], ['Blockly.E goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.browserEvents', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/common.js', ['Blockly.common'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/component_manager.js', ['Blockly.ComponentManager'], [], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.serialization.blocks', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], ['Blockly.Connection', 'Blockly.common', 'Blockly.connectionTypes', 'Blockly.internalConstants', 'Blockly.registry'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.connectionTypes', 'Blockly.constants'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/connection_types.js', ['Blockly.connectionTypes'], [], {'lang': 'es6', 'module': 'goog'}); @@ -196,7 +196,8 @@ goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Ren goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.serialization.workspaces', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']); goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar'], ['Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/scrollbar_pair.js', ['Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Scrollbar', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.inputTypes', 'Blockly.serialization.exceptions', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/exceptions.js', ['Blockly.serialization.exceptions'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events', 'Blockly.Workspace', 'Blockly.serialization.blocks', 'Blockly.serialization.variables', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.clipboard', 'Blockly.common', 'Blockly.utils.KeyCodes'], {'lang': 'es6', 'module': 'goog'}); @@ -332,7 +333,7 @@ goog.addDependency('../../tests/mocha/gesture_test.js', ['Blockly.test.gesture'] goog.addDependency('../../tests/mocha/input_test.js', ['Blockly.test.input'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../tests/mocha/insertion_marker_test.js', ['Blockly.test.insertionMarker'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../tests/mocha/jso_deserialization_test.js', ['Blockly.test.jsoDeserialization'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../tests/mocha/jso_serialization_test.js', ['Blockly.test.jsoSerialization'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../tests/mocha/jso_serialization_test.js', ['Blockly.test.jsoSerialization'], ['Blockly.test.helpers'], {'lang': 'es8', 'module': 'goog'}); goog.addDependency('../../tests/mocha/json_test.js', ['Blockly.test.json'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../tests/mocha/keydown_test.js', ['Blockly.test.keydown'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../tests/mocha/logic_ternary_test.js', ['Blockly.test.logicTernary'], ['Blockly.test.helpers'], {'lang': 'es6', 'module': 'goog'}); diff --git a/tests/mocha/connection_test.js b/tests/mocha/connection_test.js index eaaa07876..a9788a29f 100644 --- a/tests/mocha/connection_test.js +++ b/tests/mocha/connection_test.js @@ -6,7 +6,7 @@ goog.module('Blockly.test.connection'); -const {assertSingleDeprecationWarningCall, createDeprecationWarningStub, sharedTestSetup, sharedTestTeardown, workspaceTeardown} = goog.require('Blockly.test.helpers'); +const {assertSingleDeprecationWarningCall, createDeprecationWarningStub, createGenUidStubWithReturns, defineRowBlock, defineStatementBlock, defineStackBlock, sharedTestSetup, sharedTestTeardown, workspaceTeardown} = goog.require('Blockly.test.helpers'); suite('Connection', function() { @@ -30,8 +30,8 @@ suite('Connection', function() { test('Deprecated - canConnectWithReason passes', function() { var deprecateWarnSpy = createDeprecationWarningStub(); - var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT); - var conn2 = this.createConnection(Blockly.NEXT_STATEMENT); + var conn1 = this.createConnection(Blockly.PREVIOUS_NAME); + var conn2 = this.createConnection(Blockly.NEXT_NAME); chai.assert.equal(conn1.canConnectWithReason(conn2), Blockly.Connection.CAN_CONNECT); assertSingleDeprecationWarningCall(deprecateWarnSpy, @@ -40,7 +40,7 @@ suite('Connection', function() { test('Deprecated - canConnectWithReason fails', function() { var deprecateWarnSpy = createDeprecationWarningStub(); - var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT); + var conn1 = this.createConnection(Blockly.PREVIOUS_NAME); var conn2 = this.createConnection(Blockly.OUTPUT_VALUE); chai.assert.equal(conn1.canConnectWithReason(conn2), Blockly.Connection.REASON_WRONG_TYPE); @@ -50,8 +50,8 @@ suite('Connection', function() { test('Deprecated - checkConnection passes', function() { var deprecateWarnSpy = createDeprecationWarningStub(); - var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT); - var conn2 = this.createConnection(Blockly.NEXT_STATEMENT); + var conn1 = this.createConnection(Blockly.PREVIOUS_NAME); + var conn2 = this.createConnection(Blockly.NEXT_NAME); chai.assert.doesNotThrow(function() { conn1.checkConnection(conn2); }); @@ -61,7 +61,7 @@ suite('Connection', function() { test('Deprecated - checkConnection fails', function() { var deprecateWarnSpy = createDeprecationWarningStub(); - var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT); + var conn1 = this.createConnection(Blockly.PREVIOUS_NAME); var conn2 = this.createConnection(Blockly.OUTPUT_VALUE); chai.assert.throws(function() { conn1.checkConnection(conn2); @@ -70,7 +70,7 @@ suite('Connection', function() { 'Connection.prototype.checkConnection'); }); - suite('Set Shadow Dom', function() { + suite('Set Shadow', function() { function assertBlockMatches(block, isShadow, opt_id) { chai.assert.equal(block.isShadow(), isShadow, @@ -105,6 +105,14 @@ suite('Connection', function() { chai.assert.notExists(block, `expected block ${block && block.id} to not be attached to next connection`); } + + function assertSerialization(block, jso, xmlText) { + const actualJso = Blockly.serialization.blocks + .save(block, {addNextBlocks: true}); + const actualXml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); + chai.assert.deepEqual(actualJso, jso); + chai.assert.equal(actualXml, xmlText); + } var testSuites = [ { @@ -127,660 +135,2386 @@ suite('Connection', function() { setup(function() { this.workspace = testSuite.createWorkspace(); - Blockly.defineBlocksWithJsonArray([ - { - "type": "stack_block", - "message0": "", - "previousStatement": null, - "nextStatement": null - }, - { - "type": "row_block", - "message0": "%1", - "args0": [ - { - "type": "input_value", - "name": "INPUT" - } - ], - "output": null - }, - { - "type": "statement_block", - "message0": "%1", - "args0": [ - { - "type": "input_statement", - "name": "STATEMENT" - } - ], - "previousStatement": null, - "nextStatement": null - }]); + defineRowBlock(); + defineStatementBlock(); + defineStackBlock(); + + createGenUidStubWithReturns( + new Array(30).fill().map((_, i) => 'id' + i)); }); teardown(function() { workspaceTeardown.call(this, this.workspace); }); - suite('Add - No Block Connected', function() { - // These are defined separately in each suite. - function createRowBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStatementBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStackBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - test('Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true); + suite('setShadowDom', function() { + suite('Add - No Block Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStatementBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStackBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - - test('Multiple Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true); + + suite('Add - With Block Connected', function() { + // These are defined separately in each suite. + function createRowBlocks(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStatementBlocks(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStackBlocks(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Value', function() { + var parent = createRowBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', false); + assertInputNotHasBlock(parent.getInputTargetBlock('INPUT'), 'INPUT'); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', false); + assertInputNotHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME'); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlocks(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, false); + assertNextNotHasBlock(parent.getNextBlock()); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - - test('Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true); + + suite('Add - With Shadow Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStatementBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStackBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true, '1'); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true, '2'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': '2', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true, '1'); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'a'); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true, '2'); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'b'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': '2', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'b', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true, '1'); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true, '2'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': '2', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true, '1'); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true, 'a'); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true, '2'); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true, 'b'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': '2', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'b', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true, '1'); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true, '2'); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': '2', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true, '1'); + assertNextHasBlock(parent.getNextBlock(), true, 'a'); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true, '2'); + assertNextHasBlock(parent.getNextBlock(), true, 'b'); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': '2', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'b', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - - test('Multiple Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true); + + suite('Remove - No Block Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStatementBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStackBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'INPUT'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'STATEMENT'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowDom(null); + assertNextNotHasBlock(parent); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + }, + '' + + '' + ); + }); }); - - test('Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true); + + suite('Remove - Block Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStatementBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + function createStackBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + ' ' + + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.setShadowDom(null); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputNotHasBlock(parent, 'INPUT'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection.setShadowDom(null); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputNotHasBlock(parent, 'NAME'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowDom(null); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextNotHasBlock(parent); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + }, + '' + + '' + ); + }); }); - - test('Multiple Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true); - assertNextHasBlock(parent.getNextBlock(), true); + + suite('Add - Connect & Disconnect - Remove', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStatementBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + function createStackBlock(workspace) { + var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), workspace); + return block; + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true); + var child = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.connect(child.outputConnection); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + parent.getInput('INPUT').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'INPUT'); + }); + + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('INPUT').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + var child = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.connect(child.outputConnection); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + parent.getInput('INPUT').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'INPUT'); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true); + var child = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .connect(child.previousConnection); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + parent.getInput('NAME').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'NAME'); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.getInput('NAME').connection.setShadowDom(xml); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + var child = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .connect(child.previousConnection); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + parent.getInput('NAME').connection.setShadowDom(null); + assertInputNotHasBlock(parent, 'NAME'); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true); + var child = createStatementBlock(this.workspace); + parent.nextConnection.connect(child.previousConnection); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + parent.nextConnection.setShadowDom(null); + assertNextNotHasBlock(parent); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + var xml = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ' ' + + '' + ); + parent.nextConnection.setShadowDom(xml); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + var child = createStatementBlock(this.workspace); + parent.nextConnection.connect(child.previousConnection); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + parent.nextConnection.setShadowDom(null); + assertNextNotHasBlock(parent); + }); }); }); - suite('Add - With Block Connected', function() { - // These are defined separately in each suite. - function createRowBlocks(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } + suite('setShadowState', function() { + suite('Add - No Block Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'row_block', 'id': 'id0'}, workspace); + } + + function createStatementBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'statement_block', 'id': 'id0'}, workspace); + } + + function createStackBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'stack_block', 'id': 'id0'}, workspace); + } - function createStatementBlocks(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } + test('Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection + .setShadowState({'type': 'row_block', 'id': 'id1'}); + assertInputHasBlock(parent, 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); - function createStackBlocks(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.setShadowState({ + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2' + } + } + } + }); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); - test('Value', function() { - var parent = createRowBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', false); - parent.getInput('INPUT').connection.disconnect(); - assertInputHasBlock(parent, 'INPUT', true); + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .setShadowState({'type': 'statement_block', 'id': 'id1'}); + assertInputHasBlock(parent, 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statment', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection.setShadowState({ + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + }); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection + .setShadowState({'type': 'stack_block', 'id': 'id1'}); + assertNextHasBlock(parent, true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowState({ + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + }); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - test('Multiple Value', function() { - var parent = createRowBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', false); - assertInputNotHasBlock(parent.getInputTargetBlock('INPUT'), 'INPUT'); - parent.getInput('INPUT').connection.disconnect(); - assertInputHasBlock(parent, 'INPUT', true); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true); + suite('Add - With Block Connected', function() { + // These are defined separately in each suite. + function createRowBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'block': { + 'type': 'row_block', + 'id': 'idA' + } + } + } + }, + workspace); + } + + function createStatementBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'block': { + 'type': 'statement_block', + 'id': 'idA' + } + } + } + }, + workspace); + } + + function createStackBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'block': { + 'type': 'stack_block', + 'id': 'idA' + } + } + }, + workspace); + } + + test('Value', function() { + var parent = createRowBlocks(this.workspace); + parent.getInput('INPUT').connection + .setShadowState({'type': 'row_block', 'id': 'id1'}); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Value', function() { + var parent = createRowBlocks(this.workspace); + parent.getInput('INPUT').connection.setShadowState( + { + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + } + ); + assertInputHasBlock(parent, 'INPUT', false); + assertInputNotHasBlock(parent.getInputTargetBlock('INPUT'), 'INPUT'); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlocks(this.workspace); + parent.getInput('NAME').connection + .setShadowState({'type': 'statement_block', 'id': 'id1'}); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlocks(this.workspace); + parent.getInput('NAME').connection.setShadowState( + { + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + } + ); + assertInputHasBlock(parent, 'NAME', false); + assertInputNotHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME'); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlocks(this.workspace); + parent.nextConnection + .setShadowState({'type': 'stack_block', 'id': 'id1'}); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlocks(this.workspace); + parent.nextConnection.setShadowState( + { + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + } + ); + assertNextHasBlock(parent, false); + assertNextNotHasBlock(parent.getNextBlock()); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id2', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - - test('Statement', function() { - var parent = createStatementBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', false); - parent.getInput('STATEMENT').connection.disconnect(); - assertInputHasBlock(parent, 'STATEMENT', true); + + suite('Add - With Shadow Connected', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'row_block', 'id': 'id0'}, workspace); + } + + function createStatementBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'statement_block', 'id': 'id0'}, workspace); + } + + function createStackBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'stack_block', 'id': 'id0'}, workspace); + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection + .setShadowState({'type': 'row_block', 'id': '1'}); + assertInputHasBlock(parent, 'INPUT', true, '1'); + parent.getInput('INPUT').connection + .setShadowState({'type': 'row_block', 'id': '2'}); + assertInputHasBlock(parent, 'INPUT', true, '2'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': '2', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.setShadowState( + { + 'type': 'row_block', + 'id': '1', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'a', + } + } + } + } + ); + assertInputHasBlock(parent, 'INPUT', true, '1'); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'a'); + parent.getInput('INPUT').connection.setShadowState( + { + 'type': 'row_block', + 'id': '2', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'b', + } + } + } + } + ); + assertInputHasBlock(parent, 'INPUT', true, '2'); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'b'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': '2', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'b', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .setShadowState({'type': 'statement_block', 'id': '1'}); + assertInputHasBlock(parent, 'NAME', true, '1'); + parent.getInput('NAME').connection + .setShadowState({'type': 'statement_block', 'id': '2'}); + assertInputHasBlock(parent, 'NAME', true, '2'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': '2', + } + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection.setShadowState( + { + 'type': 'statement_block', + 'id': '1', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'a', + } + } + } + } + ); + assertInputHasBlock(parent, 'NAME', true, '1'); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true, 'a'); + parent.getInput('NAME').connection.setShadowState( + { + 'type': 'statement_block', + 'id': '2', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'b', + } + } + } + } + ); + assertInputHasBlock(parent, 'NAME', true, '2'); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true, 'b'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': '2', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'b', + } + } + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection + .setShadowState({'type': 'stack_block', 'id': '1'}); + assertNextHasBlock(parent, true, '1'); + parent.nextConnection + .setShadowState({'type': 'stack_block', 'id': '2'}); + assertNextHasBlock(parent, true, '2'); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': '2', + } + } + }, + '' + + '' + + '' + + '' + + '' + ); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowState( + { + 'type': 'stack_block', + 'id': '1', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'a', + } + } + } + ); + assertNextHasBlock(parent, true, '1'); + assertNextHasBlock(parent.getNextBlock(), true, 'a'); + parent.nextConnection.setShadowState( + { + 'type': 'stack_block', + 'id': '2', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'b', + } + } + } + ); + assertNextHasBlock(parent, true, '2'); + assertNextHasBlock(parent.getNextBlock(), true, 'b'); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': '2', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'b', + } + } + } + } + }, + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }); }); - - test('Multiple Statement', function() { - var parent = createStatementBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', false); - assertInputNotHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT'); - parent.getInput('STATEMENT').connection.disconnect(); - assertInputHasBlock(parent, 'STATEMENT', true); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true); + + suite('Remove - No Block Connected', function() { + // These are defined separately in each suite. + function createRowBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + } + } + } + }, + workspace); + } + + function createStatementBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + } + } + } + }, + workspace); + } + + function createStackBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + } + } + }, + workspace); + } + + test('Value', function() { + var parent = createRowBlocks(this.workspace); + parent.getInput('INPUT').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'INPUT'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlocks(this.workspace); + parent.getInput('NAME').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'NAME'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlocks(this.workspace); + parent.nextConnection.setShadowState(null); + assertNextNotHasBlock(parent); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + }, + '' + + '' + ); + }); }); - - test('Next', function() { - var parent = createStackBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, false); - parent.nextConnection.disconnect(); - assertNextHasBlock(parent, true); + + suite('Remove - Block Connected', function() { + // These are defined separately in each suite. + function createRowBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'row_block', + 'id': 'id0', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + 'id': 'id1', + }, + 'block': { + 'type': 'row_block', + 'id': 'id2', + } + } + } + }, + workspace); + } + + function createStatementBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'statement_block', + 'id': 'id0', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + 'id': 'id1', + }, + 'block': { + 'type': 'statement_block', + 'id': 'id2', + } + } + } + }, + workspace); + } + + function createStackBlocks(workspace) { + return Blockly.serialization.blocks.load( + { + 'type': 'stack_block', + 'id': 'id0', + 'next': { + 'shadow': { + 'type': 'stack_block', + 'id': 'id1', + }, + 'block': { + 'type': 'stack_block', + 'id': 'id2', + } + } + }, + workspace); + } + + test('Value', function() { + var parent = createRowBlocks(this.workspace); + parent.getInput('INPUT').connection.setShadowState(null); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputNotHasBlock(parent, 'INPUT'); + assertSerialization( + parent, + { + 'type': 'row_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Statement', function() { + var parent = createStatementBlocks(this.workspace); + parent.getInput('NAME').connection.setShadowState(null); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputNotHasBlock(parent, 'NAME'); + assertSerialization( + parent, + { + 'type': 'statement_block', + 'id': 'id0', + }, + '' + + '' + ); + }); + + test('Next', function() { + var parent = createStackBlocks(this.workspace); + parent.nextConnection.setShadowState(null); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextNotHasBlock(parent); + assertSerialization( + parent, + { + 'type': 'stack_block', + 'id': 'id0', + }, + '' + + '' + ); + }); }); - - test('Multiple Next', function() { - var parent = createStackBlocks(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, false); - assertNextNotHasBlock(parent.getNextBlock()); - parent.nextConnection.disconnect(); - assertNextHasBlock(parent, true); - assertNextHasBlock(parent.getNextBlock(), true); - }); - }); - - suite('Add - With Shadow Connected', function() { - // These are defined separately in each suite. - function createRowBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStatementBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStackBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - test('Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true, '1'); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true, '2'); - }); - - test('Multiple Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true, '1'); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'a'); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true, '2'); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true, 'b'); - }); - - test('Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true, '1'); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true, '2'); - }); - - test('Multiple Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true, '1'); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true, 'a'); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true, '2'); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true, 'b'); - }); - - test('Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true, '1'); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true, '2'); - }); - - test('Multiple Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true, '1'); - assertNextHasBlock(parent.getNextBlock(), true, 'a'); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true, '2'); - assertNextHasBlock(parent.getNextBlock(), true, 'b'); - }); - }); - - suite('Remove - No Block Connected', function() { - // These are defined separately in each suite. - function createRowBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - function createStatementBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - function createStackBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - test('Value', function() { - var parent = createRowBlock(this.workspace); - parent.getInput('INPUT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'INPUT'); - }); - - test('Statement', function() { - var parent = createStatementBlock(this.workspace); - parent.getInput('STATEMENT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'STATMENT'); - }); - - test('Next', function() { - var parent = createStackBlock(this.workspace); - parent.nextConnection.setShadowDom(null); - assertNextNotHasBlock(parent); - }); - }); - - suite('Remove - Block Connected', function() { - // These are defined separately in each suite. - function createRowBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - function createStatementBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - function createStackBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - ' ' + - '' - ), workspace); - return block; - } - - test('Value', function() { - var parent = createRowBlock(this.workspace); - parent.getInput('INPUT').connection.setShadowDom(null); - assertInputHasBlock(parent, 'INPUT', false); - parent.getInput('INPUT').connection.disconnect(); - assertInputNotHasBlock(parent, 'INPUT'); - }); - - test('Statement', function() { - var parent = createStatementBlock(this.workspace); - parent.getInput('STATEMENT').connection.setShadowDom(null); - assertInputHasBlock(parent, 'STATEMENT', false); - parent.getInput('STATEMENT').connection.disconnect(); - assertInputNotHasBlock(parent, 'STATEMENT'); - }); - - test('Next', function() { - var parent = createStackBlock(this.workspace); - parent.nextConnection.setShadowDom(null); - assertNextHasBlock(parent, false); - parent.nextConnection.disconnect(); - assertNextNotHasBlock(parent); - }); - }); - - suite('Add - Connect & Disconnect - Remove', function() { - // These are defined separately in each suite. - function createRowBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStatementBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - function createStackBlock(workspace) { - var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' - ), workspace); - return block; - } - - test('Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true); - var child = createRowBlock(this.workspace); - parent.getInput('INPUT').connection.connect(child.outputConnection); - assertInputHasBlock(parent, 'INPUT', false); - parent.getInput('INPUT').connection.disconnect(); - assertInputHasBlock(parent, 'INPUT', true); - parent.getInput('INPUT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'INPUT'); - }); - - test('Multiple Value', function() { - var parent = createRowBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('INPUT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'INPUT', true); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true); - var child = createRowBlock(this.workspace); - parent.getInput('INPUT').connection.connect(child.outputConnection); - assertInputHasBlock(parent, 'INPUT', false); - parent.getInput('INPUT').connection.disconnect(); - assertInputHasBlock(parent, 'INPUT', true); - assertInputHasBlock( - parent.getInputTargetBlock('INPUT'), 'INPUT', true); - parent.getInput('INPUT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'INPUT'); - }); - - test('Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true); - var child = createStatementBlock(this.workspace); - parent.getInput('STATEMENT').connection - .connect(child.previousConnection); - assertInputHasBlock(parent, 'STATEMENT', false); - parent.getInput('STATEMENT').connection.disconnect(); - assertInputHasBlock(parent, 'STATEMENT', true); - parent.getInput('STATEMENT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'STATEMENT'); - }); - - test('Multiple Statement', function() { - var parent = createStatementBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.getInput('STATEMENT').connection.setShadowDom(xml); - assertInputHasBlock(parent, 'STATEMENT', true); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true); - var child = createStatementBlock(this.workspace); - parent.getInput('STATEMENT').connection - .connect(child.previousConnection); - assertInputHasBlock(parent, 'STATEMENT', false); - parent.getInput('STATEMENT').connection.disconnect(); - assertInputHasBlock(parent, 'STATEMENT', true); - assertInputHasBlock( - parent.getInputTargetBlock('STATEMENT'), 'STATEMENT', true); - parent.getInput('STATEMENT').connection.setShadowDom(null); - assertInputNotHasBlock(parent, 'STATEMENT'); - }); - - test('Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true); - var child = createStatementBlock(this.workspace); - parent.nextConnection.connect(child.previousConnection); - assertNextHasBlock(parent, false); - parent.nextConnection.disconnect(); - assertNextHasBlock(parent, true); - parent.nextConnection.setShadowDom(null); - assertNextNotHasBlock(parent); - }); - - test('Multiple Next', function() { - var parent = createStackBlock(this.workspace); - var xml = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - parent.nextConnection.setShadowDom(xml); - assertNextHasBlock(parent, true); - assertNextHasBlock(parent.getNextBlock(), true); - var child = createStatementBlock(this.workspace); - parent.nextConnection.connect(child.previousConnection); - assertNextHasBlock(parent, false); - parent.nextConnection.disconnect(); - assertNextHasBlock(parent, true); - assertNextHasBlock(parent.getNextBlock(), true); - parent.nextConnection.setShadowDom(null); - assertNextNotHasBlock(parent); + + suite('Add - Connect & Disconnect - Remove', function() { + // These are defined separately in each suite. + function createRowBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'row_block'}, workspace); + } + + function createStatementBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'statement_block'}, workspace); + } + + function createStackBlock(workspace) { + return Blockly.serialization.blocks + .load({'type': 'stack_block'}, workspace); + } + + test('Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection + .setShadowState({'type': 'row_block'}); + assertInputHasBlock(parent, 'INPUT', true); + var child = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.connect(child.outputConnection); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + parent.getInput('INPUT').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'INPUT'); + }); + + test('Multiple Value', function() { + var parent = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.setShadowState({ + 'type': 'row_block', + 'inputs': { + 'INPUT': { + 'shadow': { + 'type': 'row_block', + } + } + } + }); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + var child = createRowBlock(this.workspace); + parent.getInput('INPUT').connection.connect(child.outputConnection); + assertInputHasBlock(parent, 'INPUT', false); + parent.getInput('INPUT').connection.disconnect(); + assertInputHasBlock(parent, 'INPUT', true); + assertInputHasBlock( + parent.getInputTargetBlock('INPUT'), 'INPUT', true); + parent.getInput('INPUT').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'INPUT'); + }); + + test('Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .setShadowState({'type': 'statement_block'}); + assertInputHasBlock(parent, 'NAME', true); + var child = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .connect(child.previousConnection); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + parent.getInput('NAME').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'NAME'); + }); + + test('Multiple Statement', function() { + var parent = createStatementBlock(this.workspace); + parent.getInput('NAME').connection.setShadowState({ + 'type': 'statement_block', + 'inputs': { + 'NAME': { + 'shadow': { + 'type': 'statement_block', + } + } + } + }); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + var child = createStatementBlock(this.workspace); + parent.getInput('NAME').connection + .connect(child.previousConnection); + assertInputHasBlock(parent, 'NAME', false); + parent.getInput('NAME').connection.disconnect(); + assertInputHasBlock(parent, 'NAME', true); + assertInputHasBlock( + parent.getInputTargetBlock('NAME'), 'NAME', true); + parent.getInput('NAME').connection.setShadowState(null); + assertInputNotHasBlock(parent, 'NAME'); + }); + + test('Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowState({'type': 'stack_block'}); + var child = createStatementBlock(this.workspace); + parent.nextConnection.connect(child.previousConnection); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + parent.nextConnection.setShadowState(null); + assertNextNotHasBlock(parent); + }); + + test('Multiple Next', function() { + var parent = createStackBlock(this.workspace); + parent.nextConnection.setShadowState({ + 'type': 'stack_block', + 'next': { + 'shadow': { + 'type': 'stack_block' + } + } + }); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + var child = createStatementBlock(this.workspace); + parent.nextConnection.connect(child.previousConnection); + assertNextHasBlock(parent, false); + parent.nextConnection.disconnect(); + assertNextHasBlock(parent, true); + assertNextHasBlock(parent.getNextBlock(), true); + parent.nextConnection.setShadowState(null); + assertNextNotHasBlock(parent); + }); }); }); }); @@ -912,7 +2646,7 @@ suite('Connection', function() { }, { "type": "input_statement", - "name": "STATEMENT", + "name": "NAME", "check": 'check1' } ], @@ -925,7 +2659,7 @@ suite('Connection', function() { "args0": [ { "type": "input_statement", - "name": "STATEMENT", + "name": "NAME", "check": 'check1' } ], @@ -938,7 +2672,7 @@ suite('Connection', function() { "args0": [ { "type": "input_statement", - "name": "STATEMENT", + "name": "NAME", "check": 'check2' } ], @@ -951,7 +2685,7 @@ suite('Connection', function() { "args0": [ { "type": "input_statement", - "name": "STATEMENT", + "name": "NAME", "check": 'check1' } ], @@ -984,13 +2718,13 @@ suite('Connection', function() { var newParent = this.workspace.newBlock('statement_block'); var child = this.workspace.newBlock('stack_block'); - oldParent.getInput('STATEMENT').connection + oldParent.getInput('NAME').connection .connect(child.previousConnection); - newParent.getInput('STATEMENT').connection + newParent.getInput('NAME').connection .connect(child.previousConnection); chai.assert.isFalse( - oldParent.getInput('STATEMENT').connection.isConnected()); + oldParent.getInput('NAME').connection.isConnected()); this.assertBlockCount(3); }); @@ -1029,15 +2763,15 @@ suite('Connection', function() { var xml = Blockly.Xml.textToDom( '' ); - newParent.getInput('STATEMENT').connection.setShadowDom(xml); + newParent.getInput('NAME').connection.setShadowDom(xml); chai.assert.isTrue( - newParent.getInputTargetBlock('STATEMENT').isShadow()); + newParent.getInputTargetBlock('NAME').isShadow()); - newParent.getInput('STATEMENT').connection + newParent.getInput('NAME').connection .connect(child.previousConnection); chai.assert.isFalse( - newParent.getInputTargetBlock('STATEMENT').isShadow()); + newParent.getInputTargetBlock('NAME').isShadow()); this.assertBlockCount(2); }); @@ -1082,15 +2816,15 @@ suite('Connection', function() { var xml = Blockly.Xml.textToDom( '' ); - newParent.getInput('STATEMENT').connection.setShadowDom(xml); - newParent.getInputTargetBlock('STATEMENT') + newParent.getInput('NAME').connection.setShadowDom(xml); + newParent.getInputTargetBlock('NAME') .setFieldValue('new', 'FIELD'); - newParent.getInput('STATEMENT').connection + newParent.getInput('NAME').connection .connect(child.previousConnection); - newParent.getInput('STATEMENT').connection.disconnect(); + newParent.getInput('NAME').connection.disconnect(); - const target = newParent.getInputTargetBlock('STATEMENT'); + const target = newParent.getInputTargetBlock('NAME'); chai.assert.isTrue(target.isShadow()); chai.assert.equal(target.getFieldValue('FIELD'), 'new'); this.assertBlockCount(3); @@ -1408,16 +3142,16 @@ suite('Connection', function() { var parent = this.workspace.newBlock('statement_block'); var oldChild = this.workspace.newBlock('stack_block'); var newChild = this.workspace.newBlock('stack_block'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild); + parent.getInputTargetBlock('NAME'), newChild); chai.assert.isTrue(newChild.nextConnection.isConnected()); chai.assert.equal(newChild.getNextBlock(), oldChild); this.assertBlockCount(3); @@ -1428,17 +3162,17 @@ suite('Connection', function() { var oldChild = this.workspace.newBlock('stack_block'); var newChild1 = this.workspace.newBlock('stack_block_1to2'); var newChild2 = this.workspace.newBlock('stack_block_2to1'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); newChild1.nextConnection.connect(newChild2.previousConnection); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild1.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild1); + parent.getInputTargetBlock('NAME'), newChild1); chai.assert.isTrue(newChild2.nextConnection.isConnected()); chai.assert.equal(newChild2.getNextBlock(), oldChild); this.assertBlockCount(4); @@ -1448,17 +3182,17 @@ suite('Connection', function() { var parent = this.workspace.newBlock('statement_block'); var oldChild = this.workspace.newBlock('stack_block'); var newChild = this.workspace.newBlock('stack_block_1to2'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); var spy = sinon.spy(oldChild.previousConnection, 'onFailedConnect'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild); + parent.getInputTargetBlock('NAME'), newChild); chai.assert.isFalse(newChild.nextConnection.isConnected()); chai.assert.isTrue(spy.calledOnce); this.assertBlockCount(3); @@ -1468,17 +3202,17 @@ suite('Connection', function() { var parent = this.workspace.newBlock('statement_block'); var oldChild = this.workspace.newBlock('stack_block'); var newChild = this.workspace.newBlock('stack_block_noend'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); var spy = sinon.spy(oldChild.previousConnection, 'onFailedConnect'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild); + parent.getInputTargetBlock('NAME'), newChild); chai.assert.isTrue(spy.calledOnce); this.assertBlockCount(3); }); @@ -1489,20 +3223,20 @@ suite('Connection', function() { var parent = this.workspace.newBlock('statement_block'); var oldChild = this.workspace.newBlock('stack_block'); var newChild = this.workspace.newBlock('stack_block'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); var xml = Blockly.Xml.textToDom( '' ); newChild.nextConnection.setShadowDom(xml); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild); + parent.getInputTargetBlock('NAME'), newChild); chai.assert.isTrue(newChild.nextConnection.isConnected()); chai.assert.equal(newChild.getNextBlock(), oldChild); this.assertBlockCount(3); @@ -1513,7 +3247,7 @@ suite('Connection', function() { var oldChild = this.workspace.newBlock('stack_block'); var newChild1 = this.workspace.newBlock('stack_block_1to2'); var newChild2 = this.workspace.newBlock('stack_block_2to1'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); newChild1.nextConnection.connect(newChild2.previousConnection); var xml = Blockly.Xml.textToDom( @@ -1521,13 +3255,13 @@ suite('Connection', function() { ); newChild2.nextConnection.setShadowDom(xml); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild1.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild1); + parent.getInputTargetBlock('NAME'), newChild1); chai.assert.isTrue(newChild2.nextConnection.isConnected()); chai.assert.equal(newChild2.getNextBlock(), oldChild); this.assertBlockCount(4); @@ -1537,7 +3271,7 @@ suite('Connection', function() { var parent = this.workspace.newBlock('statement_block'); var oldChild = this.workspace.newBlock('stack_block'); var newChild = this.workspace.newBlock('stack_block_1to2'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(oldChild.previousConnection); var xml = Blockly.Xml.textToDom( '' @@ -1545,13 +3279,13 @@ suite('Connection', function() { newChild.nextConnection.setShadowDom(xml); var spy = sinon.spy(oldChild.previousConnection, 'onFailedConnect'); - parent.getInput('STATEMENT').connection + parent.getInput('NAME').connection .connect(newChild.previousConnection); chai.assert.isTrue( - parent.getInput('STATEMENT').connection.isConnected()); + parent.getInput('NAME').connection.isConnected()); chai.assert.equal( - parent.getInputTargetBlock('STATEMENT'), newChild); + parent.getInputTargetBlock('NAME'), newChild); chai.assert.isTrue(newChild.nextConnection.isConnected()); chai.assert.isTrue(newChild.getNextBlock().isShadow()); chai.assert.isTrue(spy.calledOnce); diff --git a/tests/mocha/jso_deserialization_test.js b/tests/mocha/jso_deserialization_test.js index f248dca3f..2f9cf59f2 100644 --- a/tests/mocha/jso_deserialization_test.js +++ b/tests/mocha/jso_deserialization_test.js @@ -627,7 +627,7 @@ suite('JSO Deserialization', function() { }); }); - suite.skip('Real child of shadow', function() { + suite('Real child of shadow', function() { test('Input', function() { const state = { 'blocks': { @@ -664,10 +664,10 @@ suite('JSO Deserialization', function() { 'type': 'text_print', 'next': { 'shadow': { - 'type': 'text_input', + 'type': 'text_print', 'next': { 'block': { - 'type': 'text_input', + 'type': 'text_print', } } } diff --git a/tests/mocha/jso_serialization_test.js b/tests/mocha/jso_serialization_test.js index 51dfe3c27..0d4aeb5ce 100644 --- a/tests/mocha/jso_serialization_test.js +++ b/tests/mocha/jso_serialization_test.js @@ -424,7 +424,7 @@ suite('JSO Serialization', function() { const block = this.workspace.newBlock(blockType); block.getInput(inputName).connection.setShadowDom( Blockly.Xml.textToDom( - '')); + '')); return block; }; @@ -435,7 +435,7 @@ suite('JSO Serialization', function() { childBlock.outputConnection || childBlock.previousConnection); block.getInput(inputName).connection.setShadowDom( Blockly.Xml.textToDom( - '')); + '')); return block; }; @@ -501,11 +501,11 @@ suite('JSO Serialization', function() { this.assertChild('row_block', 'INPUT'); }); - test.skip('Shadow', function() { + test('Shadow', function() { this.assertShadow('row_block', 'INPUT'); }); - test.skip('Overwritten shadow', function() { + test('Overwritten shadow', function() { this.assertOverwrittenShadow('row_block', 'INPUT'); }); }); @@ -531,11 +531,11 @@ suite('JSO Serialization', function() { this.assertChild('statement_block', 'NAME'); }); - test.skip('Shadow', function() { + test('Shadow', function() { this.assertShadow('statement_block', 'NAME'); }); - test.skip('Overwritten shadow', function() { + test('Overwritten shadow', function() { this.assertOverwrittenShadow('statement_block', 'NAME'); }); @@ -595,7 +595,7 @@ suite('JSO Serialization', function() { const block = this.workspace.newBlock('stack_block'); block.nextConnection.setShadowDom( Blockly.Xml.textToDom( - '')); + '')); return block; }; @@ -605,7 +605,7 @@ suite('JSO Serialization', function() { block.nextConnection.connect(childBlock.previousConnection); block.nextConnection.setShadowDom( Blockly.Xml.textToDom( - '')); + '')); return block; }; }); @@ -619,14 +619,14 @@ suite('JSO Serialization', function() { jso['next'], {'block': { 'type': 'stack_block', 'id': 'id2'}}); }); - test.skip('Shadow', function() { + test('Shadow', function() { const block = this.createNextWithShadow(); const jso = Blockly.serialization.blocks.save(block); chai.assert.deepInclude( jso['next'], {'shadow': { 'type': 'stack_block', 'id': 'test'}}); }); - test.skip('Overwritten shadow', function() { + test('Overwritten shadow', function() { const block = this.createNextWithShadowAndChild(); const jso = Blockly.serialization.blocks.save(block); chai.assert.deepInclude(