mirror of
https://github.com/google/blockly.git
synced 2026-01-19 06:47:12 +01:00
Fix collapsed blocks not updating when modified (#3806)
* Stop recursive render calls * Fix visibility bugs. * Make toString ignore collapsed input. * Add automatic collapse rendering handling * Fix insertion markers with collapsed * Add tests * Fix build? * PR comments * Add missing jsdoc
This commit is contained in:
@@ -229,6 +229,17 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
|
||||
*/
|
||||
Blockly.Block.CommentModel;
|
||||
|
||||
/**
|
||||
* The language-neutral id given to the collapsed input.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.Block.COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
|
||||
/**
|
||||
* The language-neutral id given to the collapsed field.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.Block.COLLAPSED_FIELD_NAME = '_TEMP_COLLAPSED_FIELD';
|
||||
|
||||
/**
|
||||
* Optional text data that round-trips between blocks and XML.
|
||||
* Has no effect. May be used by 3rd parties for meta information.
|
||||
@@ -1295,20 +1306,19 @@ Blockly.Block.prototype.setCollapsed = function(collapsed) {
|
||||
Blockly.Block.prototype.toString = function(opt_maxLength, opt_emptyToken) {
|
||||
var text = [];
|
||||
var emptyFieldPlaceholder = opt_emptyToken || '?';
|
||||
if (this.collapsed_) {
|
||||
text.push(this.getInput('_TEMP_COLLAPSED_INPUT').fieldRow[0].getText());
|
||||
} else {
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
text.push(field.getText());
|
||||
}
|
||||
if (input.connection) {
|
||||
var child = input.connection.targetBlock();
|
||||
if (child) {
|
||||
text.push(child.toString(undefined, opt_emptyToken));
|
||||
} else {
|
||||
text.push(emptyFieldPlaceholder);
|
||||
}
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
if (input.name == Blockly.Block.COLLAPSED_INPUT_NAME) {
|
||||
continue;
|
||||
}
|
||||
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
text.push(field.getText());
|
||||
}
|
||||
if (input.connection) {
|
||||
var child = input.connection.targetBlock();
|
||||
if (child) {
|
||||
text.push(child.toString(undefined, opt_emptyToken));
|
||||
} else {
|
||||
text.push(emptyFieldPlaceholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,14 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
||||
|
||||
/** @type {boolean} */
|
||||
this.rendered = false;
|
||||
/**
|
||||
* Is this block currently rendering? Used to stop recursive render calls
|
||||
* from actually triggering a re-render.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.renderIsInProgress_ = false;
|
||||
|
||||
|
||||
/** @type {!Blockly.WorkspaceSvg} */
|
||||
this.workspace = workspace;
|
||||
@@ -602,60 +610,51 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
|
||||
if (this.collapsed_ == collapsed) {
|
||||
return;
|
||||
}
|
||||
var renderList = [];
|
||||
// Show/hide the inputs.
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
renderList.push.apply(renderList, input.setVisible(!collapsed));
|
||||
}
|
||||
|
||||
var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
|
||||
if (collapsed) {
|
||||
var icons = this.getIcons();
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
icons[i].setVisible(false);
|
||||
}
|
||||
var text = this.toString(Blockly.COLLAPSE_CHARS);
|
||||
this.appendDummyInput(COLLAPSED_INPUT_NAME).appendField(text).init();
|
||||
|
||||
// Add any warnings on enclosed blocks to this block.
|
||||
var descendants = this.getDescendants(true);
|
||||
var nextBlock = this.getNextBlock();
|
||||
if (nextBlock) {
|
||||
var index = descendants.indexOf(nextBlock);
|
||||
descendants.splice(index, descendants.length - index);
|
||||
}
|
||||
for (var i = 1, block; (block = descendants[i]); i++) {
|
||||
if (block.warning) {
|
||||
this.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'],
|
||||
Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.removeInput(COLLAPSED_INPUT_NAME);
|
||||
// Clear any warnings inherited from enclosed blocks.
|
||||
if (this.warning) {
|
||||
this.warning.setText('', Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
if (!Object.keys(this.warning.text_).length) {
|
||||
this.setWarningText(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Blockly.BlockSvg.superClass_.setCollapsed.call(this, collapsed);
|
||||
if (!collapsed) {
|
||||
this.updateCollapsed_();
|
||||
} else if (this.rendered) {
|
||||
this.render();
|
||||
// Don't bump neighbours. Users like to store collapsed functions together
|
||||
// and bumping makes them go out of alignment.
|
||||
}
|
||||
};
|
||||
|
||||
if (!renderList.length) {
|
||||
// No child blocks, just render this block.
|
||||
renderList[0] = this;
|
||||
}
|
||||
if (this.rendered) {
|
||||
for (var i = 0, block; (block = renderList[i]); i++) {
|
||||
block.render();
|
||||
/**
|
||||
* Makes sure that when the block is collapsed, it is rendered correctly
|
||||
* for that state.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.updateCollapsed_ = function() {
|
||||
var collapsed = this.isCollapsed();
|
||||
var collapsedInputName = Blockly.Block.COLLAPSED_INPUT_NAME;
|
||||
var collapsedFieldName = Blockly.Block.COLLAPSED_FIELD_NAME;
|
||||
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
if (input.name != collapsedInputName) {
|
||||
input.setVisible(!collapsed);
|
||||
}
|
||||
// Don't bump neighbours.
|
||||
// Although bumping neighbours would make sense, users often collapse
|
||||
// all their functions and store them next to each other. Expanding and
|
||||
// bumping causes all their definitions to go out of alignment.
|
||||
}
|
||||
|
||||
if (!collapsed) {
|
||||
this.removeInput(collapsedInputName);
|
||||
return;
|
||||
}
|
||||
|
||||
var icons = this.getIcons();
|
||||
for (var i = 0, icon; (icon = icons[i]); i++) {
|
||||
icon.setVisible(false);
|
||||
}
|
||||
|
||||
var text = this.toString(Blockly.COLLAPSE_CHARS);
|
||||
var field = this.getField(collapsedFieldName);
|
||||
if (field) {
|
||||
field.setValue(text);
|
||||
return;
|
||||
}
|
||||
var input = this.getInput(collapsedInputName) ||
|
||||
this.appendDummyInput(collapsedInputName);
|
||||
input.appendField(new Blockly.FieldLabel(text), collapsedFieldName);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1647,31 +1646,40 @@ Blockly.BlockSvg.prototype.getRootBlock = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the block.
|
||||
* Lays out and reflows a block based on its contents and settings.
|
||||
* @param {boolean=} opt_bubble If false, just render this block.
|
||||
* If true, also render block's parent, grandparent, etc. Defaults to true.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.render = function(opt_bubble) {
|
||||
Blockly.utils.dom.startTextWidthCache();
|
||||
this.rendered = true;
|
||||
(/** @type {!Blockly.WorkspaceSvg} */ (this.workspace))
|
||||
.getRenderer().render(this);
|
||||
// No matter how we rendered, connection locations should now be correct.
|
||||
this.updateConnectionLocations_();
|
||||
if (opt_bubble !== false) {
|
||||
// Render all blocks above this one (propagate a reflow).
|
||||
var parentBlock = this.getParent();
|
||||
if (parentBlock) {
|
||||
parentBlock.render(true);
|
||||
} else {
|
||||
// Top-most block. Fire an event to allow scrollbars to resize.
|
||||
this.workspace.resizeContents();
|
||||
}
|
||||
if (this.renderIsInProgress_) {
|
||||
return; // Don't allow recursive renders.
|
||||
}
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
this.renderIsInProgress_ = true;
|
||||
try {
|
||||
this.rendered = true;
|
||||
Blockly.utils.dom.startTextWidthCache();
|
||||
|
||||
this.updateMarkers_();
|
||||
if (this.isCollapsed()) {
|
||||
this.updateCollapsed_();
|
||||
}
|
||||
this.workspace.getRenderer().render(this);
|
||||
this.updateConnectionLocations_();
|
||||
|
||||
if (opt_bubble !== false) {
|
||||
var parentBlock = this.getParent();
|
||||
if (parentBlock) {
|
||||
parentBlock.render(true);
|
||||
} else {
|
||||
// Top-most block. Fire an event to allow scrollbars to resize.
|
||||
this.workspace.resizeContents();
|
||||
}
|
||||
}
|
||||
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
this.updateMarkers_();
|
||||
} finally {
|
||||
this.renderIsInProgress_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,21 +91,23 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
|
||||
if (index < 0 || index > this.fieldRow.length) {
|
||||
throw Error('index ' + index + ' out of bounds.');
|
||||
}
|
||||
|
||||
// Falsy field values don't generate a field, unless the field is an empty
|
||||
// string and named.
|
||||
if (!field && !(field == '' && opt_name)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
// Generate a FieldLabel when given a plain text field.
|
||||
if (typeof field == 'string') {
|
||||
field = new Blockly.FieldLabel(/** @type {string} */ (field));
|
||||
}
|
||||
|
||||
field.setSourceBlock(this.sourceBlock_);
|
||||
if (this.sourceBlock_.rendered) {
|
||||
field.init();
|
||||
}
|
||||
field.name = opt_name;
|
||||
field.setVisible(this.isVisible());
|
||||
|
||||
if (field.prefixField) {
|
||||
// Add any prefix.
|
||||
@@ -173,7 +175,6 @@ Blockly.Input.prototype.setVisible = function(visible) {
|
||||
}
|
||||
this.visible_ = visible;
|
||||
|
||||
var display = visible ? 'block' : 'none';
|
||||
for (var y = 0, field; (field = this.fieldRow[y]); y++) {
|
||||
field.setVisible(visible);
|
||||
}
|
||||
@@ -186,10 +187,7 @@ Blockly.Input.prototype.setVisible = function(visible) {
|
||||
}
|
||||
var child = this.connection.targetBlock();
|
||||
if (child) {
|
||||
child.getSvgRoot().style.display = display;
|
||||
if (!visible) {
|
||||
child.rendered = false;
|
||||
}
|
||||
child.getSvgRoot().style.display = visible ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
return renderList;
|
||||
|
||||
@@ -253,23 +253,25 @@ Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlo
|
||||
result.domToMutation(oldMutationDom);
|
||||
}
|
||||
}
|
||||
result.setCollapsed(sourceBlock.isCollapsed());
|
||||
result.setInputsInline(sourceBlock.getInputsInline());
|
||||
// Copy visible field values from the other block. These values may impact
|
||||
// the rendered size of the insertion marker. Note that we do not care
|
||||
// about child blocks here.
|
||||
// Copy field values from the other block. These values may impact the
|
||||
// rendered size of the insertion marker. Note that we do not care about
|
||||
// child blocks here.
|
||||
for (var i = 0; i < sourceBlock.inputList.length; i++) {
|
||||
var sourceInput = sourceBlock.inputList[i];
|
||||
if (sourceInput.isVisible()) {
|
||||
var resultInput = result.inputList[i];
|
||||
for (var j = 0; j < sourceInput.fieldRow.length; j++) {
|
||||
var sourceField = sourceInput.fieldRow[j];
|
||||
var resultField = resultInput.fieldRow[j];
|
||||
resultField.setValue(sourceField.getValue());
|
||||
}
|
||||
if (sourceInput.name == Blockly.Block.COLLAPSED_INPUT_NAME) {
|
||||
continue; // Ignore the collapsed input.
|
||||
}
|
||||
var resultInput = result.inputList[i];
|
||||
for (var j = 0; j < sourceInput.fieldRow.length; j++) {
|
||||
var sourceField = sourceInput.fieldRow[j];
|
||||
var resultField = resultInput.fieldRow[j];
|
||||
resultField.setValue(sourceField.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
result.setCollapsed(sourceBlock.isCollapsed());
|
||||
result.setInputsInline(sourceBlock.getInputsInline());
|
||||
|
||||
result.initSvg();
|
||||
result.getSvgRoot().setAttribute('visibility', 'hidden');
|
||||
} finally {
|
||||
|
||||
@@ -454,6 +454,8 @@ Blockly.RenderedConnection.prototype.disconnectInternal_ = function(parentBlock,
|
||||
if (childBlock.rendered) {
|
||||
childBlock.updateDisabled();
|
||||
childBlock.render();
|
||||
// Reset visibility, since the child is now a top block.
|
||||
childBlock.getSvgRoot().style.display = 'block';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -504,14 +506,16 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
|
||||
var parentConnection = this;
|
||||
var parentBlock = parentConnection.getSourceBlock();
|
||||
var childBlock = childConnection.getSourceBlock();
|
||||
var parentRendered = parentBlock.rendered;
|
||||
var childRendered = childBlock.rendered;
|
||||
|
||||
if (parentBlock.rendered) {
|
||||
if (parentRendered) {
|
||||
parentBlock.updateDisabled();
|
||||
}
|
||||
if (childBlock.rendered) {
|
||||
if (childRendered) {
|
||||
childBlock.updateDisabled();
|
||||
}
|
||||
if (parentBlock.rendered && childBlock.rendered) {
|
||||
if (parentRendered && childRendered) {
|
||||
if (parentConnection.type == Blockly.NEXT_STATEMENT ||
|
||||
parentConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Child block may need to square off its corners if it is in a stack.
|
||||
@@ -523,6 +527,13 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
|
||||
parentBlock.render();
|
||||
}
|
||||
}
|
||||
|
||||
// The input the child block is connected to (if any).
|
||||
var parentInput = parentBlock.getInputWithBlock(childBlock);
|
||||
if (parentInput) {
|
||||
var visible = parentInput.isVisible();
|
||||
childBlock.getSvgRoot().style.display = visible ? 'block' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1209,7 +1209,434 @@ suite('Blocks', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('Collapsing and Expanding', function() {
|
||||
function assertCollapsed(block, opt_string) {
|
||||
chai.assert.isTrue(block.isCollapsed());
|
||||
for (var i = 0, input; (input = block.inputList[i]); i++) {
|
||||
if (input.name == Blockly.Block.COLLAPSED_INPUT_NAME) {
|
||||
continue;
|
||||
}
|
||||
chai.assert.isFalse(input.isVisible());
|
||||
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
chai.assert.isFalse(field.isVisible());
|
||||
}
|
||||
}
|
||||
var icons = block.getIcons();
|
||||
for (var i = 0, icon; (icon = icons[i]); i++) {
|
||||
chai.assert.isFalse(icon.isVisible());
|
||||
}
|
||||
|
||||
var input = block.getInput(Blockly.Block.COLLAPSED_INPUT_NAME);
|
||||
chai.assert.isNotNull(input);
|
||||
chai.assert.isTrue(input.isVisible());
|
||||
var field = block.getField(Blockly.Block.COLLAPSED_FIELD_NAME);
|
||||
chai.assert.isNotNull(field);
|
||||
chai.assert.isTrue(field.isVisible());
|
||||
|
||||
if (opt_string) {
|
||||
chai.assert.equal(field.getText(), opt_string);
|
||||
}
|
||||
}
|
||||
function assertNotCollapsed(block) {
|
||||
chai.assert.isFalse(block.isCollapsed());
|
||||
for (var i = 0, input; (input = block.inputList[i]); i++) {
|
||||
chai.assert.isTrue(input.isVisible());
|
||||
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
chai.assert.isTrue(field.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
var input = block.getInput(Blockly.Block.COLLAPSED_INPUT_NAME);
|
||||
chai.assert.isNull(input);
|
||||
var field = block.getField(Blockly.Block.COLLAPSED_FIELD_NAME);
|
||||
chai.assert.isNull(field);
|
||||
}
|
||||
function isBlockHidden(block) {
|
||||
var node = block.getSvgRoot();
|
||||
do {
|
||||
var visible = node.style.display != 'none';
|
||||
if (!visible) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentNode;
|
||||
} while (node != document);
|
||||
return false;
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
Blockly.Events.disable();
|
||||
// We need a visible workspace.
|
||||
this.workspace = Blockly.inject('blocklyDiv', {});
|
||||
Blockly.defineBlocksWithJsonArray([
|
||||
{
|
||||
"type": "variable_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "NAME",
|
||||
"variable": "x"
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
||||
this.createBlock = function(type) {
|
||||
var block = this.workspace.newBlock(type);
|
||||
block.initSvg();
|
||||
block.render();
|
||||
return block;
|
||||
};
|
||||
});
|
||||
teardown(function() {
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Blocks['variable_block'];
|
||||
});
|
||||
suite('Connecting and Disconnecting', function() {
|
||||
test('Connect Block to Next', function() {
|
||||
var blockA = this.createBlock('stack_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.nextConnection.connect(blockB.previousConnection);
|
||||
assertNotCollapsed(blockB);
|
||||
});
|
||||
test('Connect Block to Value Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
});
|
||||
test('Connect Block to Statement Input', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
});
|
||||
test('Connect Block to Child of Collapsed - Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
var blockC = this.createBlock('row_block');
|
||||
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockB.getInput('INPUT').connection.connect(blockC.outputConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Connect Block to Child of Collapsed - Next', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
var blockC = this.createBlock('stack_block');
|
||||
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockB.nextConnection.connect(blockC.previousConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Connect Block to Value Input Already Taken', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
var blockC = this.createBlock('row_block');
|
||||
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockA.getInput('INPUT').connection.connect(blockC.outputConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
// Still hidden after C is inserted between.
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Connect Block to Statement Input Already Taken', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
var blockC = this.createBlock('stack_block');
|
||||
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockC.previousConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
// Still hidden after C is inserted between.
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Connect Block with Child - Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
var blockC = this.createBlock('row_block');
|
||||
|
||||
blockB.getInput('INPUT').connection.connect(blockC.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Connect Block with Child - Statement', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
var blockC = this.createBlock('stack_block');
|
||||
|
||||
blockB.nextConnection.connect(blockC.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
|
||||
blockA.setCollapsed(false);
|
||||
assertNotCollapsed(blockA);
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Disconnect Block from Value Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockB.outputConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
});
|
||||
test('Disconnect Block from Statement Input', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
blockB.previousConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
});
|
||||
test('Disconnect Block from Child of Collapsed - Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
var blockC = this.createBlock('row_block');
|
||||
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
blockB.getInput('INPUT').connection.connect(blockC.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
|
||||
blockC.outputConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Disconnect Block from Child of Collapsed - Next', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
var blockC = this.createBlock('stack_block');
|
||||
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
blockB.nextConnection.connect(blockC.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
|
||||
blockC.previousConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Disconnect Block with Child - Input', function() {
|
||||
var blockA = this.createBlock('row_block');
|
||||
var blockB = this.createBlock('row_block');
|
||||
var blockC = this.createBlock('row_block');
|
||||
|
||||
blockB.getInput('INPUT').connection.connect(blockC.outputConnection);
|
||||
blockA.getInput('INPUT').connection.connect(blockB.outputConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
|
||||
blockB.outputConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
test('Disconnect Block with Child - Statement', function() {
|
||||
var blockA = this.createBlock('statement_block');
|
||||
var blockB = this.createBlock('stack_block');
|
||||
var blockC = this.createBlock('stack_block');
|
||||
|
||||
blockB.nextConnection.connect(blockC.previousConnection);
|
||||
blockA.getInput('STATEMENT').connection
|
||||
.connect(blockB.previousConnection);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isTrue(isBlockHidden(blockC));
|
||||
chai.assert.isTrue(isBlockHidden(blockB));
|
||||
|
||||
blockB.previousConnection.disconnect();
|
||||
chai.assert.isFalse(isBlockHidden(blockB));
|
||||
chai.assert.isFalse(isBlockHidden(blockC));
|
||||
});
|
||||
});
|
||||
suite('Adding and Removing Block Parts', function() {
|
||||
test('Add Previous Connection', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setPreviousStatement(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNotNull(blockA.previousConnection);
|
||||
});
|
||||
test('Add Next Connection', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setNextStatement(true);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNotNull(blockA.nextConnection);
|
||||
});
|
||||
test('Add Input', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.appendDummyInput('NAME');
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNotNull(blockA.getInput('NAME'));
|
||||
});
|
||||
test('Add Field', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
var input = blockA.appendDummyInput('NAME');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
input.appendField(new Blockly.FieldLabel('test'), 'FIELD');
|
||||
assertCollapsed(blockA);
|
||||
var field = blockA.getField('FIELD');
|
||||
chai.assert.isNotNull(field);
|
||||
chai.assert.equal('test', field.getText());
|
||||
});
|
||||
test('Add Icon', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setCommentText('test');
|
||||
assertCollapsed(blockA);
|
||||
});
|
||||
test('Remove Previous Connection', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setPreviousStatement(true);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setPreviousStatement(false);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNull(blockA.previousConnection);
|
||||
});
|
||||
test('Remove Next Connection', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setNextStatement(true);
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setNextStatement(false);
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNull(blockA.nextConnection);
|
||||
});
|
||||
test('Remove Input', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.appendDummyInput('NAME');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.removeInput('NAME');
|
||||
assertCollapsed(blockA);
|
||||
chai.assert.isNull(blockA.getInput('NAME'));
|
||||
});
|
||||
test('Remove Field', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
var input = blockA.appendDummyInput('NAME');
|
||||
input.appendField(new Blockly.FieldLabel('test'), 'FIELD');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
input.removeField('FIELD');
|
||||
assertCollapsed(blockA);
|
||||
var field = blockA.getField('FIELD');
|
||||
chai.assert.isNull(field);
|
||||
});
|
||||
test('Remove Icon', function() {
|
||||
var blockA = this.createBlock('empty_block');
|
||||
blockA.setCommentText('test');
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA);
|
||||
blockA.setCommentText(null);
|
||||
assertCollapsed(blockA);
|
||||
});
|
||||
});
|
||||
suite('Renaming Vars', function() {
|
||||
test('Simple Rename', function() {
|
||||
var blockA = this.createBlock('variable_block');
|
||||
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA, 'x');
|
||||
|
||||
var variable = this.workspace.getVariable('x', '');
|
||||
this.workspace.renameVariableById(variable.getId(), 'y');
|
||||
assertCollapsed(blockA, 'y');
|
||||
});
|
||||
test('Coalesce, Different Case', function() {
|
||||
var blockA = this.createBlock('variable_block');
|
||||
|
||||
blockA.setCollapsed(true);
|
||||
assertCollapsed(blockA, 'x');
|
||||
|
||||
var variable = this.workspace.createVariable('y');
|
||||
this.workspace.renameVariableById(variable.getId(), 'X');
|
||||
assertCollapsed(blockA, 'X');
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('Style', function() {
|
||||
suite('Headless', function() {
|
||||
setup(function() {
|
||||
|
||||
@@ -40,7 +40,7 @@ async function runMochaTestsInBrowser() {
|
||||
var elem = await browser.$('#failureCount');
|
||||
var text = await elem.getAttribute('tests_failed');
|
||||
return text != 'unset';
|
||||
}, 7000);
|
||||
}, 9000);
|
||||
|
||||
const elem = await browser.$('#failureCount');
|
||||
const numOfFailure = await elem.getAttribute('tests_failed');
|
||||
|
||||
Reference in New Issue
Block a user