mirror of
https://github.com/google/blockly.git
synced 2026-01-06 16:40:07 +01:00
feat: adds hooks for serializing plugins (#5276)
* Reformat registry tests * Add tests for plugin hooks * Add plugin hooks for serialization * Switch PluginSerializer to IPluginSerializer * fix: types * fix: PR comments * fix: tests * cleanup: formatting * fix: types * feat: add respecting case in registry * feat: add separate registry for serializers * fix: rename serialiation registry alias * fix: move serializer interface into interface dir
This commit is contained in:
committed by
alschmiedt
parent
486123e4ff
commit
07057d087c
71
core/interfaces/i_serializer.js
Normal file
71
core/interfaces/i_serializer.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The record type for an object containing functions for
|
||||
* serializing part of the workspace.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.module('Blockly.serialization.ISerializer');
|
||||
goog.module.declareLegacyNamespace();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const Workspace = goog.requireType('Blockly.Workspace');
|
||||
|
||||
|
||||
/**
|
||||
* Serializes and deserializes a plugin.
|
||||
* @interface
|
||||
*/
|
||||
class ISerializer {
|
||||
constructor() {
|
||||
/**
|
||||
* A priority value used to determine the order of deserializing plugins.
|
||||
* More positive priorities are deserialized before less positive
|
||||
* priorities. Eg if you have priorities (0, -10, 10, 100) the order of
|
||||
* deserialiation will be (100, 10, 0, -10).
|
||||
* If two plugins have the same priority, they are deserialized in an
|
||||
* arbitrary order relative to each other.
|
||||
* @type {number}
|
||||
*/
|
||||
this.priority;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars, valid-jsdoc */
|
||||
|
||||
/**
|
||||
* Saves the state of the plugin.
|
||||
* @param {!Workspace} workspace The workspace the plugin to serialize is
|
||||
* associated with.
|
||||
* @return {?} A JS object containing the plugin's state, or null if
|
||||
* there is no state to record.
|
||||
*/
|
||||
save(workspace) {}
|
||||
|
||||
/* eslint-enable valid-jsdoc */
|
||||
|
||||
/**
|
||||
* Loads the state of the plugin.
|
||||
* @param {?} state The state of the plugin to deserialize. This will always
|
||||
* be non-null.
|
||||
* @param {!Workspace} workspace The workspace the plugin to deserialize is
|
||||
* associated with.
|
||||
*/
|
||||
load(state, workspace) {}
|
||||
|
||||
/**
|
||||
* Clears the state of the plugin.
|
||||
* @param {!Workspace} workspace The workspace the plugin to clear the state
|
||||
* of is associated with.
|
||||
*/
|
||||
clear(workspace) {}
|
||||
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
|
||||
exports.ISerializer = ISerializer;
|
||||
@@ -38,6 +38,8 @@ const Renderer = goog.requireType('Blockly.blockRendering.Renderer');
|
||||
const Theme = goog.requireType('Blockly.Theme');
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const ToolboxItem = goog.requireType('Blockly.ToolboxItem');
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const ISerializer = goog.requireType('Blockly.serialization.ISerializer');
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,12 +47,20 @@ const ToolboxItem = goog.requireType('Blockly.ToolboxItem');
|
||||
* registering and the value being the constructor function.
|
||||
* e.g. {'field': {'field_angle': Blockly.FieldAngle}}
|
||||
*
|
||||
* @type {Object<string, Object<string, function(new:?)>>}
|
||||
* @type {!Object<string, !Object<string, (function(new:?)|!Object)>>}
|
||||
*/
|
||||
const typeMap = Object.create(null);
|
||||
/** @private */
|
||||
exports.typeMap_ = typeMap;
|
||||
|
||||
/**
|
||||
* A map of maps. With the keys being the type and caseless name of the class we
|
||||
* are registring, and the value being the most recent cased name for that
|
||||
* registration.
|
||||
* @type {!Object<string, !Object<string, string>>}
|
||||
*/
|
||||
const nameMap = Object.create(null);
|
||||
|
||||
/**
|
||||
* The string used to register the default class for a type of plugin.
|
||||
* @type {string}
|
||||
@@ -118,6 +128,12 @@ Type.METRICS_MANAGER = new Type('metricsManager');
|
||||
/** @type {!Type<IBlockDragger>} */
|
||||
Type.BLOCK_DRAGGER = new Type('blockDragger');
|
||||
|
||||
/**
|
||||
* @type {!Type<ISerializer>}
|
||||
* @package
|
||||
*/
|
||||
Type.SERIALIZER = new Type('serializer');
|
||||
|
||||
/**
|
||||
* Registers a class based on a type and name.
|
||||
* @param {string|!Type<T>} type The type of the plugin.
|
||||
@@ -129,7 +145,7 @@ Type.BLOCK_DRAGGER = new Type('blockDragger');
|
||||
* an already registered item.
|
||||
* @throws {Error} if the type or name is empty, a name with the given type has
|
||||
* already been registered, or if the given class or object is not valid for
|
||||
* it's type.
|
||||
* it's type.
|
||||
* @template T
|
||||
*/
|
||||
const register = function(type, name, registryItem, opt_allowOverrides) {
|
||||
@@ -146,25 +162,29 @@ const register = function(type, name, registryItem, opt_allowOverrides) {
|
||||
'Invalid name "' + name + '". The name must be a' +
|
||||
' non-empty string.');
|
||||
}
|
||||
name = name.toLowerCase();
|
||||
const caselessName = name.toLowerCase();
|
||||
if (!registryItem) {
|
||||
throw Error('Can not register a null value');
|
||||
}
|
||||
let typeRegistry = typeMap[type];
|
||||
let nameRegistry = nameMap[type];
|
||||
// If the type registry has not been created, create it.
|
||||
if (!typeRegistry) {
|
||||
typeRegistry = typeMap[type] = Object.create(null);
|
||||
nameRegistry = nameMap[type] = Object.create(null);
|
||||
}
|
||||
|
||||
// Validate that the given class has all the required properties.
|
||||
validate(type, registryItem);
|
||||
|
||||
// Don't throw an error if opt_allowOverrides is true.
|
||||
if (!opt_allowOverrides && typeRegistry[name]) {
|
||||
if (!opt_allowOverrides && typeRegistry[caselessName]) {
|
||||
throw Error(
|
||||
'Name "' + name + '" with type "' + type + '" already registered.');
|
||||
'Name "' + caselessName + '" with type "' + type +
|
||||
'" already registered.');
|
||||
}
|
||||
typeRegistry[name] = registryItem;
|
||||
typeRegistry[caselessName] = registryItem;
|
||||
nameRegistry[caselessName] = name;
|
||||
};
|
||||
exports.register = register;
|
||||
|
||||
@@ -203,6 +223,7 @@ const unregister = function(type, name) {
|
||||
return;
|
||||
}
|
||||
delete typeMap[type][name];
|
||||
delete nameMap[type][name];
|
||||
};
|
||||
exports.unregister = unregister;
|
||||
|
||||
@@ -288,6 +309,43 @@ const getObject = function(type, name, opt_throwIfMissing) {
|
||||
};
|
||||
exports.getObject = getObject;
|
||||
|
||||
/**
|
||||
* Returns a map of items registered with the given type.
|
||||
* @param {string|!Type<T>} type The type of the plugin. (e.g. Category)
|
||||
* @param {boolean} opt_cased Whether or not to return a map with cased keys
|
||||
* (rather than caseless keys). False by default.
|
||||
* @param {boolean=} opt_throwIfMissing Whether or not to throw an error if we
|
||||
* are unable to find the object. False by default.
|
||||
* @return {?Object<string, ?T|?function(new:T, ...?)>} A map of objects with
|
||||
* the given type, or null if none exists.
|
||||
* @template T
|
||||
*/
|
||||
const getAllItems = function(type, opt_cased, opt_throwIfMissing) {
|
||||
type = String(type).toLowerCase();
|
||||
const typeRegistry = typeMap[type];
|
||||
if (!typeRegistry) {
|
||||
const msg = `Unable to find [${type}] in the registry.`;
|
||||
if (opt_throwIfMissing) {
|
||||
throw new Error(`${msg} You must require or register a ${type} plugin.`);
|
||||
} else {
|
||||
console.warn(msg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!opt_cased) {
|
||||
return typeRegistry;
|
||||
}
|
||||
const nameRegistry = nameMap[type];
|
||||
const casedRegistry = Object.create(null);
|
||||
const keys = Object.keys(typeRegistry);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
casedRegistry[nameRegistry[key]] = typeRegistry[key];
|
||||
}
|
||||
return casedRegistry;
|
||||
};
|
||||
exports.getAllItems = getAllItems;
|
||||
|
||||
/**
|
||||
* Gets the class from Blockly options for the given type.
|
||||
* This is used for plugins that override a built in feature. (e.g. Toolbox)
|
||||
|
||||
@@ -82,4 +82,7 @@ goog.require('Blockly.zelos.Renderer');
|
||||
// Classic is the default theme.
|
||||
goog.require('Blockly.Themes.Classic');
|
||||
|
||||
goog.require('Blockly.serialization.blocks');
|
||||
goog.require('Blockly.serialization.registry');
|
||||
goog.require('Blockly.serialization.variables');
|
||||
goog.require('Blockly.serialization.workspaces');
|
||||
|
||||
@@ -19,10 +19,14 @@ 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');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {ISerializer} = goog.requireType('Blockly.serialization.ISerializer');
|
||||
const Size = goog.require('Blockly.utils.Size');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const Workspace = goog.requireType('Blockly.Workspace');
|
||||
const inputTypes = goog.require('Blockly.inputTypes');
|
||||
const priorities = goog.require('Blockly.serialization.priorities');
|
||||
const serializationRegistry = goog.require('Blockly.serialization.registry');
|
||||
|
||||
|
||||
// TODO: Remove this once lint is fixed.
|
||||
@@ -577,3 +581,71 @@ const initBlock = function(block, rendered) {
|
||||
block.initModel();
|
||||
}
|
||||
};
|
||||
|
||||
// Aliases to disambiguate saving/loading within the serializer.
|
||||
const saveBlock = save;
|
||||
const loadBlock = load;
|
||||
|
||||
/**
|
||||
* Serializer for saving and loading block state.
|
||||
* @implements {ISerializer}
|
||||
*/
|
||||
class BlockSerializer {
|
||||
constructor() {
|
||||
/**
|
||||
* The priority for deserializing blocks.
|
||||
* @type {number}
|
||||
*/
|
||||
this.priority = priorities.BLOCKS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the blocks of the given workspace.
|
||||
* @param {!Workspace} workspace The workspace to save the blocks of.
|
||||
* @return {?{languageVersion: number, blocks:!Array<!State>}} The state of
|
||||
* the workspace's blocks, or null if there are no blocks.
|
||||
*/
|
||||
save(workspace) {
|
||||
const blockState = [];
|
||||
for (const block of workspace.getTopBlocks(false)) {
|
||||
const state = saveBlock(block, {addCoordinates: true});
|
||||
if (state) {
|
||||
blockState.push(state);
|
||||
}
|
||||
}
|
||||
if (blockState.length) {
|
||||
return {
|
||||
'languageVersion': 0, // Currently unused.
|
||||
'blocks': blockState
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the blocks defined by the given state into the given
|
||||
* workspace.
|
||||
* @param {{languageVersion: number, blocks:!Array<!State>}} state The state
|
||||
* of the blocks to deserialize.
|
||||
* @param {!Workspace} workspace The workspace to deserialize into.
|
||||
*/
|
||||
load(state, workspace) {
|
||||
const blockStates = state['blocks'];
|
||||
for (const state of blockStates) {
|
||||
loadBlock(state, workspace, {recordUndo: Events.getRecordUndo()});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of any blocks that exist on the workspace.
|
||||
* @param {!Workspace} workspace The workspace to clear the blocks of.
|
||||
*/
|
||||
clear(workspace) {
|
||||
// Cannot use workspace.clear() because that also removes variables.
|
||||
for (const block of workspace.getTopBlocks(false)) {
|
||||
block.dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializationRegistry.register('blocks', new BlockSerializer());
|
||||
|
||||
34
core/serialization/priorities.js
Normal file
34
core/serialization/priorities.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Includes constants for the priorities of different plugin
|
||||
* serializers. Higher priorities are deserialized first.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The top level namespace for priorities of plugin serializers.
|
||||
* @namespace Blockly.serialization.priorities
|
||||
*/
|
||||
goog.module('Blockly.serialization.priorities');
|
||||
goog.module.declareLegacyNamespace();
|
||||
|
||||
|
||||
/**
|
||||
* The priority for deserializing variables.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
exports.VARIABLES = 100;
|
||||
|
||||
/**
|
||||
* The priority for deserializing blocks.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
exports.BLOCKS = 50;
|
||||
40
core/serialization/registry.js
Normal file
40
core/serialization/registry.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Contains functions registering serializers (eg blocks,
|
||||
* variables, plugins, etc).
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.module('Blockly.serialization.registry');
|
||||
goog.module.declareLegacyNamespace();
|
||||
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {ISerializer} = goog.requireType('Blockly.serialization.ISerializer');
|
||||
const registry = goog.require('Blockly.registry');
|
||||
|
||||
|
||||
/**
|
||||
* Registers the given serializer so that it can be used for serialization and
|
||||
* deserialization.
|
||||
* @param {string} name The name of the serializer to register.
|
||||
* @param {ISerializer} serializer The serializer to register.
|
||||
*/
|
||||
const register = function(name, serializer) {
|
||||
registry.register(registry.Type.SERIALIZER, name, serializer);
|
||||
};
|
||||
exports.register = register;
|
||||
|
||||
/**
|
||||
* Unregisters the serializer associated with the given name.
|
||||
* @param {string} name The name of the serializer to unregister.
|
||||
*/
|
||||
const unregister = function(name) {
|
||||
registry.unregister(registry.Type.SERIALIZER, name);
|
||||
};
|
||||
exports.unregister = unregister;
|
||||
@@ -15,9 +15,13 @@ goog.module.declareLegacyNamespace();
|
||||
|
||||
const Events = goog.require('Blockly.Events');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {ISerializer} = goog.requireType('Blockly.serialization.ISerializer');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const VariableModel = goog.requireType('Blockly.VariableModel');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const Workspace = goog.requireType('Blockly.Workspace');
|
||||
const priorities = goog.require('Blockly.serialization.priorities');
|
||||
const serializationRegistry = goog.require('Blockly.serialization.registry');
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,7 +40,7 @@ exports.State = State;
|
||||
* @param {!VariableModel} variableModel The variable to serialize.
|
||||
* @return {!State} The serialized state of the variable.
|
||||
*/
|
||||
const save = function(variableModel) {
|
||||
const saveVariable = function(variableModel) {
|
||||
const state = {
|
||||
'name': variableModel.name,
|
||||
'id': variableModel.getId()
|
||||
@@ -46,12 +50,9 @@ const save = function(variableModel) {
|
||||
}
|
||||
return state;
|
||||
};
|
||||
/** @package */
|
||||
exports.save = save;
|
||||
|
||||
/**
|
||||
* Loads the variable represented by the given state into the given workspace.
|
||||
* Do not call this directly, use workspace.createVariable instead.
|
||||
* @param {!State} state The state of a variable to deserialize into the
|
||||
* workspace.
|
||||
* @param {!Workspace} workspace The workspace to add the variable to.
|
||||
@@ -59,7 +60,7 @@ exports.save = save;
|
||||
* recordUndo: If true, events triggered by this function will be undo-able
|
||||
* by the user. False by default.
|
||||
*/
|
||||
const load = function(state, workspace, {recordUndo = false} = {}) {
|
||||
const loadVariable = function(state, workspace, {recordUndo = false} = {}) {
|
||||
const prevRecordUndo = Events.getRecordUndo();
|
||||
Events.setRecordUndo(recordUndo);
|
||||
const existingGroup = Events.getGroup();
|
||||
@@ -72,5 +73,54 @@ const load = function(state, workspace, {recordUndo = false} = {}) {
|
||||
Events.setGroup(existingGroup);
|
||||
Events.setRecordUndo(prevRecordUndo);
|
||||
};
|
||||
/** @package */
|
||||
exports.load = load;
|
||||
|
||||
/**
|
||||
* Serializer for saving and loading variable state.
|
||||
* @implements {ISerializer}
|
||||
*/
|
||||
class VariableSerializer {
|
||||
constructor() {
|
||||
/**
|
||||
* The priority for deserializing variables.
|
||||
* @type {number}
|
||||
*/
|
||||
this.priority = priorities.VARIABLES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the variables of the given workspace.
|
||||
* @param {!Workspace} workspace The workspace to save the variables of.
|
||||
* @return {?Array<!State>} The state of the workspace's variables, or null
|
||||
* if there are no variables.
|
||||
*/
|
||||
save(workspace) {
|
||||
const variableStates = [];
|
||||
for (const variable of workspace.getAllVariables()) {
|
||||
variableStates.push(saveVariable(variable));
|
||||
}
|
||||
return variableStates.length ? variableStates : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the variable defined by the given state into the given
|
||||
* workspace.
|
||||
* @param {!Array<!State>} state The state of the variables to deserialize.
|
||||
* @param {!Workspace} workspace The workspace to deserialize into.
|
||||
*/
|
||||
load(state, workspace) {
|
||||
for (const varState of state) {
|
||||
loadVariable(varState, workspace, {recordUndo: Events.getRecordUndo()});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of any variables or potential variables that exist on the
|
||||
* workspace.
|
||||
* @param {!Workspace} workspace The workspace to clear the variables of.
|
||||
*/
|
||||
clear(workspace) {
|
||||
workspace.getVariableMap().clear();
|
||||
}
|
||||
}
|
||||
|
||||
serializationRegistry.register('variables', new VariableSerializer());
|
||||
|
||||
@@ -16,9 +16,8 @@ goog.module.declareLegacyNamespace();
|
||||
const Events = goog.require('Blockly.Events');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const Workspace = goog.require('Blockly.Workspace');
|
||||
const blocks = goog.require('Blockly.serialization.blocks');
|
||||
const dom = goog.require('Blockly.utils.dom');
|
||||
const variables = goog.require('Blockly.serialization.variables');
|
||||
const registry = goog.require('Blockly.registry');
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,31 +27,13 @@ const variables = goog.require('Blockly.serialization.variables');
|
||||
*/
|
||||
const save = function(workspace) {
|
||||
const state = Object.create(null);
|
||||
|
||||
// TODO: Switch this to use plugin serialization system (once it is built).
|
||||
const variableStates = [];
|
||||
const vars = workspace.getAllVariables();
|
||||
for (let i = 0; i < vars.length; i++) {
|
||||
variableStates.push(variables.save(vars[i]));
|
||||
}
|
||||
if (variableStates.length) {
|
||||
state['variables'] = variableStates;
|
||||
}
|
||||
|
||||
const blockStates = [];
|
||||
for (let block of workspace.getTopBlocks(false)) {
|
||||
const blockState = blocks.save(block, {addCoordinates: true});
|
||||
if (blockState) {
|
||||
blockStates.push(blockState);
|
||||
const serializerMap = registry.getAllItems(registry.Type.SERIALIZER, true);
|
||||
for (const key in serializerMap) {
|
||||
const save = serializerMap[key].save(workspace);
|
||||
if (save) {
|
||||
state[key] = save;
|
||||
}
|
||||
}
|
||||
if (blockStates.length) {
|
||||
// This is an object to support adding language version later.
|
||||
state['blocks'] = {
|
||||
'blocks': blockStates
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
exports.save = save;
|
||||
@@ -67,8 +48,14 @@ exports.save = save;
|
||||
* by the user. False by default.
|
||||
*/
|
||||
const load = function(state, workspace, {recordUndo = false} = {}) {
|
||||
// TODO: Switch this to use plugin serialization system (once it is built).
|
||||
// TODO: Add something for clearing the state before deserializing.
|
||||
const serializerMap = registry.getAllItems(registry.Type.SERIALIZER, true);
|
||||
if (!serializerMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
const deserializers = Object.entries(serializerMap)
|
||||
.sort(([, {priority: priorityA}], [, {priority: priorityB}]) =>
|
||||
priorityB - priorityA);
|
||||
|
||||
const prevRecordUndo = Events.getRecordUndo();
|
||||
Events.setRecordUndo(recordUndo);
|
||||
@@ -81,18 +68,19 @@ const load = function(state, workspace, {recordUndo = false} = {}) {
|
||||
if (workspace.setResizesEnabled) {
|
||||
workspace.setResizesEnabled(false);
|
||||
}
|
||||
|
||||
if (state['variables']) {
|
||||
const variableStates = state['variables'];
|
||||
for (let i = 0; i < variableStates.length; i++) {
|
||||
variables.load(variableStates[i], workspace, {recordUndo});
|
||||
}
|
||||
|
||||
// We want to trigger clearing in reverse priority order so plugins don't end
|
||||
// up missing dependencies.
|
||||
for (const [, deserializer] of deserializers.reverse()) {
|
||||
deserializer.clear(workspace);
|
||||
}
|
||||
|
||||
if (state['blocks']) {
|
||||
const blockStates = state['blocks']['blocks'];
|
||||
for (let i = 0; i < blockStates.length; i++) {
|
||||
blocks.load(blockStates[i], workspace, {recordUndo});
|
||||
// reverse() is destructive, so we have to re-reverse to correct the order.
|
||||
for (let [name, deserializer] of deserializers.reverse()) {
|
||||
name = /** @type {string} */ (name);
|
||||
const pluginState = state[name];
|
||||
if (pluginState) {
|
||||
deserializer.load(state[name], workspace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistra
|
||||
goog.addDependency('../../core/interfaces/i_registrable_field.js', ['Blockly.IRegistrableField'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_selectable_toolbox_item.js', ['Blockly.ISelectableToolboxItem'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_serializer.js', ['Blockly.serialization.ISerializer'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.IToolboxItem'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
@@ -193,13 +194,15 @@ goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['B
|
||||
goog.addDependency('../../core/renderers/zelos/measurables/top_row.js', ['Blockly.zelos.TopRow'], ['Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.connectionTypes', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {'lang': 'es6', 'module': 'goog'});
|
||||
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/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.blocks', 'Blockly.serialization.registry', 'Blockly.serialization.variables', '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.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.serialization.priorities', 'Blockly.serialization.registry', '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/serialization/priorities.js', ['Blockly.serialization.priorities'], [], {'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/registry.js', ['Blockly.serialization.registry'], ['Blockly.registry'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.Events', 'Blockly.serialization.priorities', 'Blockly.serialization.registry'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events', 'Blockly.Workspace', 'Blockly.registry', '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'});
|
||||
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
|
||||
@@ -116,6 +116,7 @@ goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistra
|
||||
goog.addDependency('../../core/interfaces/i_registrable_field.js', ['Blockly.IRegistrableField'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_selectable_toolbox_item.js', ['Blockly.ISelectableToolboxItem'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_serializer.js', ['Blockly.serialization.ISerializer'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.IToolboxItem'], [], {'lang': 'es6', 'module': 'goog'});
|
||||
@@ -193,13 +194,15 @@ goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['B
|
||||
goog.addDependency('../../core/renderers/zelos/measurables/top_row.js', ['Blockly.zelos.TopRow'], ['Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.connectionTypes', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {'lang': 'es6', 'module': 'goog'});
|
||||
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/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.blocks', 'Blockly.serialization.registry', 'Blockly.serialization.variables', '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.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.serialization.priorities', 'Blockly.serialization.registry', '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/serialization/priorities.js', ['Blockly.serialization.priorities'], [], {'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/registry.js', ['Blockly.serialization.registry'], ['Blockly.registry'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.Events', 'Blockly.serialization.priorities', 'Blockly.serialization.registry'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events', 'Blockly.Workspace', 'Blockly.registry', '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'});
|
||||
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
|
||||
|
||||
@@ -94,188 +94,128 @@ suite('JSO Deserialization', function() {
|
||||
});
|
||||
|
||||
suite('Var create', function() {
|
||||
suite('Top-level call', function() {
|
||||
test('Just var', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': false
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
test('Record undo', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace, {recordUndo: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': true
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
test('Grouping', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.Events.setGroup('my group');
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'group': 'my group'
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
test('Multiple vars grouped', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
},
|
||||
{
|
||||
'name': 'test2',
|
||||
'id': 'testId2',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
const calls = this.eventsFireStub.getCalls();
|
||||
const group = calls[0].args[0].group;
|
||||
chai.assert.isTrue(calls.every(call => call.args[0].group == group));
|
||||
});
|
||||
|
||||
test('Var with block', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
],
|
||||
'blocks': {
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'variables_get',
|
||||
'id': 'blockId',
|
||||
'x': 42,
|
||||
'y': 42,
|
||||
'fields': {
|
||||
'VAR': 'testId'
|
||||
}
|
||||
},
|
||||
]
|
||||
test('Just var', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
const calls = this.eventsFireStub.getCalls();
|
||||
const count = calls.reduce((acc, call) => {
|
||||
if (call.args[0] instanceof Blockly.Events.VarCreate) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
chai.assert.equal(count, 1);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{'varName': 'test', 'varId': 'testId', 'varType': ''},
|
||||
this.workspace.id);
|
||||
});
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': false
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
suite('Direct call', function() {
|
||||
test('Just var', function() {
|
||||
const state = {
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
};
|
||||
Blockly.serialization.variables.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': false
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
test('Record undo', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace, {recordUndo: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': true
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
test('Record undo', function() {
|
||||
const state = {
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
};
|
||||
Blockly.serialization.variables
|
||||
.load(state, this.workspace, {recordUndo: true});
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'recordUndo': true
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
test('Grouping', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.Events.setGroup('my group');
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'group': 'my group'
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
|
||||
test('Grouping', function() {
|
||||
const state = {
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
};
|
||||
Blockly.Events.setGroup('my group');
|
||||
Blockly.serialization.variables.load(state, this.workspace);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
test('Multiple vars grouped', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
},
|
||||
{
|
||||
'name': 'test2',
|
||||
'id': 'testId2',
|
||||
}
|
||||
]
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
const calls = this.eventsFireStub.getCalls();
|
||||
const group = calls[0].args[0].group;
|
||||
chai.assert.isTrue(calls.every(call => call.args[0].group == group));
|
||||
});
|
||||
|
||||
test('Var with block', function() {
|
||||
const state = {
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test',
|
||||
'id': 'testId',
|
||||
}
|
||||
],
|
||||
'blocks': {
|
||||
'blocks': [
|
||||
{
|
||||
'varName': 'test',
|
||||
'varId': 'testId',
|
||||
'varType': '',
|
||||
'group': 'my group'
|
||||
'type': 'variables_get',
|
||||
'id': 'blockId',
|
||||
'x': 42,
|
||||
'y': 42,
|
||||
'fields': {
|
||||
'VAR': 'testId'
|
||||
}
|
||||
},
|
||||
this.workspace.id);
|
||||
});
|
||||
]
|
||||
}
|
||||
};
|
||||
Blockly.serialization.workspaces.load(state, this.workspace);
|
||||
const calls = this.eventsFireStub.getCalls();
|
||||
const count = calls.reduce((acc, call) => {
|
||||
if (call.args[0] instanceof Blockly.Events.VarCreate) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
chai.assert.equal(count, 1);
|
||||
assertEventFired(
|
||||
this.eventsFireStub,
|
||||
Blockly.Events.VarCreate,
|
||||
{'varName': 'test', 'varId': 'testId', 'varType': ''},
|
||||
this.workspace.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -681,4 +621,60 @@ suite('JSO Deserialization', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Priority', function() {
|
||||
const blocksSerializer = Blockly.registry.getClass(
|
||||
Blockly.registry.Type.SERIALIZER, 'blocks');
|
||||
const variablesSerializer = Blockly.registry.getClass(
|
||||
Blockly.registry.Type.SERIALIZER, 'variables');
|
||||
|
||||
Blockly.serialization.registry.unregister('blocks');
|
||||
Blockly.serialization.registry.unregister('variables');
|
||||
|
||||
const calls = [];
|
||||
|
||||
const first = {
|
||||
priority: 100,
|
||||
save: () => null,
|
||||
load: () => calls.push('first-load'),
|
||||
clear: () => calls.push('first-clear'),
|
||||
};
|
||||
const second = {
|
||||
priority: 0,
|
||||
save: () => null,
|
||||
load: () => calls.push('second-load'),
|
||||
clear: () => calls.push('second-clear'),
|
||||
};
|
||||
const third = {
|
||||
priority: -10,
|
||||
save: () => null,
|
||||
load: () => calls.push('third-load'),
|
||||
clear: () => calls.push('third-clear'),
|
||||
};
|
||||
|
||||
Blockly.serialization.registry.register('third', third);
|
||||
Blockly.serialization.registry.register('first', first);
|
||||
Blockly.serialization.registry.register('second', second);
|
||||
|
||||
Blockly.serialization.workspaces.load(
|
||||
{'first': {}, 'third': {}, 'second': {}}, this.workspace);
|
||||
|
||||
Blockly.serialization.registry.unregister('first');
|
||||
Blockly.serialization.registry.unregister('second');
|
||||
Blockly.serialization.registry.unregister('third');
|
||||
|
||||
Blockly.serialization.registry.register('blocks', blocksSerializer);
|
||||
Blockly.serialization.registry.register('variables', variablesSerializer);
|
||||
|
||||
chai.assert.deepEqual(
|
||||
calls,
|
||||
[
|
||||
'third-clear',
|
||||
'second-clear',
|
||||
'first-clear',
|
||||
'first-load',
|
||||
'second-load',
|
||||
'third-load'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -699,20 +699,21 @@ suite('JSO Serialization', function() {
|
||||
|
||||
suite('Variables', function() {
|
||||
test('Without type', function() {
|
||||
const variable = this.workspace.createVariable('testVar', '', 'testId');
|
||||
const jso = Blockly.serialization.variables.save(variable);
|
||||
assertProperty(jso, 'name', 'testVar');
|
||||
assertProperty(jso, 'id', 'testId');
|
||||
assertNoProperty(jso, 'type');
|
||||
this.workspace.createVariable('testVar', '', 'testId');
|
||||
const jso = Blockly.serialization.workspaces.save(this.workspace);
|
||||
const variable = jso['variables'][0];
|
||||
assertProperty(variable, 'name', 'testVar');
|
||||
assertProperty(variable, 'id', 'testId');
|
||||
assertNoProperty(variable, 'type');
|
||||
});
|
||||
|
||||
test('With type', function() {
|
||||
const variable = this.workspace
|
||||
.createVariable('testVar', 'testType', 'testId');
|
||||
const jso = Blockly.serialization.variables.save(variable);
|
||||
assertProperty(jso, 'name', 'testVar');
|
||||
assertProperty(jso, 'id', 'testId');
|
||||
assertProperty(jso, 'type', 'testType');
|
||||
this.workspace.createVariable('testVar', 'testType', 'testId');
|
||||
const jso = Blockly.serialization.workspaces.save(this.workspace);
|
||||
const variable = jso['variables'][0];
|
||||
assertProperty(variable, 'name', 'testVar');
|
||||
assertProperty(variable, 'id', 'testId');
|
||||
assertProperty(variable, 'type', 'testType');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ goog.module('Blockly.test.registry');
|
||||
const {sharedTestSetup, sharedTestTeardown} = goog.require('Blockly.test.helpers');
|
||||
|
||||
|
||||
|
||||
suite('Registry', function() {
|
||||
var TestClass = function() {};
|
||||
TestClass.prototype.testMethod = function() {
|
||||
@@ -18,39 +19,195 @@ suite('Registry', function() {
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
if (Blockly.registry.typeMap_['test'] &&
|
||||
Blockly.registry.typeMap_['test']['test_name']) {
|
||||
delete Blockly.registry.typeMap_['test']['test_name'];
|
||||
if (Blockly.registry.hasItem('test', 'test_name')) {
|
||||
Blockly.registry.unregister('test', 'test_name');
|
||||
}
|
||||
});
|
||||
|
||||
suite('Registration', function() {
|
||||
test('Simple', function() {
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
});
|
||||
|
||||
test('Empty String Key', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.register('test', '', TestClass);
|
||||
}, 'Invalid name');
|
||||
});
|
||||
|
||||
test('Class as Key', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.register('test', TestClass, '');
|
||||
}, 'Invalid name');
|
||||
});
|
||||
|
||||
test('Overwrite a Key', function() {
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
}, 'already registered');
|
||||
});
|
||||
|
||||
test('Null Value', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.register('test', 'field_custom_test', null);
|
||||
}, 'Can not register a null value');
|
||||
});
|
||||
});
|
||||
|
||||
suite('hasItem', function() {
|
||||
setup(function() {
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
});
|
||||
|
||||
test('Has', function() {
|
||||
chai.assert.isTrue(Blockly.registry.hasItem('test', 'test_name'));
|
||||
});
|
||||
|
||||
suite('Does not have', function() {
|
||||
test('Type', function() {
|
||||
chai.assert.isFalse(Blockly.registry.hasItem('bad_type', 'test_name'));
|
||||
});
|
||||
|
||||
test('Name', function() {
|
||||
chai.assert.isFalse(Blockly.registry.hasItem('test', 'bad_name'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Case', function() {
|
||||
test('Caseless type', function() {
|
||||
chai.assert.isTrue(Blockly.registry.hasItem('TEST', 'test_name'));
|
||||
});
|
||||
|
||||
test('Caseless name', function() {
|
||||
chai.assert.isTrue(Blockly.registry.hasItem('test', 'TEST_NAME'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('getClass', function() {
|
||||
setup(function() {
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
});
|
||||
|
||||
test('Has', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getClass('test', 'test_name'));
|
||||
});
|
||||
|
||||
suite('Does not have', function() {
|
||||
test('Type', function() {
|
||||
chai.assert.isNull(Blockly.registry.getClass('bad_type', 'test_name'));
|
||||
});
|
||||
|
||||
test('Name', function() {
|
||||
chai.assert.isNull(Blockly.registry.getClass('test', 'bad_name'));
|
||||
});
|
||||
|
||||
test('Throw if missing', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.getClass('test', 'bad_name', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Case', function() {
|
||||
test('Caseless type', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getClass('TEST', 'test_name'));
|
||||
});
|
||||
|
||||
test('Caseless name', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getClass('test', 'TEST_NAME'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('getObject', function() {
|
||||
setup(function() {
|
||||
Blockly.registry.register('test', 'test_name', {});
|
||||
});
|
||||
|
||||
test('Has', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getObject('test', 'test_name'));
|
||||
});
|
||||
|
||||
suite('Does not have', function() {
|
||||
test('Type', function() {
|
||||
chai.assert.isNull(Blockly.registry.getObject('bad_type', 'test_name'));
|
||||
});
|
||||
|
||||
test('Name', function() {
|
||||
chai.assert.isNull(Blockly.registry.getObject('test', 'bad_name'));
|
||||
});
|
||||
|
||||
test('Throw if missing', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.getObject('test', 'bad_name', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Case', function() {
|
||||
test('Caseless type', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getObject('TEST', 'test_name'));
|
||||
});
|
||||
|
||||
test('Caseless name', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getObject('test', 'TEST_NAME'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('getAllItems', function() {
|
||||
setup(function() {
|
||||
Blockly.registry.register('test', 'test_name', {});
|
||||
Blockly.registry.register('test', 'casedNAME', {});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
Blockly.registry.unregister('test', 'casedname');
|
||||
});
|
||||
|
||||
test('Has', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getAllItems('test'));
|
||||
});
|
||||
|
||||
test('Does not have', function() {
|
||||
chai.assert.isNull(Blockly.registry.getAllItems('bad_type'));
|
||||
});
|
||||
|
||||
test('Throw if missing', function() {
|
||||
chai.assert.throws(function() {
|
||||
Blockly.registry.getAllItems('bad_type', false, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Ignore type case', function() {
|
||||
chai.assert.isNotNull(Blockly.registry.getAllItems('TEST'));
|
||||
});
|
||||
|
||||
test('Respect name case', function() {
|
||||
chai.assert.deepEqual(
|
||||
Blockly.registry.getAllItems('test', true),
|
||||
{
|
||||
'test_name': {},
|
||||
'casedNAME': {}
|
||||
});
|
||||
});
|
||||
|
||||
test('Respect overwriting name case', function() {
|
||||
Blockly.registry.register('test', 'CASEDname', {}, true);
|
||||
chai.assert.deepEqual(
|
||||
Blockly.registry.getAllItems('test', true),
|
||||
{
|
||||
'test_name': {},
|
||||
'CASEDname': {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('getClassFromOptions', function() {
|
||||
setup(function() {
|
||||
this.defaultClass = function() {};
|
||||
@@ -62,25 +219,31 @@ suite('Registry', function() {
|
||||
'test' : 'test_name'
|
||||
}
|
||||
};
|
||||
Blockly.registry.typeMap_['test'] = {
|
||||
'test_name': TestClass,
|
||||
'default': this.defaultClass
|
||||
};
|
||||
Blockly.registry.register('test', 'test_name', TestClass);
|
||||
Blockly.registry.register('test', 'default', this.defaultClass);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
Blockly.registry.unregister('test', 'default');
|
||||
});
|
||||
|
||||
test('Simple - Plugin name given', function() {
|
||||
var testClass = Blockly.registry.getClassFromOptions('test', this.options);
|
||||
chai.assert.instanceOf(new testClass(), TestClass);
|
||||
});
|
||||
|
||||
test('Simple - Plugin class given', function() {
|
||||
this.options.plugins['test'] = TestClass;
|
||||
var testClass = Blockly.registry.getClassFromOptions('test', this.options);
|
||||
chai.assert.instanceOf(new testClass(), TestClass);
|
||||
});
|
||||
|
||||
test('No Plugin Name Given', function() {
|
||||
delete this.options['plugins']['test'];
|
||||
var testClass = Blockly.registry.getClassFromOptions('test', this.options);
|
||||
chai.assert.instanceOf(new testClass(), this.defaultClass);
|
||||
});
|
||||
|
||||
test('Incorrect Plugin Name', function() {
|
||||
this.options['plugins']['test'] = 'random';
|
||||
var testClass;
|
||||
|
||||
Reference in New Issue
Block a user