mirror of
https://github.com/google/blockly.git
synced 2026-05-12 15:10:11 +02:00
fix: create and delete events, and the trashcan (#5425)
* fix: create and delete events with JSON serialization * fix: trashcan with JSON serialization * fix: build * fix: tests * fix: PR comments * fix: types * fix: tests
This commit is contained in:
+35
-17
@@ -26,8 +26,8 @@ goog.require('Blockly.Events.Abstract');
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.xml');
|
||||
goog.require('Blockly.Xml');
|
||||
goog.require('Blockly.serialization.blocks');
|
||||
|
||||
goog.requireType('Blockly.Block');
|
||||
|
||||
@@ -247,12 +247,15 @@ Blockly.Events.Create = function(opt_block) {
|
||||
this.recordUndo = false;
|
||||
}
|
||||
|
||||
if (opt_block.workspace.rendered) {
|
||||
this.xml = Blockly.Xml.blockToDomWithXY(opt_block);
|
||||
} else {
|
||||
this.xml = Blockly.Xml.blockToDom(opt_block);
|
||||
}
|
||||
this.xml = Blockly.Xml.blockToDomWithXY(opt_block);
|
||||
this.ids = Blockly.Events.getDescendantIds(opt_block);
|
||||
|
||||
/**
|
||||
* JSON representation of the block that was just created.
|
||||
* @type {!Blockly.serialization.blocks.State}
|
||||
*/
|
||||
this.json = /** @type {!Blockly.serialization.blocks.State} */
|
||||
(Blockly.serialization.blocks.save(opt_block, {addCoordinates: true}));
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.Events.Create, Blockly.Events.BlockBase);
|
||||
|
||||
@@ -279,6 +282,7 @@ Blockly.Events.Create.prototype.toJson = function() {
|
||||
var json = Blockly.Events.Create.superClass_.toJson.call(this);
|
||||
json['xml'] = Blockly.Xml.domToText(this.xml);
|
||||
json['ids'] = this.ids;
|
||||
json['json'] = this.json;
|
||||
if (!this.recordUndo) {
|
||||
json['recordUndo'] = this.recordUndo;
|
||||
}
|
||||
@@ -293,6 +297,8 @@ Blockly.Events.Create.prototype.fromJson = function(json) {
|
||||
Blockly.Events.Create.superClass_.fromJson.call(this, json);
|
||||
this.xml = Blockly.Xml.textToDom(json['xml']);
|
||||
this.ids = json['ids'];
|
||||
this.json = /** @type {!Blockly.serialization.blocks.State} */
|
||||
(json['json']);
|
||||
if (json['recordUndo'] !== undefined) {
|
||||
this.recordUndo = json['recordUndo'];
|
||||
}
|
||||
@@ -305,9 +311,7 @@ Blockly.Events.Create.prototype.fromJson = function(json) {
|
||||
Blockly.Events.Create.prototype.run = function(forward) {
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
var xml = Blockly.utils.xml.createElement('xml');
|
||||
xml.appendChild(this.xml);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
Blockly.serialization.blocks.load(this.json, workspace);
|
||||
} else {
|
||||
for (var i = 0, id; (id = this.ids[i]); i++) {
|
||||
var block = workspace.getBlockById(id);
|
||||
@@ -341,12 +345,22 @@ Blockly.Events.Delete = function(opt_block) {
|
||||
this.recordUndo = false;
|
||||
}
|
||||
|
||||
if (opt_block.workspace.rendered) {
|
||||
this.oldXml = Blockly.Xml.blockToDomWithXY(opt_block);
|
||||
} else {
|
||||
this.oldXml = Blockly.Xml.blockToDom(opt_block);
|
||||
}
|
||||
this.oldXml = Blockly.Xml.blockToDomWithXY(opt_block);
|
||||
this.ids = Blockly.Events.getDescendantIds(opt_block);
|
||||
|
||||
/**
|
||||
* Was the block that was just deleted a shadow?
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.wasShadow = opt_block.isShadow();
|
||||
|
||||
/**
|
||||
* JSON representation of the block that was just deleted.
|
||||
* @type {!Blockly.serialization.blocks.State}
|
||||
*/
|
||||
this.oldJson = /** @type {!Blockly.serialization.blocks.State} */
|
||||
(Blockly.serialization.blocks.save(opt_block, {addCoordinates: true}));
|
||||
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.Events.Delete, Blockly.Events.BlockBase);
|
||||
|
||||
@@ -372,6 +386,8 @@ Blockly.Events.Delete.prototype.toJson = function() {
|
||||
var json = Blockly.Events.Delete.superClass_.toJson.call(this);
|
||||
json['oldXml'] = Blockly.Xml.domToText(this.oldXml);
|
||||
json['ids'] = this.ids;
|
||||
json['wasShadow'] = this.wasShadow;
|
||||
json['oldJson'] = this.oldJson;
|
||||
if (!this.recordUndo) {
|
||||
json['recordUndo'] = this.recordUndo;
|
||||
}
|
||||
@@ -386,6 +402,10 @@ Blockly.Events.Delete.prototype.fromJson = function(json) {
|
||||
Blockly.Events.Delete.superClass_.fromJson.call(this, json);
|
||||
this.oldXml = Blockly.Xml.textToDom(json['oldXml']);
|
||||
this.ids = json['ids'];
|
||||
this.wasShadow =
|
||||
json['wasShadow'] || this.oldXml.tagName.toLowerCase() == 'shadow';
|
||||
this.oldJson = /** @type {!Blockly.serialization.blocks.State} */
|
||||
(json['oldJson']);
|
||||
if (json['recordUndo'] !== undefined) {
|
||||
this.recordUndo = json['recordUndo'];
|
||||
}
|
||||
@@ -408,9 +428,7 @@ Blockly.Events.Delete.prototype.run = function(forward) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var xml = Blockly.utils.xml.createElement('xml');
|
||||
xml.appendChild(this.oldXml);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
Blockly.serialization.blocks.load(this.oldJson, workspace);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+48
-50
@@ -30,7 +30,6 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.Svg');
|
||||
goog.require('Blockly.utils.toolbox');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.Events.Abstract');
|
||||
goog.requireType('Blockly.IDraggable');
|
||||
@@ -64,7 +63,7 @@ Blockly.Trashcan = function(workspace) {
|
||||
this.id = 'trashcan';
|
||||
|
||||
/**
|
||||
* A list of XML (stored as strings) representing blocks in the trashcan.
|
||||
* A list of JSON (stored as strings) representing blocks in the trashcan.
|
||||
* @type {!Array<string>}
|
||||
* @private
|
||||
*/
|
||||
@@ -426,8 +425,10 @@ Blockly.Trashcan.prototype.openFlyout = function() {
|
||||
if (this.contentsIsOpen()) {
|
||||
return;
|
||||
}
|
||||
var xml = this.contents_.map(Blockly.Xml.textToDom);
|
||||
this.flyout.show(xml);
|
||||
var contents = this.contents_.map(function(string) {
|
||||
return JSON.parse(string);
|
||||
});
|
||||
this.flyout.show(contents);
|
||||
this.fireUiEvent_(true);
|
||||
};
|
||||
|
||||
@@ -706,14 +707,12 @@ Blockly.Trashcan.prototype.onDelete_ = function(event) {
|
||||
if (this.workspace_.options.maxTrashcanContents <= 0) {
|
||||
return;
|
||||
}
|
||||
// Must check that the tagName exists since oldXml can be a DocumentFragment.
|
||||
if (event.type == Blockly.Events.BLOCK_DELETE && event.oldXml.tagName &&
|
||||
event.oldXml.tagName.toLowerCase() != 'shadow') {
|
||||
var cleanedXML = this.cleanBlockXML_(event.oldXml);
|
||||
if (this.contents_.indexOf(cleanedXML) != -1) {
|
||||
if (event.type == Blockly.Events.BLOCK_DELETE && !event.wasShadow) {
|
||||
var cleanedJson = this.cleanBlockJson_(event.oldJson);
|
||||
if (this.contents_.indexOf(cleanedJson) != -1) {
|
||||
return;
|
||||
}
|
||||
this.contents_.unshift(cleanedXML);
|
||||
this.contents_.unshift(cleanedJson);
|
||||
while (this.contents_.length >
|
||||
this.workspace_.options.maxTrashcanContents) {
|
||||
this.contents_.pop();
|
||||
@@ -724,50 +723,49 @@ Blockly.Trashcan.prototype.onDelete_ = function(event) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts XML representing a block into text that can be stored in the
|
||||
* content array.
|
||||
* @param {!Element} xml An XML tree defining the block and any
|
||||
* connected child blocks.
|
||||
* @return {string} Text representing the XML tree, cleaned of all unnecessary
|
||||
* attributes.
|
||||
* Converts JSON representing a block into text that can be stored in the
|
||||
* content array.
|
||||
* @param {!Blockly.serialization.blocks.State} json A JSON representation of
|
||||
* a block's state.
|
||||
* @return {string} Text representing the JSON, cleaned of all unnecessary
|
||||
* attributes.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Trashcan.prototype.cleanBlockXML_ = function(xml) {
|
||||
var xmlBlock = xml.cloneNode(true);
|
||||
var node = xmlBlock;
|
||||
while (node) {
|
||||
// Things like text inside tags are still treated as nodes, but they
|
||||
// don't have attributes (or the removeAttribute function) so we can
|
||||
// skip removing attributes from them.
|
||||
if (node.removeAttribute) {
|
||||
node.removeAttribute('x');
|
||||
node.removeAttribute('y');
|
||||
node.removeAttribute('id');
|
||||
node.removeAttribute('disabled');
|
||||
if (node.nodeName == 'comment') { // Future proof just in case.
|
||||
node.removeAttribute('h');
|
||||
node.removeAttribute('w');
|
||||
node.removeAttribute('pinned');
|
||||
}
|
||||
Blockly.Trashcan.prototype.cleanBlockJson_ = function(json) {
|
||||
json = /** @type {!Blockly.serialization.blocks.State} */
|
||||
(JSON.parse(JSON.stringify(json))); // Create deep copy.
|
||||
|
||||
function cleanRec(json) {
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to go down the tree
|
||||
var nextNode = node.firstChild || node.nextSibling;
|
||||
// If we can't go down, try to go back up the tree.
|
||||
if (!nextNode) {
|
||||
nextNode = node.parentNode;
|
||||
while (nextNode) {
|
||||
// We are valid again!
|
||||
if (nextNode.nextSibling) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
break;
|
||||
}
|
||||
// Try going up again. If parentNode is null that means we have
|
||||
// reached the top, and we will break out of both loops.
|
||||
nextNode = nextNode.parentNode;
|
||||
}
|
||||
delete json['id'];
|
||||
delete json['x'];
|
||||
delete json['y'];
|
||||
delete json['enabled'];
|
||||
|
||||
if (json['icons'] && json['icons']['comment']) {
|
||||
var comment = json['icons']['comment'];
|
||||
delete comment['height'];
|
||||
delete comment['width'];
|
||||
delete comment['pinned'];
|
||||
}
|
||||
|
||||
var inputs = json['inputs'];
|
||||
for (var name in inputs) {
|
||||
var input = inputs[name];
|
||||
cleanRec(input['block']);
|
||||
cleanRec(input['shadow']);
|
||||
}
|
||||
if (json['next']) {
|
||||
var next = json['next'];
|
||||
cleanRec(next['block']);
|
||||
cleanRec(next['shadow']);
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
return Blockly.Xml.domToText(xmlBlock);
|
||||
|
||||
cleanRec(json);
|
||||
json['kind'] = 'BLOCK';
|
||||
return JSON.stringify(json);
|
||||
};
|
||||
|
||||
+96
-26
@@ -562,48 +562,118 @@ suite('Events', function() {
|
||||
viewLeft: 0, scale: 1.2, oldScale: 1})},
|
||||
];
|
||||
var blockEventTestCases = [
|
||||
{title: 'Block change', class: Blockly.Events.BlockChange,
|
||||
{
|
||||
title: 'Block change',
|
||||
class: Blockly.Events.BlockChange,
|
||||
getArgs: (thisObj) => [thisObj.block, 'collapsed', null, false, true],
|
||||
getExpectedJson: (thisObj) => ({type: 'change',
|
||||
blockId: thisObj.block.id, element: 'collapsed', oldValue: false,
|
||||
newValue: true})},
|
||||
{title: 'Block create', class: Blockly.Events.BlockCreate,
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'change',
|
||||
blockId: thisObj.block.id,
|
||||
element: 'collapsed',
|
||||
oldValue: false,
|
||||
newValue: true
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Block create',
|
||||
class: Blockly.Events.BlockCreate,
|
||||
getArgs: (thisObj) => [thisObj.block],
|
||||
getExpectedJson: (thisObj) => ({type: 'create',
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'create',
|
||||
blockId: thisObj.block.id,
|
||||
xml: '<block xmlns="https://developers.google.com/blockly/xml"' +
|
||||
' type="simple_test_block" id="testBlockId1"></block>',
|
||||
ids: [thisObj.block.id]})},
|
||||
{title: 'Block create (shadow)', class: Blockly.Events.BlockCreate,
|
||||
' type="simple_test_block" id="testBlockId1" x="0" y="0">' +
|
||||
'</block>',
|
||||
ids: [thisObj.block.id],
|
||||
json: {
|
||||
'type': 'simple_test_block',
|
||||
'id': 'testBlockId1',
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Block create (shadow)',
|
||||
class: Blockly.Events.BlockCreate,
|
||||
getArgs: (thisObj) => [thisObj.shadowBlock],
|
||||
getExpectedJson: (thisObj) => ({type: 'create',
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'create',
|
||||
blockId: thisObj.shadowBlock.id,
|
||||
xml: '<shadow xmlns="https://developers.google.com/blockly/xml"' +
|
||||
' type="simple_test_block" id="testBlockId2"></shadow>',
|
||||
ids: [thisObj.shadowBlock.id], recordUndo: false})},
|
||||
{title: 'Block delete', class: Blockly.Events.BlockDelete,
|
||||
' type="simple_test_block" id="testBlockId2" x="0" y="0">' +
|
||||
'</shadow>',
|
||||
ids: [thisObj.shadowBlock.id],
|
||||
json: {
|
||||
'type': 'simple_test_block',
|
||||
'id': 'testBlockId2',
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
},
|
||||
recordUndo: false
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Block delete',
|
||||
class: Blockly.Events.BlockDelete,
|
||||
getArgs: (thisObj) => [thisObj.block],
|
||||
getExpectedJson: (thisObj) => ({type: 'delete',
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'delete',
|
||||
blockId: thisObj.block.id,
|
||||
oldXml: '<block xmlns="https://developers.google.com/blockly/xml"' +
|
||||
' type="simple_test_block" id="testBlockId1"></block>',
|
||||
ids: [thisObj.block.id]})},
|
||||
{title: 'Block delete (shadow)', class: Blockly.Events.BlockDelete,
|
||||
' type="simple_test_block" id="testBlockId1" x="0" y="0">' +
|
||||
'</block>',
|
||||
ids: [thisObj.block.id],
|
||||
wasShadow: false,
|
||||
oldJson: {
|
||||
'type': 'simple_test_block',
|
||||
'id': 'testBlockId1',
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Block delete (shadow)',
|
||||
class: Blockly.Events.BlockDelete,
|
||||
getArgs: (thisObj) => [thisObj.shadowBlock],
|
||||
getExpectedJson: (thisObj) => ({type: 'delete',
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'delete',
|
||||
blockId: thisObj.shadowBlock.id,
|
||||
oldXml: '<shadow xmlns="https://developers.google.com/blockly/xml"' +
|
||||
' type="simple_test_block" id="testBlockId2"></shadow>',
|
||||
ids: [thisObj.shadowBlock.id], recordUndo: false})},
|
||||
' type="simple_test_block" id="testBlockId2" x="0" y="0">' +
|
||||
'</shadow>',
|
||||
ids: [thisObj.shadowBlock.id],
|
||||
wasShadow: true,
|
||||
oldJson: {
|
||||
'type': 'simple_test_block',
|
||||
'id': 'testBlockId2',
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
},
|
||||
recordUndo: false
|
||||
})
|
||||
},
|
||||
// TODO(#4577) Test serialization of move event coordinate properties.
|
||||
{title: 'Block move', class: Blockly.Events.BlockMove,
|
||||
{
|
||||
title: 'Block move',
|
||||
class: Blockly.Events.BlockMove,
|
||||
getArgs: (thisObj) => [thisObj.block],
|
||||
getExpectedJson: (thisObj) => ({type: 'move',
|
||||
blockId: thisObj.block.id})},
|
||||
{title: 'Block move (shadow)', class: Blockly.Events.BlockMove,
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'move',
|
||||
blockId: thisObj.block.id
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Block move (shadow)',
|
||||
class: Blockly.Events.BlockMove,
|
||||
getArgs: (thisObj) => [thisObj.shadowBlock],
|
||||
getExpectedJson: (thisObj) => ({type: 'move',
|
||||
blockId: thisObj.shadowBlock.id, recordUndo: false})},
|
||||
getExpectedJson: (thisObj) => ({
|
||||
type: 'move',
|
||||
blockId: thisObj.shadowBlock.id,
|
||||
recordUndo: false
|
||||
})
|
||||
},
|
||||
];
|
||||
var workspaceEventTestCases = [
|
||||
{title: 'Finished Loading', class: Blockly.Events.FinishedLoading,
|
||||
|
||||
@@ -684,7 +684,11 @@ suite('JSO Deserialization', function() {
|
||||
Blockly.Blocks['test_block'] = {
|
||||
init: function() { },
|
||||
|
||||
mutationToDom: function() { },
|
||||
mutationToDom: function() {
|
||||
var container = Blockly.utils.xml.createElement('mutation');
|
||||
container.setAttribute('value', 'some value');
|
||||
return container;
|
||||
},
|
||||
|
||||
domToMutation: function(element) {
|
||||
this.someProperty = element.getAttribute('value');
|
||||
|
||||
@@ -457,18 +457,18 @@ function assertNthCallEventArgEquals(spy, n, instanceType, expectedProperties,
|
||||
assertXmlProperties_(eventArg, xmlProperties);
|
||||
}
|
||||
|
||||
function defineStackBlock() {
|
||||
function defineStackBlock(name = 'stack_block') {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "stack_block",
|
||||
"type": name,
|
||||
"message0": "",
|
||||
"previousStatement": null,
|
||||
"nextStatement": null
|
||||
}]);
|
||||
}
|
||||
|
||||
function defineRowBlock() {
|
||||
function defineRowBlock(name = 'row_block') {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "row_block",
|
||||
"type": name,
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
@@ -480,9 +480,9 @@ function defineRowBlock() {
|
||||
}]);
|
||||
}
|
||||
|
||||
function defineStatementBlock() {
|
||||
function defineStatementBlock(name = 'statement_block') {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "statement_block",
|
||||
"type": name,
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
@@ -498,9 +498,9 @@ function defineStatementBlock() {
|
||||
}]);
|
||||
}
|
||||
|
||||
function defineBasicBlockWithField() {
|
||||
function defineBasicBlockWithField(name = 'test_field_block') {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "test_field_block",
|
||||
"type": name,
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
|
||||
@@ -10,14 +10,13 @@ suite("Trashcan", function() {
|
||||
'<xml xmlns="https://developers.google.com/blockly/xml">' +
|
||||
xmlString + '</xml>');
|
||||
xml = xml.children[0];
|
||||
var event = new Blockly.Events.Delete();
|
||||
event.oldXml = xml;
|
||||
event.workspaceId = workspace.id;
|
||||
var block = Blockly.Xml.domToBlock(xml, workspace);
|
||||
var event = new Blockly.Events.Delete(block);
|
||||
Blockly.Events.fire(event);
|
||||
}
|
||||
function fireNonDeleteEvent(workspace, oldXml) {
|
||||
var event = new Blockly.Events.Abstract();
|
||||
event.type = 'dummy_type';
|
||||
event.type = 'test_field_block';
|
||||
event.workspaceId = workspace.id;
|
||||
if (oldXml) {
|
||||
event.oldXml = oldXml;
|
||||
@@ -27,17 +26,27 @@ suite("Trashcan", function() {
|
||||
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
defineBasicBlockWithField();
|
||||
defineRowBlock();
|
||||
defineRowBlock('row_block2');
|
||||
defineStatementBlock();
|
||||
defineStatementBlock('statement_block2');
|
||||
defineStackBlock();
|
||||
defineStackBlock('stack_block2');
|
||||
defineMutatorBlocks();
|
||||
this.workspace = Blockly.inject('blocklyDiv',
|
||||
{'trashcan': true, 'maxTrashcanContents': Infinity});
|
||||
this.trashcan = this.workspace.trashcan;
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
Blockly.Extensions.unregister('xml_mutator');
|
||||
Blockly.Extensions.unregister('jso_mutator');
|
||||
});
|
||||
|
||||
suite("Events", function() {
|
||||
test("Delete", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="test_field_block"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("Non-Delete", function() {
|
||||
@@ -47,7 +56,7 @@ suite("Trashcan", function() {
|
||||
test("Non-Delete w/ oldXml", function() {
|
||||
var xml = Blockly.Xml.textToDom(
|
||||
'<xml xmlns="https://developers.google.com/blockly/xml">' +
|
||||
' <block type="dummy_type"/>' +
|
||||
' <block type="test_field_block"/>' +
|
||||
'</xml>'
|
||||
);
|
||||
xml = xml.children[0];
|
||||
@@ -55,7 +64,7 @@ suite("Trashcan", function() {
|
||||
chai.assert.equal(this.trashcan.contents_.length, 0);
|
||||
});
|
||||
test("Shadow Delete", function() {
|
||||
fireDeleteEvent(this.workspace, '<shadow type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<shadow type="test_field_block"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 0);
|
||||
});
|
||||
test("Click without contents - fires workspace click", function() {
|
||||
@@ -68,7 +77,7 @@ suite("Trashcan", function() {
|
||||
this.workspace.id, null);
|
||||
});
|
||||
test("Click with contents - fires trashcanOpen", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="test_field_block"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
// Stub flyout interaction.
|
||||
var showFlyoutStub = sinon.stub(this.trashcan.flyout, "show");
|
||||
@@ -102,56 +111,52 @@ suite("Trashcan", function() {
|
||||
});
|
||||
suite("Unique Contents", function() {
|
||||
test("Simple", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="test_field_block"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="test_field_block"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("Different Coords", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" x="10" y="10"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" x="20" y="20"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block" x="10" y="10"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block" x="20" y="20"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("Different IDs", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" id="id1"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" id="id2"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block" id="id1"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block" id="id2"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("No Disabled - Disabled True", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" disabled="true"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block"/>');
|
||||
fireDeleteEvent(
|
||||
this.workspace, '<block type="test_field_block" disabled="true"/>');
|
||||
// Disabled tags get removed because disabled blocks aren't allowed to
|
||||
// be dragged from flyouts. See #2239 and #3243.
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("No Editable - Editable False", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" editable="false"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("No Movable - Movable False", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type" movable="false"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("Different Field Values", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <field name="dummy_name">dummy_value1</field>' +
|
||||
'<block type="test_field_block">' +
|
||||
' <field name="NAME">dummy_value1</field>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <field name="dummy_name">dummy_value2</field>' +
|
||||
'<block type="test_field_block">' +
|
||||
' <field name="NAME">dummy_value2</field>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("No Values - Values", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="row_block"/>');
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <value name="dummy_input">' +
|
||||
' <block type="dummy_type"/>' +
|
||||
'<block type="row_block">' +
|
||||
' <value name="INPUT">' +
|
||||
' <block type="row_block"/>' +
|
||||
' </value>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -159,27 +164,27 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Value Blocks", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <value name="dummy_input">' +
|
||||
' <block type="dummy_type1"/>' +
|
||||
'<block type="row_block">' +
|
||||
' <value name="INPUT">' +
|
||||
' <block type="row_block"/>' +
|
||||
' </value>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <value name="dummy_input">' +
|
||||
' <block type="dummy_type2"/>' +
|
||||
'<block type="row_block">' +
|
||||
' <value name="INPUT">' +
|
||||
' <block type="row_block2"/>' +
|
||||
' </value>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("No Statements - Statements", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="statement_block"/>');
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <statement name="dummy_input">' +
|
||||
' <block type="dummy_type"/>' +
|
||||
'<block type="statement_block">' +
|
||||
' <statement name="NAME">' +
|
||||
' <block type="statement_block"/>' +
|
||||
' </statement>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -187,27 +192,27 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Statement Blocks", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <statement name="dummy_input">' +
|
||||
' <block type="dummy_type1"/>' +
|
||||
'<block type="statement_block">' +
|
||||
' <statement name="NAME">' +
|
||||
' <block type="statement_block"/>' +
|
||||
' </statement>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <statement name="dummy_input">' +
|
||||
' <block type="dummy_type2"/>' +
|
||||
'<block type="test_field_block">' +
|
||||
' <statement name="NAME">' +
|
||||
' <block type="statement_block2"/>' +
|
||||
' </statement>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("No Next - Next", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="stack_block"/>');
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="stack_block">' +
|
||||
' <next>' +
|
||||
' <block type="dummy_type"/>' +
|
||||
' <block type="stack_block"/>' +
|
||||
' </next>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -215,25 +220,25 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Next Blocks", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="stack_block">' +
|
||||
' <next>' +
|
||||
' <block type="dummy_type1"/>' +
|
||||
' <block type="stack_block"/>' +
|
||||
' </next>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="stack_block">' +
|
||||
' <next>' +
|
||||
' <block type="dummy_type2"/>' +
|
||||
' <block type="stack_block2"/>' +
|
||||
' </next>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("No Comment - Comment", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="test_field_block"/>');
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment>comment_text</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -241,12 +246,12 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Comment Text", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment>comment_text1</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment>comment_text2</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -254,12 +259,12 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Comment Size", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment h="10" w="10">comment_text</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment h="20" w="20">comment_text</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
@@ -268,36 +273,27 @@ suite("Trashcan", function() {
|
||||
});
|
||||
test("Different Comment Pinned", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment pinned="false">comment_text</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
'<block type="test_field_block">' +
|
||||
' <comment pinned="true">comment_text</comment>' +
|
||||
'</block>'
|
||||
);
|
||||
// pinned tags are removed b/c the blocks appear the same.
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
});
|
||||
test("No Mutator - Mutator", function() {
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <mutation dummy_attribute="dummy_value"></mutation>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
});
|
||||
test("Different Mutator", function() {
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <mutation dummy_attribute="dummy_value1"></mutation>' +
|
||||
'<block type="xml_block">' +
|
||||
' <mutation hasInput="true"></mutation>' +
|
||||
'</block>'
|
||||
);
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type">' +
|
||||
' <mutation dummy_attribute="dummy_value2"></mutation>' +
|
||||
'<block type="xml_block">' +
|
||||
' <mutation hasInputt="false"></mutation>' +
|
||||
'</block>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 2);
|
||||
@@ -307,22 +303,10 @@ suite("Trashcan", function() {
|
||||
test("Max 0", function() {
|
||||
this.workspace.options.maxTrashcanContents = 0;
|
||||
fireDeleteEvent(this.workspace,
|
||||
'<block type="dummy_type"/>'
|
||||
'<block type="test_field_block"/>'
|
||||
);
|
||||
chai.assert.equal(this.trashcan.contents_.length, 0);
|
||||
this.workspace.options.maxTrashcanContents = Infinity;
|
||||
});
|
||||
test("Last In First Out", function() {
|
||||
this.workspace.options.maxTrashcanContents = 1;
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type1"/>');
|
||||
fireDeleteEvent(this.workspace, '<block type="dummy_type2"/>');
|
||||
chai.assert.equal(this.trashcan.contents_.length, 1);
|
||||
chai.assert.equal(
|
||||
Blockly.Xml.textToDom(this.trashcan.contents_[0])
|
||||
.getAttribute('type'),
|
||||
'dummy_type2'
|
||||
);
|
||||
this.workspace.options.maxTrashcanContents = Infinity;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user