Initial commit of 001code-html Scratch frontend project.
Includes scratch-gui, scratch-vm, scratch-blocks, scratch-render, scratch-l10n, and deployment config. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
21
scratch-blocks/tests/blocks/index.html
Normal file
21
scratch-blocks/tests/blocks/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Unit Tests for Blockly Blocks</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script> <!-- TODO: Test messages in all languages. -->
|
||||
<script src="../../blocks/colour.js"></script>
|
||||
<script src="../../blocks/lists.js"></script>
|
||||
<script src="../../blocks/logic.js"></script>
|
||||
<script src="../../blocks/loops.js"></script>
|
||||
<script src="../../blocks/math.js"></script>
|
||||
<script src="../../blocks/procedures.js"></script>
|
||||
<script src="../../blocks/text.js"></script>
|
||||
<script src="../../blocks/variables.js"></script>
|
||||
<script>goog.require('goog.testing.jsunit');</script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="logic_ternary_test.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
316
scratch-blocks/tests/blocks/logic_ternary_test.js
Normal file
316
scratch-blocks/tests/blocks/logic_ternary_test.js
Normal file
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_logic_ternary_structure() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
assertEquals(3, block.inputList && block.inputList.length);
|
||||
assertEquals(1, block.getInput('IF').connection.check_.length);
|
||||
assertEquals('Boolean', block.getInput('IF').connection.check_[0]);
|
||||
assertTrue(!!block.onchangeWrapper_); // Has onchange handler
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachSameTypeCheckInThenAndElseWithoutParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
|
||||
var string1 = workspace.newBlock('text');
|
||||
var string2 = workspace.newBlock('text_charAt');
|
||||
|
||||
block.getInput('THEN').connection.connect(string1.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string1.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(string2.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string1.getRootBlock()); // Still connected.
|
||||
assertEquals(block, string2.getRootBlock());
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachDifferectTypeChecksInThenAndElseWithoutParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachSameTypeCheckInThenAndElseWithMatchingParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var parent = workspace.newBlock('text_trim');
|
||||
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
assertEquals(parent, block.getRootBlock());
|
||||
|
||||
var string1 = workspace.newBlock('text');
|
||||
var string2 = workspace.newBlock('text_charAt');
|
||||
|
||||
block.getInput('THEN').connection.connect(string1.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string1.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(string2.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string1.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(parent, string2.getRootBlock());
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachDifferectTypeChecksInThenAndElseWithUncheckedParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var parent = workspace.newBlock('text_print');
|
||||
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
assertEquals(parent, block.parentBlock_);
|
||||
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(parent, number.getRootBlock());
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachDifferectTypeChecksInThenAndElseWithPermissiveParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var parent = workspace.newBlock('text_length'); // Allows String or Array
|
||||
|
||||
parent.getInput('VALUE').connection.connect(block.outputConnection);
|
||||
assertEquals(parent, block.parentBlock_);
|
||||
|
||||
var string = workspace.newBlock('text');
|
||||
var array = workspace.newBlock('lists_create_empty');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(array.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(parent, array.getRootBlock());
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachMismatchTypeToThen_breakWithParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var parent = workspace.newBlock('text_length'); // Allows String or Array
|
||||
|
||||
parent.getInput('VALUE').connection.connect(block.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.parentBlock_);
|
||||
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('ELSE').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock());
|
||||
|
||||
// Adding mismatching number.
|
||||
block.getInput('THEN').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, block.getRootBlock()); // Disconnected from parent.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
assertEquals(block, string.getRootBlock()); // ELSE string still connected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachMismatchTypeToElse_breakWithParent() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var parent = workspace.newBlock('text_length'); // Allows String or Array
|
||||
|
||||
parent.getInput('VALUE').connection.connect(block.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.parentBlock_);
|
||||
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Still connected to parent.
|
||||
assertEquals(parent, string.getRootBlock());
|
||||
|
||||
// Adding mismatching number.
|
||||
block.getInput('ELSE').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, block.getRootBlock()); // Disconnected from parent.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
assertEquals(block, string.getRootBlock()); // THEN string still connected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachToUncheckedParentWithDifferentTypes() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
|
||||
// Attaching to parent.
|
||||
var parent = workspace.newBlock('text_print');
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
assertEquals(parent, block.getRootBlock());
|
||||
assertEquals(parent, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(parent, number.getRootBlock()); // Input ELSE still connected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachToPermissiveParentWithDifferentTypes() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var string = workspace.newBlock('text');
|
||||
var array = workspace.newBlock('lists_create_empty');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(array.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(block, array.getRootBlock());
|
||||
|
||||
// Attaching to parent.
|
||||
var parent = workspace.newBlock('text_print');
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock());
|
||||
assertEquals(parent, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(parent, array.getRootBlock()); // Input ELSE still connected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachToParentWithMismatchingThen_disconnectThen() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var number = workspace.newBlock('math_number');
|
||||
var string = workspace.newBlock('text');
|
||||
|
||||
block.getInput('THEN').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, number.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(block, string.getRootBlock());
|
||||
|
||||
// Attaching to parent.
|
||||
var parent = workspace.newBlock('text_trim');
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Successful connection to parent.
|
||||
assertEquals(parent, string.getRootBlock()); // Input ELSE still connected.
|
||||
assertEquals(number, number.getRootBlock()); // Input THEN disconnected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_logic_ternary_attachToParentWithMismatchingElse_disconnectElse() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var block = workspace.newBlock('logic_ternary');
|
||||
var string = workspace.newBlock('text');
|
||||
var number = workspace.newBlock('math_number');
|
||||
|
||||
block.getInput('THEN').connection.connect(string.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock());
|
||||
block.getInput('ELSE').connection.connect(number.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(block, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(block, number.getRootBlock());
|
||||
|
||||
// Attaching to parent.
|
||||
var parent = workspace.newBlock('text_trim');
|
||||
parent.getInput('TEXT').connection.connect(block.outputConnection);
|
||||
Blockly.Events.fireNow_(); // Force synchronous onchange() call.
|
||||
assertEquals(parent, block.getRootBlock()); // Successful connection to parent.
|
||||
assertEquals(parent, string.getRootBlock()); // Input THEN still connected.
|
||||
assertEquals(number, number.getRootBlock()); // Input ELSE disconnected.
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
239
scratch-blocks/tests/custom_procedure_playground.html
Normal file
239
scratch-blocks/tests/custom_procedure_playground.html
Normal file
@@ -0,0 +1,239 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>Custom Procedure Playground</title>
|
||||
<script src="../blockly_uncompressed_vertical.js"></script>
|
||||
<script src="../msg/messages.js"></script>
|
||||
<script src="../blocks_vertical/vertical_extensions.js"></script>
|
||||
<script src="../blocks_common/math.js"></script>
|
||||
<script src="../blocks_common/text.js"></script>
|
||||
<script src="../blocks_common/colour.js"></script>
|
||||
<script src="../blocks_vertical/control.js"></script>
|
||||
<script src="../blocks_vertical/event.js"></script>
|
||||
<script src="../blocks_vertical/motion.js"></script>
|
||||
<script src="../blocks_vertical/looks.js"></script>
|
||||
<script src="../blocks_vertical/procedures.js"></script>
|
||||
<script src="../blocks_vertical/operators.js"></script>
|
||||
<script src="../blocks_vertical/pen.js"></script>
|
||||
<script src="../blocks_vertical/sound.js"></script>
|
||||
<script src="../blocks_vertical/sensing.js"></script>
|
||||
<script src="../blocks_vertical/data.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<div id="secondaryDiv" style="height: 480px; width: 600px;"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="primaryDiv" style="height: 480px; width: 600px;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td id="editor-actions">
|
||||
<button id="text_number" onclick="addTextNumber()">Add text/number input</button>
|
||||
<button id="boolean" onclick="addBoolean()">Add boolean input</button>
|
||||
<button id="label" onclick="addLabel()">Add label</button>
|
||||
<button id="cancelButton" onclick="cancel()">cancel</button>
|
||||
<button id="okButton" onclick="applyMutation()">ok</button>
|
||||
<button id="rndButton" onclick="removeRandomInput()">remove random input</button>
|
||||
<button id="addRndButton" onclick="addRandomInput()">add random input</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<xml id="mutator_blocks" style="display:none">
|
||||
<block type="procedures_declaration" x="25" y="25" deletable="false">
|
||||
<mutation proccode="say %s %n times if %b" argumentnames="["something","this many","this is true"]" argumentdefaults="["",1,false]" warp="false" argumentids="["a42", "b23", "c99"]"/>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="main_ws_blocks" style="display:none">
|
||||
<block id="]){{Y!7N9ezN+j@Vr`8p" type="procedures_definition" x="25" y="25">
|
||||
<statement name="custom_block">
|
||||
<shadow type="procedures_prototype" id="caller_internal">
|
||||
<mutation proccode="say %s %n times if %b" argumentnames="["something","this many","this is true"]" argumentdefaults="["",1,false]" warp="false" argumentids="["a42", "b23", "c99"]"/>
|
||||
</shadow>
|
||||
</statement>
|
||||
<next>
|
||||
<block id="caller_external" type="procedures_call">
|
||||
<mutation proccode="say %s %n times if %b" argumentnames="["something","this many","this is true"]" argumentdefaults="["",1,false]" warp="false" argumentids="["a42", "b23", "c99"]"/>
|
||||
<value name="input0">
|
||||
<shadow id="V0~M:TxRk0Ua%osjGzh," type="text">
|
||||
<field name="TEXT"/></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="input1">
|
||||
<shadow id="uPx3si(KkbL1)-XpbXoQ" type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="main_ws_blocks_simpler" style="display:none">
|
||||
<block id="]){{Y!7N9ezN+j@Vr`8p" type="procedures_definition" x="25" y="25">
|
||||
<statement name="custom_block">
|
||||
<shadow type="procedures_prototype" id="caller_internal">
|
||||
<mutation proccode="say %s %n times if %b" argumentnames="["something","this many","this is true"]" argumentdefaults="["",1,false]" warp="false" argumentids="["a42", "b23", "c99"]"/>
|
||||
</shadow>
|
||||
</statement>
|
||||
<next>
|
||||
<block id="caller_external" type="procedures_call">
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="main_ws_blocks_simplest" style="display:none">
|
||||
<block id="]){{Y!7N9ezN+j@Vr`8p" type="procedures_definition" x="25" y="25">
|
||||
<statement name="custom_block">
|
||||
<shadow type="procedures_prototype" id="caller_internal">
|
||||
<mutation proccode="my first function" argumentnames="[]" argumentdefaults="[]" warp="false" argumentids="[]"/>
|
||||
</shadow>
|
||||
</statement>
|
||||
<next>
|
||||
<block id="caller_external" type="procedures_call">
|
||||
<mutation proccode="my first function" argumentnames="[]" argumentdefaults="[]" warp="false" argumentids="[]"/>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="mutator_blocks_simplest" style="display:none">
|
||||
<block type="procedures_declaration" x="25" y="25" deletable="false">
|
||||
<mutation proccode="my first function" argumentnames="[]" argumentdefaults="[]" warp="false" argumentids="[]"/>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="toolbox" style="display:none">
|
||||
<category name="More" colour="#FF6680" secondaryColour="#FF4D6A" custom="PROCEDURE">
|
||||
</category>
|
||||
<category name="Values">
|
||||
<block type="operator_round">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Statements">
|
||||
<block type="control_forever"></block>
|
||||
<block type="sound_stopallsounds"></block>
|
||||
<block type="control_start_as_clone"></block>
|
||||
<block type="control_stop">
|
||||
<mutation hasnext="false"></mutation>
|
||||
<field name="STOP_OPTION">all</field>
|
||||
</block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var editorActions = document.getElementById('editor-actions');
|
||||
editorActions.style.visibility = 'hidden';
|
||||
|
||||
var callback = null;
|
||||
var mutationRoot = null;
|
||||
|
||||
var declarationWorkspace = Blockly.inject('primaryDiv',
|
||||
{media: '../media/'});
|
||||
|
||||
declarationWorkspace.addChangeListener(function() {
|
||||
if (mutationRoot) {
|
||||
mutationRoot.onChangeFn();
|
||||
}
|
||||
});
|
||||
|
||||
var definitionWorkspace = Blockly.inject('secondaryDiv', {
|
||||
media: '../media/',
|
||||
toolbox: document.getElementById('toolbox'),
|
||||
zoom: {
|
||||
startScale: 0.75
|
||||
}
|
||||
});
|
||||
|
||||
Blockly.Xml.clearWorkspaceAndLoadFromXml(document.getElementById('main_ws_blocks_simplest'),
|
||||
definitionWorkspace);
|
||||
|
||||
Blockly.Procedures.externalProcedureDefCallback = function (mutation, cb) {
|
||||
editorActions.style.visibility = 'visible';
|
||||
callback = cb;
|
||||
declarationWorkspace.clear();
|
||||
mutationRoot = declarationWorkspace.newBlock('procedures_declaration');
|
||||
mutationRoot.domToMutation(mutation);
|
||||
mutationRoot.initSvg();
|
||||
mutationRoot.render(false);
|
||||
}
|
||||
|
||||
function applyMutation() {
|
||||
var mutation = mutationRoot.mutationToDom(/* opt_generateShadows */ true)
|
||||
console.log(mutation);
|
||||
callback(mutation);
|
||||
callback = null;
|
||||
mutationRoot = null;
|
||||
declarationWorkspace.clear();
|
||||
definitionWorkspace.refreshToolboxSelection_()
|
||||
editorActions.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
function addLabel() {
|
||||
mutationRoot.addLabelExternal();
|
||||
}
|
||||
|
||||
function addBoolean() {
|
||||
mutationRoot.addBooleanExternal();
|
||||
}
|
||||
|
||||
function addTextNumber() {
|
||||
mutationRoot.addStringNumberExternal();
|
||||
}
|
||||
|
||||
function removeRandomInput() {
|
||||
var rnd = Math.floor(Math.random() * mutationRoot.inputList.length);
|
||||
mutationRoot.removeInput(mutationRoot.inputList[rnd].name);
|
||||
mutationRoot.onChangeFn();
|
||||
mutationRoot.updateDisplay_();
|
||||
}
|
||||
|
||||
function addRandomInput() {
|
||||
var rnd = Math.floor(Math.random() * 3);
|
||||
switch (rnd) {
|
||||
case 0:
|
||||
addTextNumber();
|
||||
break;
|
||||
case 1:
|
||||
addLabel();
|
||||
break;
|
||||
case 2:
|
||||
addBoolean();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
callback = null;
|
||||
mutationRoot = null;
|
||||
declarationWorkspace.clear();
|
||||
definitionWorkspace.refreshToolboxSelection_()
|
||||
editorActions.style.visibility = 'hidden';
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
421
scratch-blocks/tests/horizontal_playground.html
Normal file
421
scratch-blocks/tests/horizontal_playground.html
Normal file
@@ -0,0 +1,421 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<title>Horizontal Playground</title>
|
||||
|
||||
<script src="../blockly_uncompressed_horizontal.js"></script>
|
||||
<script src="../msg/messages.js"></script>
|
||||
<script src="../blocks_common/math.js"></script>
|
||||
<script src="../blocks_common/text.js"></script>
|
||||
<script src="../blocks_horizontal/control.js"></script>
|
||||
<script src="../blocks_horizontal/event.js"></script>
|
||||
<script src="../blocks_horizontal/wedo.js"></script>
|
||||
<script src="../blocks_horizontal/default_toolbox.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
var fakeDragStack = [];
|
||||
var workspace = null;
|
||||
|
||||
function start() {
|
||||
var soundsEnabled = null;
|
||||
if (sessionStorage) {
|
||||
// Restore sounds state.
|
||||
soundsEnabled = sessionStorage.getItem('soundsEnabled');
|
||||
if (soundsEnabled === null) {
|
||||
soundsEnabled = true;
|
||||
} else {
|
||||
soundsEnabled = (soundsEnabled === 'true');
|
||||
}
|
||||
} else {
|
||||
soundsEnabled = true;
|
||||
}
|
||||
setSoundsEnabled(soundsEnabled);
|
||||
|
||||
// Setup blocks
|
||||
// Parse the URL arguments.
|
||||
var match = location.search.match(/dir=([^&]+)/);
|
||||
var rtl = match && match[1] == 'rtl';
|
||||
document.forms.options.elements.dir.selectedIndex = Number(rtl);
|
||||
var toolbox = getToolboxElement();
|
||||
document.forms.options.elements.toolbox.selectedIndex =
|
||||
toolbox ? 1: 0;
|
||||
|
||||
match = location.search.match(/side=([^&]+)/);
|
||||
|
||||
var side = match ? match[1] : 'start';
|
||||
|
||||
document.forms.options.elements.side.value = side;
|
||||
|
||||
workspace = Blockly.inject('blocklyDiv', {
|
||||
comments: false,
|
||||
disable: false,
|
||||
collapse: false,
|
||||
media: '../media/',
|
||||
readOnly: false,
|
||||
rtl: rtl,
|
||||
scrollbars: true,
|
||||
toolbox: toolbox,
|
||||
trashcan: true,
|
||||
horizontalLayout: side == 'top' || side == 'bottom',
|
||||
toolboxPosition: side == 'top' || side == 'start' ? 'start' : 'end',
|
||||
sounds: soundsEnabled,
|
||||
grid: {spacing: 16,
|
||||
length: 1,
|
||||
colour: '#2C344A',
|
||||
snap: false
|
||||
},
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: true,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
},
|
||||
colours: {
|
||||
fieldShadow: 'rgba(255, 255, 255, 0.3)',
|
||||
dragShadowOpacity: 0.6
|
||||
}
|
||||
});
|
||||
|
||||
if (sessionStorage) {
|
||||
// Restore previously displayed text.
|
||||
var text = sessionStorage.getItem('textarea');
|
||||
if (text) {
|
||||
document.getElementById('importExport').value = text;
|
||||
}
|
||||
taChange();
|
||||
|
||||
// Restore event logging state.
|
||||
var state = sessionStorage.getItem('logEvents');
|
||||
logEvents(Boolean(state));
|
||||
|
||||
// Restore flyout event logging state.
|
||||
state = sessionStorage.getItem('logFlyoutEvents');
|
||||
logFlyoutEvents(Boolean(state));
|
||||
}
|
||||
}
|
||||
|
||||
function getToolboxElement() {
|
||||
var match = location.search.match(/toolbox=([^&]+)/);
|
||||
return document.getElementById('toolbox-' + (match ? match[1] : 'categories'));
|
||||
}
|
||||
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
taChange();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(workspace, xml);
|
||||
taChange();
|
||||
}
|
||||
|
||||
// Disable the "Import from XML" button if the XML is invalid.
|
||||
// Preserve text between page reloads.
|
||||
function taChange() {
|
||||
var textarea = document.getElementById('importExport');
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('textarea', textarea.value);
|
||||
}
|
||||
var valid = true;
|
||||
try {
|
||||
Blockly.Xml.textToDom(textarea.value);
|
||||
} catch (e) {
|
||||
valid = false;
|
||||
}
|
||||
document.getElementById('import').disabled = !valid;
|
||||
}
|
||||
|
||||
function logEvents(state) {
|
||||
var checkbox = document.getElementById('logCheck');
|
||||
checkbox.checked = state;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logEvents', state ? 'checked' : '');
|
||||
}
|
||||
if (state) {
|
||||
workspace.addChangeListener(logger);
|
||||
} else {
|
||||
workspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logFlyoutEvents(state) {
|
||||
var checkbox = document.getElementById('logFlyoutCheck');
|
||||
checkbox.checked = state;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logFlyoutEvents', state ? 'checked' : '');
|
||||
}
|
||||
var flyoutWorkspace = (workspace.flyout_) ? workspace.flyout_.workspace_ :
|
||||
workspace.toolbox_.flyout_.workspace_;
|
||||
if (state) {
|
||||
flyoutWorkspace.addChangeListener(logger);
|
||||
} else {
|
||||
flyoutWorkspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logger(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function glowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function glowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function sprinkles(n) {
|
||||
var prototypes = [];
|
||||
var toolbox = workspace.options.languageTree;
|
||||
if (!toolbox) {
|
||||
console.error('Toolbox not found; add a toolbox element to the DOM.');
|
||||
return;
|
||||
}
|
||||
var blocks = toolbox.getElementsByTagName('block');
|
||||
for (var i = 0; i < n; i++) {
|
||||
var blockXML = blocks[Math.floor(Math.random() * blocks.length)];
|
||||
var block = Blockly.Xml.domToBlock(blockXML, workspace);
|
||||
block.initSvg();
|
||||
block.moveBy(
|
||||
Math.round(Math.random() * 450 + 40),
|
||||
Math.round(Math.random() * 600 + 40)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function spaghetti(n) {
|
||||
var xml = spaghettiXml;
|
||||
for(var i = 0; i < n; i++) {
|
||||
xml = xml.replace(/(<(statement|next)( name="SUBSTACK")?>)<\//g,
|
||||
'$1' + spaghettiXml + '</');
|
||||
}
|
||||
xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + xml + '</xml>';
|
||||
var dom = Blockly.Xml.textToDom(xml);
|
||||
console.time('Spaghetti domToWorkspace');
|
||||
Blockly.Xml.domToWorkspace(workspace, dom);
|
||||
console.timeEnd('Spaghetti domToWorkspace');
|
||||
}
|
||||
|
||||
var spaghettiXml = [
|
||||
' <block type="control_repeat">',
|
||||
' <value name="TIMES">',
|
||||
' <shadow type="math_whole_number">',
|
||||
' <field name="NUM">10</field>',
|
||||
' </shadow>',
|
||||
' </value>',
|
||||
' <statement name="SUBSTACK"></statement>',
|
||||
' <next></next>',
|
||||
' </block>'
|
||||
].join('\n');
|
||||
|
||||
function setSoundsEnabled(state) {
|
||||
var checkbox = document.getElementById('soundsEnabled');
|
||||
checkbox.checked = (state) ? 'checked' : '';
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('soundsEnabled', state);
|
||||
}
|
||||
}
|
||||
function fakeDrag(id, dx, dy, opt_workspace) {
|
||||
var ws = opt_workspace || Blockly.getMainWorkspace();
|
||||
var blockToDrag = ws.getBlockById(id);
|
||||
|
||||
if (!blockToDrag) {
|
||||
fakeDragWrapper();
|
||||
return;
|
||||
}
|
||||
var blockTop = blockToDrag.svgGroup_.getBoundingClientRect().top;
|
||||
var blockLeft = blockToDrag.svgGroup_.getBoundingClientRect().left;
|
||||
|
||||
// Click somewhere on the block.
|
||||
var mouseDownEvent = new MouseEvent('mousedown',
|
||||
{clientX: blockLeft + 5, clientY: blockTop + 5});
|
||||
blockToDrag.onMouseDown_(mouseDownEvent);
|
||||
|
||||
// Throw in a move for good measure.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseMoveEvent = new MouseEvent('mousemove',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseMove_(mouseMoveEvent);
|
||||
|
||||
// Drop at dx, dy.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseUpEvent = new MouseEvent('mouseup',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseUp_(mouseUpEvent);
|
||||
|
||||
setTimeout(fakeDragWrapper(), 100);
|
||||
}, 30);
|
||||
}, 30);
|
||||
};
|
||||
|
||||
function fakeDragWrapper() {
|
||||
var dragInfo = fakeDragStack.pop();
|
||||
if (dragInfo) {
|
||||
fakeDrag(dragInfo.id, dragInfo.dx, dragInfo.dy, dragInfo.workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function fakeManyDrags() {
|
||||
var blockList = workspace.getAllBlocks();
|
||||
for (var i = 0; i < 2 * blockList.length; i++) {
|
||||
fakeDragStack.push(
|
||||
{
|
||||
id: blockList[Math.round(Math.random() * (blockList.length - 1))].id,
|
||||
// Move some blocks up and to the left, but mostly down and to the right.
|
||||
dx: Math.round((Math.random() - 0.25) * 200),
|
||||
dy: Math.round((Math.random() - 0.25) * 200),
|
||||
workspace: workspace
|
||||
});
|
||||
}
|
||||
fakeDragWrapper();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
#blocklyDiv {
|
||||
float: right;
|
||||
height: 95%;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#collaborators {
|
||||
float: right;
|
||||
width: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#collaborators > img {
|
||||
margin-right: 5px;
|
||||
height: 30px;
|
||||
padding-bottom: 5px;
|
||||
width: 30px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#importExport {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="start()">
|
||||
<div id="collaborators"></div>
|
||||
<div id="blocklyDiv"></div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<h1>Horizontal Blocks</h1>
|
||||
<p>
|
||||
<a href="javascript:void(workspace.setVisible(true))">Show</a>
|
||||
- <a href="javascript:void(workspace.setVisible(false))">Hide</a>
|
||||
</p>
|
||||
|
||||
<form id="options">
|
||||
<select name="dir" onchange="document.forms.options.submit()">
|
||||
<option value="ltr">LTR</option>
|
||||
<option value="rtl">RTL</option>
|
||||
</select>
|
||||
<select name="toolbox" onchange="document.forms.options.submit()">
|
||||
<option value="categories">Categories</option>
|
||||
<option value="simple">Simple</option>
|
||||
</select>
|
||||
<select name="side" onchange="document.forms.options.submit()">
|
||||
<option value="start">Start</option>
|
||||
<option value="end">End</option>
|
||||
<option value="top">Top</option>
|
||||
<option value="bottom">Bottom</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
|
||||
<input type="button" value="Import from XML" onclick="fromXml()" id="import">
|
||||
<br>
|
||||
<textarea id="importExport" style="width: 26%; height: 12em"
|
||||
onchange="taChange();" onkeyup="taChange()"></textarea>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Log events:
|
||||
<input type="checkbox" onclick="logEvents(this.checked)" id="logCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Log flyout events:
|
||||
<input type="checkbox" onclick="logFlyoutEvents(this.checked)" id="logFlyoutCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Enable sounds (after refresh):
|
||||
<input type="checkbox" onclick="setSoundsEnabled(this.checked)" id="soundsEnabled">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Stress test:
|
||||
<input type="button" value="Sprinkles!" onclick="sprinkles(100)">
|
||||
<input type="button" value="Spaghetti!" onclick="spaghetti(10)">
|
||||
<input type="button" value="Fake some drags!" onclick="fakeManyDrags()">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Glows:
|
||||
<input type="button" value="Glow last clicked block" onclick="glowBlock()" />
|
||||
<input type="button" value="Unglow last clicked block" onclick="unglowBlock()" />
|
||||
<input type="button" value="Stack glow last clicked block" onclick="glowStack()" />
|
||||
<input type="button" value="Stack unglow last clicked block" onclick="unglowStack()" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Undo" onclick="workspace.undo()" />
|
||||
<input type="button" value="Redo" onclick="workspace.undo(true)" />
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
106
scratch-blocks/tests/jsunit/block_test.js
Normal file
106
scratch-blocks/tests/jsunit/block_test.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_appendField_FieldIconMenu() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block_name = 'test_jsonInit_FieldIconMenu';
|
||||
var field_name = 'TEST_FIELD';
|
||||
var dropdown_options = [{
|
||||
value: 'VALUE'
|
||||
}];
|
||||
|
||||
Blockly.Blocks[block_name] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldIconMenu(dropdown_options),
|
||||
field_name);
|
||||
this.setOutput(true);
|
||||
}
|
||||
};
|
||||
|
||||
var block = workspace.newBlock(block_name);
|
||||
assertTrue('IconMenu field not added to block by appendField',
|
||||
block.getField(field_name) instanceof Blockly.FieldIconMenu);
|
||||
}
|
||||
|
||||
function test_jsonInit_FieldIconMenu() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block_name = 'test_jsonInit_FieldIconMenu';
|
||||
var field_name = 'TEST_FIELD';
|
||||
var dropdown_options = [{
|
||||
value: 'VALUE'
|
||||
}];
|
||||
|
||||
Blockly.Blocks[block_name] = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
message0: '%1',
|
||||
args0: [{
|
||||
type: 'field_iconmenu',
|
||||
name: field_name,
|
||||
options: dropdown_options
|
||||
}],
|
||||
output: null
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var block = workspace.newBlock(block_name);
|
||||
assertTrue('IconMenu field not added to block by jsonInit',
|
||||
block.getField(field_name) instanceof Blockly.FieldIconMenu);
|
||||
}
|
||||
|
||||
function test_jsonInit_colors() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block_name = 'test_jsonInit_FieldDropdown_colors';
|
||||
var field_name = 'TEST_FIELD';
|
||||
var dropdown_options = [
|
||||
['value', 'VALUE']
|
||||
];
|
||||
|
||||
Blockly.Blocks[block_name] = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
message0: '%1',
|
||||
args0: [{
|
||||
type: 'field_dropdown',
|
||||
name: field_name,
|
||||
options: dropdown_options
|
||||
}],
|
||||
output: null,
|
||||
colour: '#111111',
|
||||
colourSecondary: '#222222',
|
||||
colourTertiary: '#333333',
|
||||
colourQuaternary: '#444444'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var block = workspace.newBlock(block_name);
|
||||
var field = block.getField(field_name);
|
||||
|
||||
assertEquals('Block primary colour not set', block.getColour(), '#111111');
|
||||
assertEquals('Block secondary colour not set', block.getColourSecondary(), '#222222');
|
||||
assertEquals('Block tertiary colour not set', block.getColourTertiary(), '#333333');
|
||||
assertEquals('Block quaternary colour not set', block.getColourQuaternary(), '#444444');
|
||||
|
||||
assertEquals('Source block is not correct', field.sourceBlock_, block);
|
||||
}
|
||||
310
scratch-blocks/tests/jsunit/connection_db_test.js
Normal file
310
scratch-blocks/tests/jsunit/connection_db_test.js
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2015 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function verify_DB_(msg, expected, db) {
|
||||
var equal = (expected.length == db.connections_.length);
|
||||
if (equal) {
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
if (expected[i] != db.connections_[i]) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (equal) {
|
||||
assertTrue(msg, true);
|
||||
} else {
|
||||
assertEquals(msg, expected, db.connections_);
|
||||
}
|
||||
}
|
||||
|
||||
function test_DB_addConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var o2 = {y_: 2, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o2);
|
||||
verify_DB_('Adding connection #2', [o2], db);
|
||||
|
||||
var o4 = {y_: 4, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connection #4', [o2, o4], db);
|
||||
|
||||
var o1 = {y_: 1, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o1);
|
||||
verify_DB_('Adding connection #1', [o1, o2, o4], db);
|
||||
|
||||
var o3a = {y_: 3, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o3a);
|
||||
verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db);
|
||||
|
||||
var o3b = {y_: 3, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o3b);
|
||||
verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db);
|
||||
}
|
||||
|
||||
function test_DB_removeConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var o1 = {y_: 1, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
var o2 = {y_: 2, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
var o3a = {y_: 3, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
var o3b = {y_: 3, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
var o3c = {y_: 3, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
var o4 = {y_: 4, sourceBlock_: {},
|
||||
getSourceBlock: Blockly.Connection.prototype.getSourceBlock};
|
||||
db.addConnection(o1);
|
||||
db.addConnection(o2);
|
||||
db.addConnection(o3c);
|
||||
db.addConnection(o3b);
|
||||
db.addConnection(o3a);
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db);
|
||||
|
||||
db.removeConnection_(o2);
|
||||
verify_DB_('Removing connection #2', [o1, o3a, o3b, o3c, o4], db);
|
||||
|
||||
db.removeConnection_(o4);
|
||||
verify_DB_('Removing connection #4', [o1, o3a, o3b, o3c], db);
|
||||
|
||||
db.removeConnection_(o1);
|
||||
verify_DB_('Removing connection #1', [o3a, o3b, o3c], db);
|
||||
|
||||
db.removeConnection_(o3a);
|
||||
verify_DB_('Removing connection #3a', [o3b, o3c], db);
|
||||
|
||||
db.removeConnection_(o3c);
|
||||
verify_DB_('Removing connection #3c', [o3b], db);
|
||||
|
||||
db.removeConnection_(o3b);
|
||||
verify_DB_('Removing connection #3b', [], db);
|
||||
}
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db,
|
||||
10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT, null, true));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning.
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db.connections_[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle.
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db.connections_[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end.
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db.connections_[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction.
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction.
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally.
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function test_DB_findPositionForConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
db.addConnection(helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT,
|
||||
null, true));
|
||||
db.addConnection(helper_createConnection(0, 1, Blockly.PREVIOUS_STATEMENT,
|
||||
null, true));
|
||||
db.addConnection(helper_createConnection(0, 2, Blockly.PREVIOUS_STATEMENT,
|
||||
null, true));
|
||||
db.addConnection(helper_createConnection(0, 4, Blockly.PREVIOUS_STATEMENT,
|
||||
null, true));
|
||||
db.addConnection(helper_createConnection(0, 5, Blockly.PREVIOUS_STATEMENT,
|
||||
null, true));
|
||||
|
||||
assertEquals(5, db.connections_.length);
|
||||
var conn = helper_createConnection(0, 3, Blockly.PREVIOUS_STATEMENT, null,
|
||||
true);
|
||||
assertEquals(3, db.findPositionForConnection_(conn));
|
||||
}
|
||||
|
||||
function test_DB_findConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(i, 0,
|
||||
Blockly.PREVIOUS_STATEMENT, null, true));
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT, null, true));
|
||||
}
|
||||
|
||||
var conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT, null,
|
||||
true);
|
||||
db.addConnection(conn);
|
||||
assertEquals(conn, db.connections_[db.findConnection(conn)]);
|
||||
|
||||
conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT, null, true);
|
||||
assertEquals(-1, db.findConnection(conn));
|
||||
}
|
||||
|
||||
function test_DB_ordering() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, 9 - i,
|
||||
Blockly.PREVIOUS_STATEMENT), null, true);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
assertEquals(i, db.connections_[i].y_);
|
||||
}
|
||||
|
||||
// quasi-random
|
||||
var xCoords = [-29, -47, -77, 2, 43, 34, -59, -52, -90, -36, -91, 38, 87, -20,
|
||||
60, 4, -57, 65, -37, -81, 57, 58, -96, 1, 67, -79, 34, 93, -90, -99, -62,
|
||||
4, 11, -36, -51, -72, 3, -50, -24, -45, -92, -38, 37, 24, -47, -73, 79,
|
||||
-20, 99, 43, -10, -87, 19, 35, -62, -36, 49, 86, -24, -47, -89, 33, -44,
|
||||
25, -73, -91, 85, 6, 0, 89, -94, 36, -35, 84, -9, 96, -21, 52, 10, -95, 7,
|
||||
-67, -70, 62, 9, -40, -95, -9, -94, 55, 57, -96, 55, 8, -48, -57, -87, 81,
|
||||
23, 65];
|
||||
var yCoords = [-81, 82, 5, 47, 30, 57, -12, 28, 38, 92, -25, -20, 23, -51, 73,
|
||||
-90, 8, 28, -51, -15, 81, -60, -6, -16, 77, -62, -42, -24, 35, 95, -46,
|
||||
-7, 61, -16, 14, 91, 57, -38, 27, -39, 92, 47, -98, 11, -33, -72, 64, 38,
|
||||
-64, -88, -35, -59, -76, -94, 45, -25, -100, -95, 63, -97, 45, 98, 99, 34,
|
||||
27, 52, -18, -45, 66, -32, -38, 70, -73, -23, 5, -2, -13, -9, 48, 74, -97,
|
||||
-11, 35, -79, -16, -77, 83, -57, -53, 35, -44, 100, -27, -15, 5, 39, 33,
|
||||
-19, -20, -95];
|
||||
for (i = 0; i < xCoords.length; i++) {
|
||||
db.addConnection(helper_createConnection(xCoords[i], yCoords[i],
|
||||
Blockly.PREVIOUS_STATEMENT), null, true);
|
||||
}
|
||||
|
||||
for (i = 1; i < xCoords.length; i++) {
|
||||
assertTrue(db.connections_[i].y_ >= db.connections_[i - 1].y_);
|
||||
}
|
||||
}
|
||||
|
||||
function test_SearchForClosest() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var sharedWorkspace = {id: "Shared workspace"};
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(null, helper_searchDB(db, 10 /* x */, 10 /* y */,
|
||||
100 /* radius */));
|
||||
|
||||
db.addConnection(helper_createConnection(100, 0, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace, true));
|
||||
assertEquals(null, helper_searchDB(db, 0, 0, 5, sharedWorkspace));
|
||||
|
||||
db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var tempConn = helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace, true);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
}
|
||||
|
||||
// Should be at 0, 9.
|
||||
var last = db.connections_[db.connections_.length - 1];
|
||||
// Correct connection is last in db; many connections in radius.
|
||||
assertEquals(last, helper_searchDB(db, 0, 10, 15, sharedWorkspace));
|
||||
// Nothing nearby.
|
||||
assertEquals(null, helper_searchDB(db, 100, 100, 3, sharedWorkspace));
|
||||
// First in db, exact match.
|
||||
assertEquals(db.connections_[0], helper_searchDB(db, 0, 0, 0, sharedWorkspace));
|
||||
|
||||
tempConn = helper_createConnection(6, 6, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace, true);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
tempConn = helper_createConnection(5, 5, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace, true);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
|
||||
var result = helper_searchDB(db, 4, 6, 3, sharedWorkspace);
|
||||
assertEquals(5, result.x_);
|
||||
assertEquals(5, result.y_);
|
||||
}
|
||||
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT,
|
||||
null, true),
|
||||
radius);
|
||||
}
|
||||
|
||||
function helper_searchDB(db, x, y, radius, shared_workspace) {
|
||||
var tempConn = helper_createConnection(x, y,
|
||||
Blockly.NEXT_STATEMENT, shared_workspace, true);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(shared_workspace);
|
||||
tempConn.sourceBlock_.nextConnection = tempConn;
|
||||
var closest = db.searchForClosest(tempConn, radius, {x: 0, y: 0});
|
||||
return closest.connection;
|
||||
}
|
||||
|
||||
function helper_makeSourceBlock(sharedWorkspace) {
|
||||
return {workspace: sharedWorkspace,
|
||||
parentBlock_: null,
|
||||
getParent: function() { return null; },
|
||||
movable_: true,
|
||||
isMovable: function() { return true; },
|
||||
isShadow: function() { return false; },
|
||||
isInsertionMarker: function() { return false; },
|
||||
getFirstStatementConnection: function() { return null; }
|
||||
};
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type, opt_shared_workspace,
|
||||
opt_rendered) {
|
||||
var workspace = opt_shared_workspace ? opt_shared_workspace : {};
|
||||
if (opt_rendered) {
|
||||
var conn = new Blockly.RenderedConnection({workspace: workspace}, type);
|
||||
} else {
|
||||
var conn = new Blockly.Connection({workspace: workspace}, type);
|
||||
}
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
||||
404
scratch-blocks/tests/jsunit/connection_test.js
Normal file
404
scratch-blocks/tests/jsunit/connection_test.js
Normal file
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for connection logic.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var input;
|
||||
var output;
|
||||
var previous;
|
||||
var next;
|
||||
|
||||
var dummyWorkspace;
|
||||
|
||||
function connectionTest_setUp() {
|
||||
dummyWorkspace = {};
|
||||
function createDummyBlock() {
|
||||
return {
|
||||
workspace: dummyWorkspace,
|
||||
isShadow: function() {return false;}
|
||||
};
|
||||
}
|
||||
input = new Blockly.Connection(createDummyBlock(),
|
||||
Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection(createDummyBlock(),
|
||||
Blockly.OUTPUT_VALUE);
|
||||
previous = new Blockly.Connection(createDummyBlock(),
|
||||
Blockly.PREVIOUS_STATEMENT);
|
||||
next = new Blockly.Connection(createDummyBlock(),
|
||||
Blockly.NEXT_STATEMENT);
|
||||
}
|
||||
|
||||
function connectionTest_tearDown() {
|
||||
input = null;
|
||||
output = null;
|
||||
previous = null;
|
||||
next = null;
|
||||
dummyWorkspace = null;
|
||||
}
|
||||
|
||||
var isMovableFn = function() { return true; };
|
||||
/**
|
||||
* These tests check that the reasons for failures to connect are consistent
|
||||
* (internal view of error states).
|
||||
*/
|
||||
function testCanConnectWithReason_TargetNull() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_TARGET_NULL,
|
||||
input.canConnectWithReason_(null));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCanConnectWithReason_Disconnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace, isMovable: isMovableFn},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
Blockly.Connection.connectReciprocally_(input, tempConnection);
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCanConnectWithReason_DifferentWorkspaces() {
|
||||
connectionTest_setUp();
|
||||
|
||||
input = new Blockly.Connection({workspace: {}}, Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
|
||||
function testCanConnectWithReason_Self() {
|
||||
connectionTest_setUp();
|
||||
|
||||
var block = {type_: "test block"};
|
||||
input.sourceBlock_ = block;
|
||||
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION,
|
||||
input.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCanConnectWithReason_Type() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(output));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCanConnectWithReason_CanConnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
previous.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
next.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
output.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* The next set of tests checks that exceptions are being thrown at the correct
|
||||
* times (external view of errors).
|
||||
*/
|
||||
function testCheckConnection_Self() {
|
||||
connectionTest_setUp();
|
||||
var block = {type_: "test block"};
|
||||
input.sourceBlock_ = block;
|
||||
try {
|
||||
input.checkConnection_(input);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypeInputPrev() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
input.checkConnection_(previous);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypeInputNext() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
input.checkConnection_(next);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypeOutputPrev() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
output.checkConnection_(previous);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypePrevInput() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
previous.checkConnection_(input);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypePrevOutput() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
previous.checkConnection_(output);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypeNextInput() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
next.checkConnection_(input);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function testCheckConnection_TypeNextOutput() {
|
||||
connectionTest_setUp();
|
||||
try {
|
||||
next.checkConnection_(output);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed_Distance() {
|
||||
var sharedWorkspace = {};
|
||||
// Two connections of opposite types near each other.
|
||||
var one = helper_createConnection(5 /* x */, 10 /* y */,
|
||||
Blockly.INPUT_VALUE, null, true);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
var two = helper_createConnection(10 /* x */, 15 /* y */,
|
||||
Blockly.OUTPUT_VALUE, null, true);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertTrue(two.isConnectionAllowed(one, 20.0));
|
||||
// Move connections farther apart.
|
||||
two.x_ = 100;
|
||||
two.y_ = 100;
|
||||
assertFalse(two.isConnectionAllowed(one, 20.0));
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed_Unrendered() {
|
||||
var sharedWorkspace = {};
|
||||
|
||||
var one = helper_createConnection(5 /* x */, 10 /* y */,
|
||||
Blockly.INPUT_VALUE);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
// Don't offer to connect a left (male) value plug to
|
||||
// an available right (female) value plug.
|
||||
// Unlike in Blockly, you can't do this even if the left value plug isn't
|
||||
// already connected.
|
||||
var two = helper_createConnection(0, 0, Blockly.OUTPUT_VALUE);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertFalse(one.isConnectionAllowed(two));
|
||||
var three = helper_createConnection(0, 0, Blockly.INPUT_VALUE);
|
||||
three.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
Blockly.Connection.connectReciprocally_(two, three);
|
||||
assertFalse(one.isConnectionAllowed(two));
|
||||
|
||||
// Don't connect two connections on the same block.
|
||||
two.sourceBlock_ = one.sourceBlock_;
|
||||
assertFalse(one.isConnectionAllowed(two));
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed_NoNext() {
|
||||
var sharedWorkspace = {};
|
||||
var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
one.sourceBlock_.nextConnection = one;
|
||||
|
||||
var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
two.sourceBlock_.previousConnection = two;
|
||||
|
||||
assertTrue(two.isConnectionAllowed(one));
|
||||
|
||||
var three = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT);
|
||||
three.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
three.sourceBlock_.previousConnection = three;
|
||||
Blockly.Connection.connectReciprocally_(one, three);
|
||||
|
||||
// A terminal block is allowed to replace another terminal block.
|
||||
assertTrue(two.isConnectionAllowed(one));
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed_InsertionMarker() {
|
||||
var sharedWorkspace = {};
|
||||
// Two connections of opposite types near each other.
|
||||
var one = helper_createConnection(5 /* x */, 10 /* y */,
|
||||
Blockly.INPUT_VALUE);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
// The second one is an insertion marker.
|
||||
var two = helper_createConnection(10 /* x */, 15 /* y */,
|
||||
Blockly.OUTPUT_VALUE);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
two.sourceBlock_.isInsertionMarker = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
assertFalse(one.isConnectionAllowed(two, 20.0));
|
||||
}
|
||||
|
||||
function testCheckConnection_Okay() {
|
||||
connectionTest_setUp();
|
||||
previous.checkConnection_(next);
|
||||
next.checkConnection_(previous);
|
||||
input.checkConnection_(output);
|
||||
output.checkConnection_(input);
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function test_canConnectWithReason_Procedures_WrongBlockType() {
|
||||
var sharedWorkspace = {};
|
||||
var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE;
|
||||
// Make one be the connection on its source block's input.
|
||||
one.sourceBlock_.getInput = function() {
|
||||
return {
|
||||
connection: one
|
||||
};
|
||||
};
|
||||
|
||||
var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
// Fail because two's source block is the wrong type.
|
||||
two.sourceBlock_.type = 'wrong_type';
|
||||
assertEquals(Blockly.Connection.REASON_CUSTOM_PROCEDURE,
|
||||
one.canConnectWithReason_(two));
|
||||
}
|
||||
|
||||
function test_canConnectWithReason_Procedures_Pass() {
|
||||
var sharedWorkspace = {};
|
||||
var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE;
|
||||
// Make one be the connection on its source block's input.
|
||||
one.sourceBlock_.getInput = function() {
|
||||
return {
|
||||
connection: one
|
||||
};
|
||||
};
|
||||
var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
two.sourceBlock_.type = Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE;
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
one.canConnectWithReason_(two));
|
||||
}
|
||||
|
||||
function test_canConnectWithReason_Procedures_NextConnection() {
|
||||
var sharedWorkspace = {};
|
||||
var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE;
|
||||
// One is the next connection, not an input connection
|
||||
one.sourceBlock_.nextConnection = one;
|
||||
one.sourceBlock_.getInput = function() {
|
||||
return {
|
||||
connection: null
|
||||
};
|
||||
};
|
||||
var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
// It should be okay, even if two's source block has the wrong type, because
|
||||
// it's not trying to connect to the input.
|
||||
two.sourceBlock_.type = 'wrong_type';
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
one.canConnectWithReason_(two));
|
||||
}
|
||||
76
scratch-blocks/tests/jsunit/db_test.js
Normal file
76
scratch-blocks/tests/jsunit/db_test.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db, 10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection_(helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT), radius);
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type) {
|
||||
var conn = new Blockly.Connection({workspace: {}}, type);
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
||||
854
scratch-blocks/tests/jsunit/event_test.js
Normal file
854
scratch-blocks/tests/jsunit/event_test.js
Normal file
@@ -0,0 +1,854 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.Events
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var mockControl_;
|
||||
var workspace;
|
||||
|
||||
function eventTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function eventTest_setUpWithMockBlocks() {
|
||||
eventTest_setUp();
|
||||
// TODO: Replace with defineGetVarBlock();
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
'type': 'field_variable_test_block',
|
||||
'message0': '%1',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_variable',
|
||||
'name': 'VAR',
|
||||
'variable': 'item'
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'simple_test_block',
|
||||
'message0': 'simple test block'
|
||||
}]);
|
||||
}
|
||||
|
||||
function eventTest_tearDown() {
|
||||
delete Blockly.Blocks['field_variable_test_block'];
|
||||
delete Blockly.Blocks['simple_test_block'];
|
||||
mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function eventTest_tearDownWithMockBlocks() {
|
||||
eventTest_tearDown();
|
||||
delete Blockly.Blocks.field_variable_test_block;
|
||||
}
|
||||
|
||||
function test_block_base_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1');
|
||||
try {
|
||||
var block = createSimpleTestBlock(workspace);
|
||||
|
||||
// Here's the event we care about.
|
||||
var event = new Blockly.Events.BlockBase(block);
|
||||
assertUndefined(event.varId);
|
||||
checkExactEventValues(event, {'blockId': '1', 'workspaceId': workspace.id,
|
||||
'group': '', 'recordUndo': true});
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_var_base_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1');
|
||||
try {
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
|
||||
var event = new Blockly.Events.VarBase(variable);
|
||||
assertUndefined(event.blockId);
|
||||
checkExactEventValues(event, {'varId': 'id1',
|
||||
'workspaceId': workspace.id, 'group': '', 'recordUndo': true});
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_abstract_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var event = new Blockly.Events.Abstract();
|
||||
assertUndefined(event.blockId);
|
||||
assertUndefined(event.workspaceId);
|
||||
assertUndefined(event.varId);
|
||||
checkExactEventValues(event, {'group': '', 'recordUndo': true});
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
// Test util
|
||||
function checkCreateEventValues(event, block, ids, type) {
|
||||
var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block));
|
||||
var result_xml = Blockly.Xml.domToText(event.xml);
|
||||
assertEquals(expected_xml, result_xml);
|
||||
isEqualArrays(ids, event.ids);
|
||||
assertEquals(type, event.type);
|
||||
}
|
||||
|
||||
// Test util
|
||||
function checkDeleteEventValues(event, block, ids, type) {
|
||||
var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block));
|
||||
var result_xml = Blockly.Xml.domToText(event.oldXml);
|
||||
assertEquals(expected_xml, result_xml);
|
||||
isEqualArrays(ids, event.ids);
|
||||
assertEquals(type, event.type);
|
||||
}
|
||||
|
||||
// Test util
|
||||
function checkExactEventValues(event, values) {
|
||||
var keys = Object.keys(values);
|
||||
for (var i = 0, field; field = keys[i]; i++) {
|
||||
assertEquals(values[field], event[field]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test util
|
||||
function createSimpleTestBlock(workspace) {
|
||||
// Disable events while constructing the block: this is a test of the
|
||||
// Blockly.Event constructors, not the block constructor.
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'simple_test_block');
|
||||
Blockly.Events.enable();
|
||||
return block;
|
||||
}
|
||||
|
||||
function test_create_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
var block = createSimpleTestBlock(workspace);
|
||||
|
||||
var event = new Blockly.Events.Create(block);
|
||||
checkCreateEventValues(event, block, ['1'], 'create');
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockCreate_constructor() {
|
||||
// expect that blockCreate behaves the same as create.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
var block = createSimpleTestBlock(workspace);
|
||||
|
||||
var event = new Blockly.Events.BlockCreate(block);
|
||||
checkCreateEventValues(event, block, ['1'], 'create');
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_delete_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
var block = createSimpleTestBlock(workspace);
|
||||
var event = new Blockly.Events.Delete(block);
|
||||
checkDeleteEventValues(event, block, ['1'], 'delete');
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockDelete_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
var block = createSimpleTestBlock(workspace);
|
||||
var event = new Blockly.Events.BlockDelete(block);
|
||||
checkDeleteEventValues(event, block, ['1'], 'delete');
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_change_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'field_variable_test_block');
|
||||
Blockly.Events.enable();
|
||||
|
||||
var event = new Blockly.Events.Change(block, 'field', 'VAR', 'id1', 'id2');
|
||||
checkExactEventValues(event, {'element': 'field', 'name': 'VAR',
|
||||
'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'});
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockChange_constructor() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'field_variable_test_block');
|
||||
Blockly.Events.enable();
|
||||
|
||||
var event = new Blockly.Events.BlockChange(block, 'field', 'VAR', 'id1',
|
||||
'id2');
|
||||
checkExactEventValues(event, {'element': 'field', 'name': 'VAR',
|
||||
'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'});
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_move_constructorCoordinate() {
|
||||
// Expect the oldCoordinate to be set.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
var coordinate = new goog.math.Coordinate(3,4);
|
||||
block1.xy_ = coordinate;
|
||||
|
||||
var event = new Blockly.Events.Move(block1);
|
||||
// Need to check for individual equality of the coordinate values since
|
||||
// the move event creates a new goog.math.Coordinate object
|
||||
assertEquals(event.oldCoordinate.x, coordinate.x);
|
||||
assertEquals(event.oldCoordinate.y, coordinate.y);
|
||||
assertEquals(event.type, 'move');
|
||||
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_move_constructoroldParentId() {
|
||||
// Expect the oldParentId to be set but not the oldCoordinate to be set.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
var block2 = createSimpleTestBlock(workspace);
|
||||
block1.parentBlock_ = block2;
|
||||
block1.xy_ = new goog.math.Coordinate(3,4);
|
||||
|
||||
var event = new Blockly.Events.Move(block1);
|
||||
checkExactEventValues(event, {'oldCoordinate': undefined,
|
||||
'oldParentId': '2', 'type': 'move'});
|
||||
block1.parentBlock_ = null;
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockMove_constructorCoordinate() {
|
||||
// Expect the oldCoordinate to be set.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
var coordinate = new goog.math.Coordinate(3,4);
|
||||
block1.xy_ = coordinate;
|
||||
|
||||
var event = new Blockly.Events.BlockMove(block1);
|
||||
// Need to check for individual equality of the coordinate values since
|
||||
// the move event creates a new goog.math.Coordinate object
|
||||
assertEquals(event.oldCoordinate.x, coordinate.x);
|
||||
assertEquals(event.oldCoordinate.y, coordinate.y);
|
||||
assertEquals(event.type, 'move');
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockMove_constructoroldParentId() {
|
||||
// Expect the oldParentId to be set but not the oldCoordinate to be set.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
var block2 = createSimpleTestBlock(workspace);
|
||||
block1.parentBlock_ = block2;
|
||||
block1.xy_ = new goog.math.Coordinate(3,4);
|
||||
|
||||
var event = new Blockly.Events.BlockMove(block1);
|
||||
checkExactEventValues(event, {'oldCoordinate': undefined,
|
||||
'oldParentId': '2', 'type': 'move'});
|
||||
block1.parentBlock_ = null;
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_uiEvent_constructor_null() {
|
||||
try {
|
||||
Blockly.Events.setGroup('testGroup');
|
||||
var event = new Blockly.Events.Ui(null, 'foo', 'bar', 'baz');
|
||||
checkExactEventValues(event,
|
||||
{
|
||||
'blockId': null,
|
||||
'workspaceId': null,
|
||||
'type': 'ui',
|
||||
'oldValue': 'bar',
|
||||
'newValue': 'baz',
|
||||
'element': 'foo',
|
||||
'recordUndo': false,
|
||||
'group': 'testGroup'
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}
|
||||
|
||||
function test_uiEvent_constructor_block() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
Blockly.Events.setGroup('testGroup');
|
||||
var event = new Blockly.Events.Ui(block1, 'foo', 'bar', 'baz');
|
||||
checkExactEventValues(event,
|
||||
{
|
||||
'blockId': '1',
|
||||
'workspaceId': workspace.id,
|
||||
'type': 'ui',
|
||||
'oldValue': 'bar',
|
||||
'newValue': 'baz',
|
||||
'element': 'foo',
|
||||
'recordUndo': false,
|
||||
'group': 'testGroup'
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
Blockly.Events.setGroup(false);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_varCreate_constructor() {
|
||||
eventTest_setUp();
|
||||
try {
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarCreate(variable);
|
||||
checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1',
|
||||
'type': 'var_create'});
|
||||
} finally {
|
||||
eventTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_varCreate_toJson() {
|
||||
eventTest_setUp();
|
||||
try {
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarCreate(variable);
|
||||
var json = event.toJson();
|
||||
var expectedJson = ({type: "var_create", varId: "id1", varType: "type1",
|
||||
varName: "name1", isLocal: false, isCloud: false});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson), JSON.stringify(json));
|
||||
|
||||
var localVariable = workspace.createVariable('name2', 'type2', 'id2', true);
|
||||
var event2 = new Blockly.Events.VarCreate(localVariable);
|
||||
var json2 = event2.toJson();
|
||||
var expectedJson2 = ({type: "var_create", varId: "id2", varType: "type2",
|
||||
varName: "name2", isLocal: true, isCloud: false});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson2), JSON.stringify(json2));
|
||||
|
||||
var cloudVariable = workspace.createVariable('name3', 'type3', 'id3', false, true);
|
||||
var event3 = new Blockly.Events.VarCreate(cloudVariable);
|
||||
var json3 = event3.toJson();
|
||||
var expectedJson3 = ({type: "var_create", varId: "id3", varType: "type3",
|
||||
varName: "name3", isLocal: false, isCloud: true});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson3), JSON.stringify(json3));
|
||||
|
||||
} finally {
|
||||
eventTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_varCreate_fromJson() {
|
||||
eventTest_setUp();
|
||||
try {
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarCreate(variable);
|
||||
var event2 = new Blockly.Events.VarCreate(null);
|
||||
var json = event.toJson();
|
||||
event2.fromJson(json);
|
||||
|
||||
assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson()));
|
||||
} finally {
|
||||
eventTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_varCreate_runForward() {
|
||||
eventTest_setUp();
|
||||
var json = {type: "var_create", varId: "id1", varType: "type1",
|
||||
varName: "name1"};
|
||||
var event = Blockly.Events.fromJson(json, workspace);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
event.run(true);
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varCreate_runBackwards() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarCreate(variable);
|
||||
assertNotNull(workspace.getVariableById('id1'));
|
||||
event.run(false);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varDelete_constructor() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarDelete(variable);
|
||||
checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1',
|
||||
'varId':'id1', 'type': 'var_delete'});
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varDelete_toJson() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarDelete(variable);
|
||||
var json = event.toJson();
|
||||
var expectedJson = ({type: "var_delete", varId: "id1", varType: "type1",
|
||||
varName: "name1", isLocal: false, isCloud: false});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson), JSON.stringify(json));
|
||||
|
||||
var localVariable = workspace.createVariable('name2', 'type2', 'id2', true);
|
||||
var event2 = new Blockly.Events.VarDelete(localVariable);
|
||||
var json2 = event2.toJson();
|
||||
var expectedJson2 = ({type: "var_delete", varId: "id2", varType: "type2",
|
||||
varName: "name2", isLocal: true, isCloud: false});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson2), JSON.stringify(json2));
|
||||
|
||||
var cloudVariable = workspace.createVariable('name3', 'type3', 'id3', false, true);
|
||||
var event3 = new Blockly.Events.VarDelete(cloudVariable);
|
||||
var json3 = event3.toJson();
|
||||
var expectedJson3 = ({type: "var_delete", varId: "id3", varType: "type3",
|
||||
varName: "name3", isLocal: false, isCloud: true});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson2), JSON.stringify(json2));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varDelete_fromJson() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarDelete(variable);
|
||||
var event2 = new Blockly.Events.VarDelete(null);
|
||||
var json = event.toJson();
|
||||
event2.fromJson(json);
|
||||
|
||||
assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson()));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varDelete_runForwards() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarDelete(variable);
|
||||
assertNotNull(workspace.getVariableById('id1'));
|
||||
event.run(true);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varDelete_runBackwards() {
|
||||
eventTest_setUp();
|
||||
var json = {type: "var_delete", varId: "id1", varType: "type1",
|
||||
varName: "name1"};
|
||||
var event = Blockly.Events.fromJson(json, workspace);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
event.run(false);
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varRename_constructor() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarRename(variable, 'name2');
|
||||
checkExactEventValues(event, {'varId': 'id1', 'oldName': 'name1',
|
||||
'newName': 'name2', 'type': 'var_rename'});
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varRename_toJson() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarRename(variable, 'name2');
|
||||
var json = event.toJson();
|
||||
var expectedJson = ({type: "var_rename", varId: "id1", oldName: "name1",
|
||||
newName: "name2"});
|
||||
|
||||
assertEquals(JSON.stringify(expectedJson), JSON.stringify(json));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varRename_fromJson() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarRename(variable, '');
|
||||
var event2 = new Blockly.Events.VarRename(null);
|
||||
var json = event.toJson();
|
||||
event2.fromJson(json);
|
||||
|
||||
assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson()));
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varRename_runForward() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarRename(variable, 'name2');
|
||||
event.run(true);
|
||||
assertNull(workspace.getVariable('name1'));
|
||||
checkVariableValues(workspace, 'name2', 'type1', 'id1');
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_varBackard_runForward() {
|
||||
eventTest_setUp();
|
||||
var variable = workspace.createVariable('name1', 'type1', 'id1');
|
||||
var event = new Blockly.Events.VarRename(variable, 'name2');
|
||||
event.run(false);
|
||||
assertNull(workspace.getVariable('name2'));
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
eventTest_tearDown();
|
||||
}
|
||||
|
||||
function test_events_filter() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [
|
||||
new Blockly.Events.BlockCreate(block1),
|
||||
new Blockly.Events.BlockMove(block1),
|
||||
new Blockly.Events.BlockChange(block1, 'field', 'VAR', 'id1', 'id2'),
|
||||
new Blockly.Events.Ui(block1, 'click')
|
||||
];
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(4, filteredEvents.length); // no event should have been removed.
|
||||
// test that the order hasn't changed
|
||||
assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate);
|
||||
assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove);
|
||||
assertTrue(filteredEvents[2] instanceof Blockly.Events.BlockChange);
|
||||
assertTrue(filteredEvents[3] instanceof Blockly.Events.Ui);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_events_filterForward() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [
|
||||
new Blockly.Events.BlockCreate(block1),
|
||||
];
|
||||
helper_addMoveEvent(events, block1, 1, 1);
|
||||
helper_addMoveEvent(events, block1, 2, 2);
|
||||
helper_addMoveEvent(events, block1, 3, 3);
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(2, filteredEvents.length); // duplicate moves should have been removed.
|
||||
// test that the order hasn't changed
|
||||
assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate);
|
||||
assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove);
|
||||
assertEquals(3, filteredEvents[1].newCoordinate.x);
|
||||
assertEquals(3, filteredEvents[1].newCoordinate.y);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_events_filterBackward() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [
|
||||
new Blockly.Events.BlockCreate(block1),
|
||||
];
|
||||
helper_addMoveEvent(events, block1, 1, 1);
|
||||
helper_addMoveEvent(events, block1, 2, 2);
|
||||
helper_addMoveEvent(events, block1, 3, 3);
|
||||
var filteredEvents = Blockly.Events.filter(events, false);
|
||||
assertEquals(2, filteredEvents.length); // duplicate event should have been removed.
|
||||
// test that the order hasn't changed
|
||||
assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate);
|
||||
assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove);
|
||||
assertEquals(1, filteredEvents[1].newCoordinate.x);
|
||||
assertEquals(1, filteredEvents[1].newCoordinate.y);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_events_filterDifferentBlocks() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var block2 = workspace.newBlock('field_variable_test_block', '2');
|
||||
var events = [
|
||||
new Blockly.Events.BlockCreate(block1),
|
||||
new Blockly.Events.BlockMove(block1),
|
||||
new Blockly.Events.BlockCreate(block2),
|
||||
new Blockly.Events.BlockMove(block2)
|
||||
];
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(4, filteredEvents.length); // no event should have been removed.
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_events_mergeMove() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [];
|
||||
helper_addMoveEvent(events, block1, 0, 0);
|
||||
helper_addMoveEvent(events, block1, 1, 1);
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(1, filteredEvents.length); // second move event merged into first
|
||||
assertEquals(1, filteredEvents[0].newCoordinate.x);
|
||||
assertEquals(1, filteredEvents[0].newCoordinate.y);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_events_mergeChange() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [
|
||||
new Blockly.Events.Change(block1, 'field', 'VAR', 'item', 'item1'),
|
||||
new Blockly.Events.Change(block1, 'field', 'VAR', 'item1', 'item2')
|
||||
];
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(1, filteredEvents.length); // second change event merged into first
|
||||
assertEquals('item', filteredEvents[0].oldValue);
|
||||
assertEquals('item2', filteredEvents[0].newValue);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_events_mergeUi() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block1 = workspace.newBlock('field_variable_test_block', '1');
|
||||
var block2 = workspace.newBlock('field_variable_test_block', '2');
|
||||
var block3 = workspace.newBlock('field_variable_test_block', '3');
|
||||
var events = [
|
||||
new Blockly.Events.Ui(block1, 'commentOpen', 'false', 'true'),
|
||||
new Blockly.Events.Ui(block1, 'click', 'false', 'true'),
|
||||
new Blockly.Events.Ui(block2, 'mutatorOpen', 'false', 'true'),
|
||||
new Blockly.Events.Ui(block2, 'click', 'false', 'true'),
|
||||
new Blockly.Events.Ui(block3, 'warningOpen', 'false', 'true'),
|
||||
new Blockly.Events.Ui(block3, 'click', 'false', 'true')
|
||||
];
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
assertEquals(3, filteredEvents.length); // click event merged into corresponding *Open event
|
||||
assertEquals('commentOpen', filteredEvents[0].element);
|
||||
assertEquals('mutatorOpen', filteredEvents[1].element);
|
||||
assertEquals('warningOpen', filteredEvents[2].element);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that events that collide on a (event, block, workspace) tuple
|
||||
* but cannot be merged do not get dropped during filtering.
|
||||
*/
|
||||
function test_events_stackclick() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block = workspace.newBlock('field_variable_test_block', '1');
|
||||
var events = [
|
||||
new Blockly.Events.Ui(block, 'click', undefined, undefined),
|
||||
new Blockly.Events.Ui(block, 'stackclick', undefined, undefined)
|
||||
];
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
// click and stackclick should both exist
|
||||
assertEquals(2, filteredEvents.length);
|
||||
assertEquals('click', filteredEvents[0].element);
|
||||
assertEquals('stackclick', filteredEvents[1].element);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutator composition could result in move events for blocks
|
||||
* connected to the mutated block that were null operations. This
|
||||
* leads to events in the undo/redo queue that do nothing, requiring
|
||||
* an extra undo/redo to proceed to the next event. This test ensures
|
||||
* that two move events that do get merged (disconnecting and
|
||||
* reconnecting a block in response to a mutator change) are filtered
|
||||
* from the queue.
|
||||
*/
|
||||
function test_events_filteraftermerge() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
var block = workspace.newBlock('field_variable_test_block', '1');
|
||||
block.setParent(null);
|
||||
var events = [];
|
||||
helper_addMoveEventParent(events, block, null);
|
||||
helper_addMoveEventParent(events, block, null);
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
// The two events should be merged, but because nothing has changed
|
||||
// they will be filtered out.
|
||||
assertEquals(0, filteredEvents.length);
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to simulate block move events.
|
||||
*
|
||||
* @param {!Array.<Blockly.Events.Abstract>} events a queue of events.
|
||||
* @param {!Blockly.Block} block the block to be moved
|
||||
* @param {number} newX new X coordinate of the block
|
||||
* @param {number} newY new Y coordinate of the block
|
||||
*/
|
||||
function helper_addMoveEvent(events, block, newX, newY) {
|
||||
events.push(new Blockly.Events.BlockMove(block));
|
||||
block.xy_ = new goog.math.Coordinate(newX, newY);
|
||||
events[events.length-1].recordNew();
|
||||
}
|
||||
|
||||
function helper_addMoveEventParent(events, block, parent) {
|
||||
events.push(new Blockly.Events.BlockMove(block));
|
||||
block.setParent(parent);
|
||||
events[events.length-1].recordNew();
|
||||
}
|
||||
|
||||
function test_events_newblock_newvar() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
|
||||
Blockly.Events.fire = temporary_fireEvent;
|
||||
temporary_fireEvent.firedEvents_ = [];
|
||||
// Expect three calls to genUid: one to set the block's ID, one for the event
|
||||
// group's id, and one for the variable's ID.
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2', '3']);
|
||||
try {
|
||||
var block = workspace.newBlock('field_variable_test_block');
|
||||
|
||||
var firedEvents = workspace.undoStack_;
|
||||
// Expect two events: varCreate and block create.
|
||||
assertEquals(2, firedEvents.length);
|
||||
|
||||
var event0 = firedEvents[0];
|
||||
var event1 = firedEvents[1];
|
||||
assertEquals('var_create', event0.type);
|
||||
assertEquals('create', event1.type);
|
||||
|
||||
// Expect the events to have the same group ID.
|
||||
assertEquals(event0.group, event1.group);
|
||||
|
||||
// Expect the group ID to be the result of the second call to genUid.
|
||||
assertEquals('2', event0.group);
|
||||
|
||||
// Expect the workspace to have a variable with ID '3'.
|
||||
assertNotNull(workspace.getVariableById('3'));
|
||||
assertEquals('3', event0.varId);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
Blockly.Events.fire = savedFireFunc;
|
||||
}
|
||||
}
|
||||
|
||||
// The sequence of events should be the same whether the block was created from
|
||||
// XML or directly.
|
||||
function test_events_newblock_newvar_xml() {
|
||||
eventTest_setUpWithMockBlocks();
|
||||
|
||||
Blockly.Events.fire = temporary_fireEvent;
|
||||
temporary_fireEvent.firedEvents_ = [];
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <block type="field_variable_test_block" id="block1">' +
|
||||
' <field name="VAR" id="id1" variabletype="">name1</field>' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
|
||||
var firedEvents = workspace.undoStack_;
|
||||
// Expect two events: varCreate and block create.
|
||||
// TODO: scratch-blocks also has move and delete events.
|
||||
// assertEquals(2, firedEvents.length);
|
||||
|
||||
var event0 = firedEvents[0];
|
||||
var event1 = firedEvents[1];
|
||||
assertEquals('var_create', event0.type);
|
||||
assertEquals('create', event1.type);
|
||||
|
||||
// Expect the events to have the same group ID.
|
||||
assertEquals(event0.group, event1.group);
|
||||
|
||||
// Expect the workspace to have a variable with ID 'id1'.
|
||||
assertNotNull(workspace.getVariableById('id1'));
|
||||
assertEquals('id1', event0.varId);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
Blockly.Events.fire = savedFireFunc;
|
||||
}
|
||||
}
|
||||
|
||||
function test_events_filter_nomerge_move() {
|
||||
// Move events should only merge if they refer to the same block and are
|
||||
// consecutive.
|
||||
// See github.com/google/blockly/pull/1892 for a worked example showing
|
||||
// how merging non-consecutive events can fail when replacing a shadow
|
||||
// block.
|
||||
eventTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var block1 = createSimpleTestBlock(workspace);
|
||||
var block2 = createSimpleTestBlock(workspace);
|
||||
|
||||
var events = [];
|
||||
helper_addMoveEvent(events, block1, 1, 1);
|
||||
helper_addMoveEvent(events, block2, 1, 1);
|
||||
events.push(new Blockly.Events.BlockDelete(block2));
|
||||
helper_addMoveEvent(events, block1, 2, 2);
|
||||
|
||||
var filteredEvents = Blockly.Events.filter(events, true);
|
||||
// Nothing should have merged.
|
||||
assertEquals(4, filteredEvents.length);
|
||||
// test that the order hasn't changed
|
||||
assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockMove);
|
||||
assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove);
|
||||
assertTrue(filteredEvents[2] instanceof Blockly.Events.BlockDelete);
|
||||
assertTrue(filteredEvents[3] instanceof Blockly.Events.BlockMove);
|
||||
} finally {
|
||||
eventTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
659
scratch-blocks/tests/jsunit/extensions_test.js
Normal file
659
scratch-blocks/tests/jsunit/extensions_test.js
Normal file
@@ -0,0 +1,659 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.Extensions
|
||||
* @author Anm@anm.me (Andrew n marshall)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_extension() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['extensions_test']);
|
||||
|
||||
var numCallsToBefore = 0;
|
||||
var numCallsToAfter = 0;
|
||||
|
||||
// Extension defined before the block type is defined.
|
||||
Blockly.Extensions.register('extensions_test_before', function () {
|
||||
numCallsToBefore++;
|
||||
this.extendedWithBefore = true;
|
||||
});
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "extension_test_block",
|
||||
"message0": "extension_test_block",
|
||||
"extensions": ["extensions_test_before", "extensions_test_after"]
|
||||
}]);
|
||||
|
||||
// Extension defined after the block type (but before instantiation).
|
||||
Blockly.Extensions.register('extensions_test_after', function () {
|
||||
numCallsToAfter++;
|
||||
this.extendedWithAfter = true;
|
||||
});
|
||||
|
||||
assert(goog.isFunction(Blockly.Extensions.ALL_['extensions_test_before']));
|
||||
assert(goog.isFunction(Blockly.Extensions.ALL_['extensions_test_after']));
|
||||
assertEquals(0, numCallsToBefore);
|
||||
assertEquals(0, numCallsToAfter);
|
||||
|
||||
block = new Blockly.Block(workspace, 'extension_test_block');
|
||||
|
||||
assertEquals(1, numCallsToBefore);
|
||||
assertEquals(1, numCallsToAfter);
|
||||
assert(block.extendedWithBefore);
|
||||
assert(block.extendedWithAfter);
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
|
||||
delete Blockly.Extensions.ALL_['extensions_test_before'];
|
||||
delete Blockly.Extensions.ALL_['extensions_test_after'];
|
||||
delete Blockly.Blocks['extension_test_block'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_extension_missing() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['missing_extension']);
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "missing_extension_block",
|
||||
"message0": "missing_extension_block",
|
||||
"extensions": ["missing_extension"]
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, 'missing_extension_block');
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks['missing_extension_block'];
|
||||
}
|
||||
assert(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_extension_not_a_function() {
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['extension_just_a_string']);
|
||||
Blockly.Extensions.register('extension_just_a_string', 'extension_just_a_string');
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['extension_just_a_string'];
|
||||
}
|
||||
assert(exceptionWasThrown);
|
||||
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['extension_is_null']);
|
||||
Blockly.Extensions.register('extension_is_null', null);
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['extension_is_null'];
|
||||
}
|
||||
assert(exceptionWasThrown);
|
||||
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['extension_is_undefined']);
|
||||
Blockly.Extensions.register('extension_is_undefined');
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['extension_is_undefined'];
|
||||
}
|
||||
assert(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_parent_tooltip_when_inline() {
|
||||
var defaultTooltip = "defaultTooltip";
|
||||
var parentTooltip = "parentTooltip";
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([
|
||||
{
|
||||
"type": "test_parent_tooltip_when_inline",
|
||||
"message0": "test_parent_tooltip_when_inline",
|
||||
"output": true,
|
||||
"tooltip": defaultTooltip,
|
||||
"extensions": ["parent_tooltip_when_inline"]
|
||||
},
|
||||
{
|
||||
"type": "test_parent",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "INPUT"
|
||||
}
|
||||
],
|
||||
"tooltip": parentTooltip
|
||||
}
|
||||
]);
|
||||
|
||||
block = new Blockly.Block(workspace, 'test_parent_tooltip_when_inline');
|
||||
|
||||
// Tooltip is dynamic after extension initialization.
|
||||
assert(goog.isFunction(block.tooltip));
|
||||
assertEquals(block.tooltip(), defaultTooltip);
|
||||
|
||||
// Tooltip is normal before connected to parent.
|
||||
var parent = new Blockly.Block(workspace, 'test_parent');
|
||||
// Inputs default to inline in scratch-blocks.
|
||||
parent.setInputsInline(false);
|
||||
assertEquals(parent.tooltip, parentTooltip);
|
||||
assertFalse(!!parent.inputsInline);
|
||||
|
||||
// Tooltip is normal when parent is not inline.
|
||||
parent.getInput('INPUT').connection.connect(block.outputConnection);
|
||||
assertEquals(block.getParent(), parent);
|
||||
assertEquals(block.tooltip(), defaultTooltip);
|
||||
|
||||
// Tooltip is parent's when parent is inline.
|
||||
parent.setInputsInline(true);
|
||||
assertEquals(block.tooltip(), parentTooltip);
|
||||
|
||||
// Tooltip revert when disconnected.
|
||||
parent.getInput('INPUT').connection.disconnect();
|
||||
assert(!block.getParent());
|
||||
assertEquals(block.tooltip(), defaultTooltip);
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
|
||||
delete Blockly.Blocks['test_parent_tooltip_when_inline'];
|
||||
delete Blockly.Blocks['test_parent'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_mixin_extension() {
|
||||
var TEST_MIXIN = {
|
||||
field: 'FIELD',
|
||||
method: function() {
|
||||
console.log('TEXT_MIXIN method()');
|
||||
}
|
||||
};
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['mixin_test']);
|
||||
|
||||
// Extension defined before the block type is defined.
|
||||
Blockly.Extensions.registerMixin('mixin_test', TEST_MIXIN);
|
||||
assert(goog.isFunction(Blockly.Extensions.ALL_['mixin_test']));
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "test_block_mixin",
|
||||
"message0": "test_block_mixin",
|
||||
"extensions": ["mixin_test"]
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, 'test_block_mixin');
|
||||
|
||||
assertEquals(TEST_MIXIN.field, block.field);
|
||||
assertEquals(TEST_MIXIN.method, block.method);
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
|
||||
delete Blockly.Extensions.ALL_['mixin_test'];
|
||||
delete Blockly.Blocks['test_block_mixin'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_bad_mixin_overwrites_local_value() {
|
||||
var TEST_MIXIN_BAD_INPUTLIST = {
|
||||
inputList: 'bad inputList' // Defined in constructor
|
||||
};
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['mixin_bad_inputList']);
|
||||
|
||||
// Extension defined before the block type is defined.
|
||||
Blockly.Extensions.registerMixin('mixin_bad_inputList', TEST_MIXIN_BAD_INPUTLIST);
|
||||
assert(goog.isFunction(Blockly.Extensions.ALL_['mixin_bad_inputList']));
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "test_block_bad_inputList",
|
||||
"message0": "test_block_bad_inputList",
|
||||
"extensions": ["mixin_bad_inputList"]
|
||||
}]);
|
||||
|
||||
try {
|
||||
block = new Blockly.Block(workspace, 'test_block_bad_inputList');
|
||||
} catch (e) {
|
||||
// Expected Error
|
||||
assert(e.message.indexOf('inputList') >= 0); // Reference the conflict
|
||||
return;
|
||||
}
|
||||
fail('Expected error when constructing block');
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
|
||||
delete Blockly.Extensions.ALL_['mixin_bad_inputList'];
|
||||
delete Blockly.Blocks['test_block_bad_inputList'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_bad_mixin_overwrites_prototype() {
|
||||
var TEST_MIXIN_BAD_COLOUR = {
|
||||
colour_: 'bad colour_' // Defined on prototype
|
||||
};
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
assertUndefined(Blockly.Extensions.ALL_['mixin_bad_colour_']);
|
||||
|
||||
// Extension defined before the block type is defined.
|
||||
Blockly.Extensions.registerMixin('mixin_bad_colour_', TEST_MIXIN_BAD_COLOUR);
|
||||
assert(goog.isFunction(Blockly.Extensions.ALL_['mixin_bad_colour_']));
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "test_block_bad_colour",
|
||||
"message0": "test_block_bad_colour",
|
||||
"extensions": ["mixin_bad_colour_"]
|
||||
}]);
|
||||
|
||||
try {
|
||||
block = new Blockly.Block(workspace, 'test_block_bad_colour');
|
||||
} catch (e) {
|
||||
// Expected Error
|
||||
assert(e.message.indexOf('colour_') >= 0); // Reference the conflict
|
||||
return;
|
||||
}
|
||||
fail('Expected error when constructing block');
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
|
||||
delete Blockly.Extensions.ALL_['mixin_bad_colour_'];
|
||||
delete Blockly.Blocks['test_block_bad_colour'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_mutator_mixin() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"mutator": "mutator_test"
|
||||
}]);
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
Blockly.Events.disable();
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
},
|
||||
compose: function() {
|
||||
return 'composeFn';
|
||||
},
|
||||
decompose: function() {
|
||||
return 'decomposeFn';
|
||||
}
|
||||
});
|
||||
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
|
||||
// Make sure all of the functions were installed correctly.
|
||||
assertEquals(block.domToMutation(), 'domToMutationFn');
|
||||
assertEquals(block.mutationToDom(), 'mutationToDomFn');
|
||||
assertEquals(block.compose(), 'composeFn');
|
||||
assertEquals(block.decompose(), 'decomposeFn');
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_mutator_mixin_no_dialog() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"mutator": "mutator_test"
|
||||
}]);
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
Blockly.Events.disable();
|
||||
assertUndefined(Blockly.Extensions.ALL_['mutator_test']);
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
}
|
||||
});
|
||||
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
|
||||
// Make sure all of the functions were installed correctly.
|
||||
assertEquals(block.domToMutation(), 'domToMutationFn');
|
||||
assertEquals(block.mutationToDom(), 'mutationToDomFn');
|
||||
assertFalse(block.hasOwnProperty('compose'));
|
||||
assertFalse(block.hasOwnProperty('decompose'));
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly check all four things that could be missing.
|
||||
function test_mutator_mixin_no_decompose_fails() {
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
},
|
||||
compose: function() {
|
||||
return 'composeFn';
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_mutator_mixin_no_compose_fails() {
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
},
|
||||
decompose: function() {
|
||||
return 'decomposeFn';
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_mutator_mixin_no_domToMutation_fails() {
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
},
|
||||
compose: function() {
|
||||
return 'composeFn';
|
||||
},
|
||||
decompose: function() {
|
||||
return 'decomposeFn';
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_mutator_mixin_no_mutationToDom_fails() {
|
||||
var exceptionWasThrown = false;
|
||||
try {
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
compose: function() {
|
||||
return 'composeFn';
|
||||
},
|
||||
decompose: function() {
|
||||
return 'decomposeFn';
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Expected.
|
||||
exceptionWasThrown = true;
|
||||
} finally {
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_use_mutator_as_extension_fails() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
var exceptionWasThrown = false;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"extensions": ["mutator_test"]
|
||||
}]);
|
||||
|
||||
Blockly.Events.disable();
|
||||
assertUndefined(Blockly.Extensions.ALL_['mutator_test']);
|
||||
Blockly.Extensions.registerMutator('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
}
|
||||
});
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
} catch (e) {
|
||||
// Expected
|
||||
exceptionWasThrown = true;
|
||||
// Should have failed on apply, not on register.
|
||||
assertNotNull(Blockly.Extensions.ALL_['mutator_test']);
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_use_mutator_mixin_as_extension_fails() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
var exceptionWasThrown = false;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"extensions": ["mutator_test"]
|
||||
}]);
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
Blockly.Events.disable();
|
||||
assertUndefined(Blockly.Extensions.ALL_['mutator_test']);
|
||||
Blockly.Extensions.registerMixin('mutator_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
}
|
||||
});
|
||||
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
} catch (e) {
|
||||
// Expected
|
||||
exceptionWasThrown = true;
|
||||
// Should have failed on apply, not on register.
|
||||
assertNotNull(Blockly.Extensions.ALL_['mutator_test']);
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['mutator_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_use_extension_as_mutator_fails() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
var exceptionWasThrown = false;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"mutator": ["extensions_test"]
|
||||
}]);
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
Blockly.Events.disable();
|
||||
assertUndefined(Blockly.Extensions.ALL_['extensions_test']);
|
||||
Blockly.Extensions.register('extensions_test', function() {
|
||||
return 'extensions_test_fn';
|
||||
});
|
||||
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
} catch (e) {
|
||||
// Expected
|
||||
exceptionWasThrown = true;
|
||||
// Should have failed on apply, not on register.
|
||||
assertNotNull(Blockly.Extensions.ALL_['extensions_test']);
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['extensions_test'];
|
||||
}
|
||||
assertTrue(exceptionWasThrown);
|
||||
}
|
||||
|
||||
function test_mutator_mixin_plus_function() {
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
var fnWasCalled = false;
|
||||
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "mutator_test_block",
|
||||
"message0": "mutator_test_block",
|
||||
"mutator": ["extensions_test"]
|
||||
}]);
|
||||
|
||||
Blockly.Events.disable();
|
||||
assertUndefined(Blockly.Extensions.ALL_['extensions_test']);
|
||||
Blockly.Extensions.registerMutator('extensions_test',
|
||||
{
|
||||
domToMutation: function() {
|
||||
return 'domToMutationFn';
|
||||
},
|
||||
mutationToDom: function() {
|
||||
return 'mutationToDomFn';
|
||||
}
|
||||
},
|
||||
function() {
|
||||
fnWasCalled = true;
|
||||
}
|
||||
);
|
||||
|
||||
// Events code calls mutationToDom and expects it to give back a meaningful
|
||||
// value.
|
||||
block = new Blockly.Block(workspace, 'mutator_test_block');
|
||||
} finally {
|
||||
if (block) {
|
||||
block.dispose();
|
||||
}
|
||||
workspace.dispose();
|
||||
Blockly.Events.enable();
|
||||
delete Blockly.Extensions.ALL_['extensions_test'];
|
||||
}
|
||||
assertTrue(fnWasCalled);
|
||||
}
|
||||
44
scratch-blocks/tests/jsunit/field_angle_test.js
Normal file
44
scratch-blocks/tests/jsunit/field_angle_test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.FieldAngle
|
||||
* @author Anm@anm.me (Andrew n marshall)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_fieldangle_constructor() {
|
||||
assertEquals(new Blockly.FieldAngle().getValue(), '0');
|
||||
assertEquals(new Blockly.FieldAngle(null).getValue(), '0');
|
||||
assertEquals(new Blockly.FieldAngle(undefined).getValue(), '0');
|
||||
assertEquals(new Blockly.FieldAngle(1).getValue(), '1');
|
||||
assertEquals(new Blockly.FieldAngle(1.5).getValue(), '1.5');
|
||||
assertEquals(new Blockly.FieldAngle('2').getValue(), '2');
|
||||
assertEquals(new Blockly.FieldAngle('2.5').getValue(), '2.5');
|
||||
|
||||
// Bad values
|
||||
assertEquals(new Blockly.FieldAngle('bad').getValue(), '0');
|
||||
assertEquals(new Blockly.FieldAngle(NaN).getValue(), '0');
|
||||
}
|
||||
|
||||
function test_fieldangle_fromJson() {
|
||||
assertEquals(Blockly.FieldAngle.fromJson({}).getValue(), '0');
|
||||
assertEquals(Blockly.FieldAngle.fromJson({ angle: 1 }).getValue(), '1');
|
||||
}
|
||||
82
scratch-blocks/tests/jsunit/field_number_test.js
Normal file
82
scratch-blocks/tests/jsunit/field_number_test.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.FieldNumber
|
||||
* @author Anm@anm.me (Andrew n marshall)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_fieldnumber_constructor() {
|
||||
// No arguments
|
||||
var field = new Blockly.FieldNumber();
|
||||
assertEquals(field.getValue(), '0');
|
||||
|
||||
// Unlike blockly, scratch-blocks doesn't store min, max, and precision.
|
||||
// TODO: Update this to check the restrictor, based on min, max, and precision.
|
||||
assertEquals(field.min_, undefined);
|
||||
assertEquals(field.max_, undefined);
|
||||
assertEquals(field.precision_, undefined);
|
||||
|
||||
// Numeric values
|
||||
field = new Blockly.FieldNumber(1);
|
||||
assertEquals(field.getValue(), '1');
|
||||
field = new Blockly.FieldNumber(1.5);
|
||||
assertEquals(field.getValue(), '1.5');
|
||||
|
||||
// String value
|
||||
field = new Blockly.FieldNumber('2');
|
||||
assertEquals(field.getValue(), '2');
|
||||
field = new Blockly.FieldNumber('2.5');
|
||||
assertEquals(field.getValue(), '2.5');
|
||||
|
||||
// All values
|
||||
field = new Blockly.FieldNumber(
|
||||
/* value */ 0,
|
||||
/* min */ -128,
|
||||
/* max */ 127,
|
||||
/* precision */ 1);
|
||||
// Unlike blockly, scratch-blocks doesn't store min, max, and precision.
|
||||
assertEquals(field.getValue(), '0');
|
||||
assertEquals(field.min_, undefined);
|
||||
assertEquals(field.max_, undefined);
|
||||
assertEquals(field.precision_, undefined);
|
||||
|
||||
// Bad value defaults to '0'
|
||||
field = new Blockly.FieldNumber('bad');
|
||||
assertEquals(field.getValue(), '0');
|
||||
field = new Blockly.FieldNumber(NaN);
|
||||
assertEquals(field.getValue(), '0');
|
||||
}
|
||||
|
||||
function test_fieldnumber_fromJson() {
|
||||
assertEquals(Blockly.FieldNumber.fromJson({}).getValue(), '0');
|
||||
assertEquals(Blockly.FieldNumber.fromJson({ value: 1 }).getValue(), '1');
|
||||
|
||||
// All options, but scratch-blocks parses min/max/precision differently from
|
||||
// Blockly. See notes in field_number.js.
|
||||
var field = Blockly.FieldNumber.fromJson({
|
||||
value: 0,
|
||||
min: -128,
|
||||
max: 127,
|
||||
precision: 1
|
||||
});
|
||||
assertEquals(field.getValue(), '0');
|
||||
}
|
||||
126
scratch-blocks/tests/jsunit/field_test.js
Normal file
126
scratch-blocks/tests/jsunit/field_test.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.Field
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_field_isEditable_simple() {
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
// EDITABLE is true by default, but without a source block a field can't be
|
||||
// edited.
|
||||
assertFalse('Field without a block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_isEditable_false() {
|
||||
// Setting EDITABLE to false doesn't matter.
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
field.EDITABLE = false;
|
||||
assertFalse('Field without a block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_isEditable_editableBlock() {
|
||||
var editableBlock = {
|
||||
isEditable: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
field.sourceBlock_ = editableBlock;
|
||||
|
||||
assertTrue('Editable field with editable block is editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_isEditable_editableBlock_false() {
|
||||
var editableBlock = {
|
||||
isEditable: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
field.sourceBlock_ = editableBlock;
|
||||
field.EDITABLE = false;
|
||||
|
||||
assertFalse('Non-editable field with editable block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_isEditable_nonEditableBlock() {
|
||||
var nonEditableBlock = {
|
||||
isEditable: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
field.sourceBlock_ = nonEditableBlock;
|
||||
|
||||
assertFalse('Editable field with non-editable block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_isEditable_nonEditableBlock_false() {
|
||||
var nonEditableBlock = {
|
||||
isEditable: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var field = new Blockly.Field("Dummy text");
|
||||
field.sourceBlock_ = nonEditableBlock;
|
||||
field.EDITABLE = false;
|
||||
|
||||
assertFalse('Non-editable field with non-editable block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_field_register_with_custom_field() {
|
||||
var CustomFieldType = function(value) {
|
||||
CustomFieldType.superClass_.constructor.call(this, value);
|
||||
};
|
||||
goog.inherits(CustomFieldType, Blockly.Field);
|
||||
|
||||
CustomFieldType.fromJson = function(options) {
|
||||
return new CustomFieldType(options['value']);
|
||||
};
|
||||
|
||||
var json = {
|
||||
type: 'field_custom_test',
|
||||
value: 'ok'
|
||||
};
|
||||
|
||||
// before registering
|
||||
var field = Blockly.Field.fromJson(json);
|
||||
assertNull(field);
|
||||
|
||||
Blockly.Field.register('field_custom_test', CustomFieldType);
|
||||
|
||||
// after registering
|
||||
field = Blockly.Field.fromJson(json);
|
||||
assertNotNull(field);
|
||||
assertEquals(field.getValue(), 'ok');
|
||||
}
|
||||
63
scratch-blocks/tests/jsunit/field_variable_getter_test.js
Normal file
63
scratch-blocks/tests/jsunit/field_variable_getter_test.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.FieldVariableGetter
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_fieldvariablegetter_constructor() {
|
||||
var field = new Blockly.FieldVariableGetter('text', 'name');
|
||||
// The field does not have a variable until after init() is called.
|
||||
assertEquals(field.getText(), '');
|
||||
assertEquals(field.getValue(), '');
|
||||
}
|
||||
|
||||
function test_fieldvariablegetter_isEditable() {
|
||||
var field = new Blockly.FieldVariableGetter('text', 'name');
|
||||
// EDITABLE is true by default, but without a source block a field can't be
|
||||
// edited.
|
||||
assertFalse('Field without a block is not editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_fieldvariablegetter_isEditableBlock() {
|
||||
var field = new Blockly.FieldVariableGetter('text', 'name');
|
||||
|
||||
var editableBlock = {
|
||||
isEditable: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
field.sourceBlock_ = editableBlock;
|
||||
|
||||
// Variable getter fields aren't user editable.
|
||||
assertFalse('Variable getter field should not be editable',
|
||||
field.isCurrentlyEditable());
|
||||
}
|
||||
|
||||
function test_fieldvariablegetter_isSerializable() {
|
||||
var field = new Blockly.FieldVariableGetter('text', 'name');
|
||||
// Variable getter fields are serializable by default.
|
||||
assertTrue('Variable getter field should be serializable',
|
||||
field.SERIALIZABLE);
|
||||
}
|
||||
202
scratch-blocks/tests/jsunit/field_variable_test.js
Normal file
202
scratch-blocks/tests/jsunit/field_variable_test.js
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.FieldVariable
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var workspace;
|
||||
var mockControl_;
|
||||
|
||||
function fieldVariableTestWithMocks_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function fieldVariableTestWithMocks_tearDown() {
|
||||
mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function fieldVariable_mockBlock(workspace) {
|
||||
return {'workspace': workspace, 'isShadow': function(){return false;}};
|
||||
}
|
||||
|
||||
function fieldVariable_createAndInitField(workspace) {
|
||||
var fieldVariable = new Blockly.FieldVariable('name1');
|
||||
var mockBlock = fieldVariable_mockBlock(workspace);
|
||||
fieldVariable.setSourceBlock(mockBlock);
|
||||
// No view to initialize, but the model still needs work.
|
||||
fieldVariable.initModel();
|
||||
return fieldVariable;
|
||||
}
|
||||
|
||||
function test_fieldVariable_Constructor() {
|
||||
workspace = new Blockly.Workspace();
|
||||
var fieldVariable = new Blockly.FieldVariable('name1');
|
||||
// The field does not have a variable until after init() is called.
|
||||
assertEquals('', fieldVariable.getText());
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_fieldVariable_setValueMatchId() {
|
||||
// Expect the fieldVariable value to be set to variable name
|
||||
fieldVariableTestWithMocks_setUp();
|
||||
workspace.createVariable('name2', null, 'id2');
|
||||
|
||||
var fieldVariable = fieldVariable_createAndInitField(workspace);
|
||||
|
||||
var oldId = fieldVariable.getValue();
|
||||
var event = new Blockly.Events.BlockChange(
|
||||
fieldVariable.sourceBlock_, 'field', undefined, oldId, 'id2');
|
||||
setUpMockMethod(mockControl_, Blockly.Events, 'fire', [event], null);
|
||||
|
||||
fieldVariable.setValue('id2');
|
||||
assertEquals('name2', fieldVariable.getText());
|
||||
assertEquals('id2', fieldVariable.getValue());
|
||||
fieldVariableTestWithMocks_tearDown();
|
||||
}
|
||||
|
||||
function test_fieldVariable_setValueNoVariable() {
|
||||
fieldVariableTestWithMocks_setUp();
|
||||
|
||||
var fieldVariable = fieldVariable_createAndInitField(workspace);
|
||||
var mockBlock = fieldVariable.sourceBlock_;
|
||||
mockBlock.isShadow = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
try {
|
||||
fieldVariable.setValue('id1');
|
||||
// Calling setValue with a variable that doesn't exist throws an error.
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
} finally {
|
||||
fieldVariableTestWithMocks_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_fieldVariable_dropdownCreateVariablesExist() {
|
||||
// Expect that the dropdown options will contain the variables that exist.
|
||||
workspace = new Blockly.Workspace();
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
workspace.createVariable('name2', '', 'id2');
|
||||
|
||||
var fieldVariable = fieldVariable_createAndInitField(workspace);
|
||||
|
||||
var result_options = Blockly.FieldVariable.dropdownCreate.call(
|
||||
fieldVariable);
|
||||
|
||||
assertEquals(result_options.length, 3);
|
||||
isEqualArrays(result_options[0], ['name1', 'id1']);
|
||||
isEqualArrays(result_options[1], ['name2', 'id2']);
|
||||
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_fieldVariable_setValueNull() {
|
||||
// This should no longer create a variable for the selected option.
|
||||
fieldVariableTestWithMocks_setUp();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['id1', null]);
|
||||
|
||||
var fieldVariable = fieldVariable_createAndInitField(workspace);
|
||||
try {
|
||||
fieldVariable.setValue(null);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
} finally {
|
||||
fieldVariableTestWithMocks_tearDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function test_fieldVariable_getVariableTypes_undefinedVariableTypes() {
|
||||
// Expect that since variableTypes is undefined, only type empty string
|
||||
// will be returned (regardless of what types are available on the workspace).
|
||||
workspace = new Blockly.Workspace();
|
||||
workspace.createVariable('name1', 'type1');
|
||||
workspace.createVariable('name2', 'type2');
|
||||
|
||||
var fieldVariable = new Blockly.FieldVariable('name1');
|
||||
var resultTypes = fieldVariable.getVariableTypes_();
|
||||
isEqualArrays(resultTypes, ['']);
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_fieldVariable_getVariableTypes_givenVariableTypes() {
|
||||
// Expect that since variableTypes is defined, it will be the return value,
|
||||
// regardless of what types are available on the workspace.
|
||||
workspace = new Blockly.Workspace();
|
||||
workspace.createVariable('name1', 'type1');
|
||||
workspace.createVariable('name2', 'type2');
|
||||
|
||||
var fieldVariable = new Blockly.FieldVariable(
|
||||
'name1', null, ['type1', 'type2']);
|
||||
var resultTypes = fieldVariable.getVariableTypes_();
|
||||
isEqualArrays(resultTypes, ['type1', 'type2']);
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_fieldVariable_getVariableTypes_nullVariableTypes() {
|
||||
// Expect all variable types to be returned.
|
||||
// The variable does not need to be initialized to do this--it just needs a
|
||||
// pointer to the workspace.
|
||||
workspace = new Blockly.Workspace();
|
||||
workspace.createVariable('name1', 'type1');
|
||||
workspace.createVariable('name2', 'type2');
|
||||
|
||||
var fieldVariable = new Blockly.FieldVariable('name1');
|
||||
var mockBlock = fieldVariable_mockBlock(workspace);
|
||||
fieldVariable.setSourceBlock(mockBlock);
|
||||
fieldVariable.variableTypes = null;
|
||||
|
||||
var resultTypes = fieldVariable.getVariableTypes_();
|
||||
// The empty string is always one of the options.
|
||||
isEqualArrays(resultTypes, ['type1', 'type2', '']);
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_fieldVariable_getVariableTypes_emptyListVariableTypes() {
|
||||
// Expect an error to be thrown.
|
||||
workspace = new Blockly.Workspace();
|
||||
workspace.createVariable('name1', 'type1');
|
||||
workspace.createVariable('name2', 'type2');
|
||||
|
||||
var fieldVariable = new Blockly.FieldVariable('name1');
|
||||
var mockBlock = fieldVariable_mockBlock(workspace);
|
||||
fieldVariable.setSourceBlock(mockBlock);
|
||||
fieldVariable.variableTypes = [];
|
||||
|
||||
try {
|
||||
fieldVariable.getVariableTypes_();
|
||||
fail();
|
||||
} catch (e) {
|
||||
//expected
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
28
scratch-blocks/tests/jsunit/generator_test.js
Normal file
28
scratch-blocks/tests/jsunit/generator_test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2012 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_prefix() {
|
||||
var generator = new Blockly.Generator('INTERCAL');
|
||||
assertEquals('Prefix nothing.', '', generator.prefixLines('', ''));
|
||||
assertEquals('Prefix a word.', '@Hello', generator.prefixLines('Hello', '@'));
|
||||
assertEquals('Prefix one line.', '12Hello\n', generator.prefixLines('Hello\n', '12'));
|
||||
assertEquals('Prefix two lines.', '***Hello\n***World\n', generator.prefixLines('Hello\nWorld\n', '***'));
|
||||
}
|
||||
90
scratch-blocks/tests/jsunit/gesture_test.js
Normal file
90
scratch-blocks/tests/jsunit/gesture_test.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for gesture.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var e;
|
||||
var workspace;
|
||||
|
||||
|
||||
function gestureTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
e = {};
|
||||
}
|
||||
|
||||
function gestureTest_tearDown() {
|
||||
e = null;
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_gestureConstructor() {
|
||||
var gesture = new Blockly.Gesture(e, workspace);
|
||||
assertEquals(gesture.mostRecentEvent_, e);
|
||||
assertEquals(gesture.creatorWorkspace_, workspace);
|
||||
}
|
||||
|
||||
function test_gestureIsField_ClickInWorkspace() {
|
||||
gestureTest_setUp();
|
||||
var block = new Blockly.Block(workspace);
|
||||
var field = new Blockly.Field();
|
||||
field.setSourceBlock(block);
|
||||
var gesture = new Blockly.Gesture(e, workspace);
|
||||
gesture.setStartField(field);
|
||||
|
||||
var isFieldClick = gesture.isFieldClick_();
|
||||
assertEquals(isFieldClick, true);
|
||||
gestureTest_tearDown();
|
||||
}
|
||||
|
||||
function gestureIsFieldClick_InFlyoutHelper(flyout, expectedResult){
|
||||
// Assign workspace flyout
|
||||
workspace.flyout_ = flyout;
|
||||
// Create a Field inside of a Block
|
||||
var block = new Blockly.Block(workspace);
|
||||
var field = new Blockly.Field();
|
||||
field.setSourceBlock(block);
|
||||
// Create gesture from the flyout
|
||||
var gesture = new Blockly.Gesture(e, workspace.flyout_);
|
||||
// Populate gesture with click start information
|
||||
gesture.setStartField(field);
|
||||
gesture.setStartFlyout_(workspace.flyout_);
|
||||
|
||||
var isFieldClick = gesture.isFieldClick_();
|
||||
assertEquals(isFieldClick, expectedResult);
|
||||
}
|
||||
|
||||
function test_gestureIsFieldClick_AutoCloseFlyout() {
|
||||
gestureTest_setUp();
|
||||
var flyout = new Blockly.VerticalFlyout({});
|
||||
gestureIsFieldClick_InFlyoutHelper(flyout, false);
|
||||
gestureTest_tearDown();
|
||||
}
|
||||
|
||||
function test_gestureIsFieldClick_AlwaysOpenFlyout() {
|
||||
gestureTest_setUp();
|
||||
var flyout = new Blockly.VerticalFlyout({});
|
||||
flyout.autoClose = false;
|
||||
gestureIsFieldClick_InFlyoutHelper(flyout, true);
|
||||
gestureTest_tearDown();
|
||||
}
|
||||
33
scratch-blocks/tests/jsunit/horizontal_tests.html
Normal file
33
scratch-blocks/tests/jsunit/horizontal_tests.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Unit Tests for Horizontal Scratch-Blockly</title>
|
||||
<script src="../../blockly_uncompressed_horizontal.js"></script>
|
||||
<script>goog.require('goog.testing.jsunit');</script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="test_utilities.js"></script>
|
||||
<script src="block_test.js"></script>
|
||||
<script src="connection_test.js"></script>
|
||||
<script src="connection_db_test.js"></script>
|
||||
<script src="extensions_test.js"></script>
|
||||
<script src="field_test.js"></script>
|
||||
<script src="field_angle_test.js"></script>
|
||||
<script src="field_number_test.js"></script>
|
||||
<script src="generator_test.js"></script>
|
||||
<script src="input_test.js"></script>
|
||||
<script src="json_test.js"></script>
|
||||
<script src="names_test.js"></script>
|
||||
<script src="procedure_test.js"></script>
|
||||
<script src="svg_test.js"></script>
|
||||
<script src="utils_test.js"></script>
|
||||
<script src="workspace_comment_test.js"></script>
|
||||
<script src="workspace_test.js"></script>
|
||||
<script src="xml_test.js"></script>
|
||||
<script src="widget_div_test.js"></script>
|
||||
|
||||
<div id="blocklyDiv" style="display: none; height: 480px; width: 600px;"></div>
|
||||
<xml id="toolbox" style="display: none"></xml>
|
||||
</body>
|
||||
</html>
|
||||
29
scratch-blocks/tests/jsunit/index.html
Normal file
29
scratch-blocks/tests/jsunit/index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script src="test_utilities.js"></script>
|
||||
<script src="utils_test.js"></script>
|
||||
<script src="connection_test.js"></script>
|
||||
<script src="connection_db_test.js"></script>
|
||||
<script src="extensions_test.js"></script>
|
||||
<script src="event_test.js"></script>
|
||||
<script src="field_test.js"></script>
|
||||
<script src="field_angle_test.js"></script>
|
||||
<script src="field_number_test.js"></script>
|
||||
<script src="field_variable_test.js"></script>
|
||||
<script src="generator_test.js"></script>
|
||||
<script src="gesture_test.js"></script>
|
||||
<script src="input_test.js"></script>
|
||||
<script src="names_test.js"></script>
|
||||
<script src="workspace_test.js"></script>
|
||||
<script src="workspace_undo_redo_test.js"></script>
|
||||
<script src="xml_test.js"></script>
|
||||
<script src="json_test.js"></script>
|
||||
<script src="variable_model_test.js"></script>
|
||||
<script src="variable_map_test.js"></script>
|
||||
<script src="widget_div_test.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
202
scratch-blocks/tests/jsunit/input_test.js
Normal file
202
scratch-blocks/tests/jsunit/input_test.js
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.Input
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_appendField_simple() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var field1 = new Blockly.FieldLabel('#1');
|
||||
var field2 = new Blockly.FieldLabel('#2');
|
||||
|
||||
// Preconditions
|
||||
assertEquals(0, input.fieldRow.length);
|
||||
|
||||
// Actual Tests
|
||||
input.appendField(field1, 'first');
|
||||
assertEquals(1, input.fieldRow.length);
|
||||
assertEquals(field1, input.fieldRow[0]);
|
||||
assertEquals('first', input.fieldRow[0].name);
|
||||
assertEquals(block, field1.sourceBlock_);
|
||||
|
||||
input.appendField(field2, 'second');
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(field2, input.fieldRow[1]);
|
||||
assertEquals('second', input.fieldRow[1].name);
|
||||
assertEquals(block, field2.sourceBlock_);
|
||||
}
|
||||
|
||||
function test_appendField_string() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var labelText = 'label';
|
||||
|
||||
// Preconditions
|
||||
assertEquals(0, input.fieldRow.length);
|
||||
|
||||
// Actual Tests
|
||||
input.appendField(labelText, 'name');
|
||||
assertEquals(1, input.fieldRow.length);
|
||||
assertEquals(Blockly.FieldLabel, input.fieldRow[0].constructor);
|
||||
assertEquals(labelText, input.fieldRow[0].getValue());
|
||||
assertEquals('name', input.fieldRow[0].name);
|
||||
}
|
||||
|
||||
function test_appendField_prefix() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var prefix = new Blockly.FieldLabel('prefix');
|
||||
var field = new Blockly.FieldLabel('field');
|
||||
field.prefixField = prefix;
|
||||
|
||||
// Preconditions
|
||||
assertEquals(0, input.fieldRow.length);
|
||||
|
||||
// Actual Tests
|
||||
input.appendField(field);
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(prefix, input.fieldRow[0]);
|
||||
assertEquals(field, input.fieldRow[1]);
|
||||
}
|
||||
|
||||
function test_appendField_suffix() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var suffix = new Blockly.FieldLabel('suffix');
|
||||
var field = new Blockly.FieldLabel('field');
|
||||
field.suffixField = suffix;
|
||||
|
||||
// Preconditions
|
||||
assertEquals(0, input.fieldRow.length);
|
||||
|
||||
// Actual Tests
|
||||
input.appendField(field);
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(field, input.fieldRow[0]);
|
||||
assertEquals(suffix, input.fieldRow[1]);
|
||||
}
|
||||
|
||||
function test_insertFieldAt_simple() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var before = new Blockly.FieldLabel('before');
|
||||
var after = new Blockly.FieldLabel('after');
|
||||
var between = new Blockly.FieldLabel('between');
|
||||
input.appendField(before);
|
||||
input.appendField(after);
|
||||
|
||||
// Preconditions
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(after, input.fieldRow[1]);
|
||||
|
||||
// Actual Tests
|
||||
input.insertFieldAt(1, between, 'name');
|
||||
assertEquals(3, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(between, input.fieldRow[1]);
|
||||
assertEquals('name', input.fieldRow[1].name);
|
||||
assertEquals(after, input.fieldRow[2]);
|
||||
}
|
||||
|
||||
function test_insertFieldAt_string() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var before = new Blockly.FieldLabel('before');
|
||||
var after = new Blockly.FieldLabel('after');
|
||||
var labelText = 'label';
|
||||
input.appendField(before);
|
||||
input.appendField(after);
|
||||
|
||||
// Preconditions
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(after, input.fieldRow[1]);
|
||||
|
||||
// Actual Tests
|
||||
input.insertFieldAt(1, labelText, 'name');
|
||||
assertEquals(3, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(Blockly.FieldLabel, input.fieldRow[1].constructor);
|
||||
assertEquals(labelText, input.fieldRow[1].getValue());
|
||||
assertEquals('name', input.fieldRow[1].name);
|
||||
assertEquals(after, input.fieldRow[2]);
|
||||
}
|
||||
|
||||
function test_insertFieldAt_prefix() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var before = new Blockly.FieldLabel('before');
|
||||
var after = new Blockly.FieldLabel('after');
|
||||
var prefix = new Blockly.FieldLabel('prefix');
|
||||
var between = new Blockly.FieldLabel('between');
|
||||
between.prefixField = prefix
|
||||
input.appendField(before);
|
||||
input.appendField(after);
|
||||
|
||||
// Preconditions
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(after, input.fieldRow[1]);
|
||||
|
||||
// Actual Tests
|
||||
input.insertFieldAt(1, between);
|
||||
assertEquals(4, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(prefix, input.fieldRow[1]);
|
||||
assertEquals(between, input.fieldRow[2]);
|
||||
assertEquals(after, input.fieldRow[3]);
|
||||
}
|
||||
|
||||
function test_insertFieldAt_prefix() {
|
||||
var ws = new Blockly.Workspace();
|
||||
var block = new Blockly.Block(ws);
|
||||
var input = new Blockly.Input(Blockly.DUMMY_INPUT, 'INPUT', block);
|
||||
var before = new Blockly.FieldLabel('before');
|
||||
var after = new Blockly.FieldLabel('after');
|
||||
var suffix = new Blockly.FieldLabel('suffix');
|
||||
var between = new Blockly.FieldLabel('between');
|
||||
between.suffixField = suffix
|
||||
input.appendField(before);
|
||||
input.appendField(after);
|
||||
|
||||
// Preconditions
|
||||
assertEquals(2, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(after, input.fieldRow[1]);
|
||||
|
||||
// Actual Tests
|
||||
input.insertFieldAt(1, between);
|
||||
assertEquals(4, input.fieldRow.length);
|
||||
assertEquals(before, input.fieldRow[0]);
|
||||
assertEquals(between, input.fieldRow[1]);
|
||||
assertEquals(suffix, input.fieldRow[2]);
|
||||
assertEquals(after, input.fieldRow[3]);
|
||||
}
|
||||
260
scratch-blocks/tests/jsunit/json_test.js
Normal file
260
scratch-blocks/tests/jsunit/json_test.js
Normal file
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/** Ensure a block can be instantiated from a JSON definition. */
|
||||
function test_json_minimal() {
|
||||
var BLOCK_TYPE = 'test_json_minimal';
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(BLOCK_TYPE, block.type);
|
||||
// TODO: asserts
|
||||
} finally {
|
||||
block.dispose();
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure message0 creates an input. */
|
||||
function test_json_message0() {
|
||||
var BLOCK_TYPE = 'test_json_message0';
|
||||
var MESSAGE0 = 'message0';
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE,
|
||||
"message0": MESSAGE0
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(1, block.inputList.length);
|
||||
assertEquals(1, block.inputList[0].fieldRow.length);
|
||||
var textField = block.inputList[0].fieldRow[0];
|
||||
assertEquals(Blockly.FieldLabel, textField.constructor);
|
||||
assertEquals(MESSAGE0, textField.getText());
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure message1 creates a new input. */
|
||||
function test_json_message1() {
|
||||
var BLOCK_TYPE = 'test_json_message1';
|
||||
var MESSAGE0 = 'message0';
|
||||
var MESSAGE1 = 'message1';
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE,
|
||||
"message0": MESSAGE0,
|
||||
"message1": MESSAGE1
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(2, block.inputList.length);
|
||||
|
||||
assertEquals(1, block.inputList[0].fieldRow.length);
|
||||
var textField = block.inputList[0].fieldRow[0];
|
||||
assertEquals(Blockly.FieldLabel, textField.constructor);
|
||||
assertEquals(MESSAGE0, textField.getText());
|
||||
|
||||
assertEquals(1, block.inputList[1].fieldRow.length);
|
||||
var textField = block.inputList[1].fieldRow[0];
|
||||
assertEquals(Blockly.FieldLabel, textField.constructor);
|
||||
assertEquals(MESSAGE1, textField.getText());
|
||||
} finally {
|
||||
block && block.dispose();
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure message string is dereferenced. */
|
||||
function test_json_message0_i18n() {
|
||||
var BLOCK_TYPE = 'test_json_message0_i18n';
|
||||
var MESSAGE0 = '%{BKY_MESSAGE}';
|
||||
var MESSAGE = 'message';
|
||||
|
||||
Blockly.Msg['MESSAGE'] = MESSAGE;
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE,
|
||||
"message0": MESSAGE0
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(1, block.inputList.length);
|
||||
assertEquals(1, block.inputList[0].fieldRow.length);
|
||||
var textField = block.inputList[0].fieldRow[0];
|
||||
assertEquals(Blockly.FieldLabel, textField.constructor);
|
||||
assertEquals(MESSAGE, textField.getText());
|
||||
} finally {
|
||||
block && block.dispose(); // Disposes of textField, too.
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
delete Blockly.Msg['MESSAGE'];
|
||||
}
|
||||
}
|
||||
|
||||
function test_json_dropdown() {
|
||||
var BLOCK_TYPE = 'test_json_dropdown';
|
||||
var FIELD_NAME = 'FIELD_NAME';
|
||||
var LABEL0 = 'LABEL0';
|
||||
var VALUE0 = 'VALUE0';
|
||||
var LABEL1 = 'LABEL1';
|
||||
var VALUE1 = 'VALUE1';
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE,
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": FIELD_NAME,
|
||||
"options": [
|
||||
[LABEL0, VALUE0],
|
||||
[LABEL1, VALUE1]
|
||||
]
|
||||
}
|
||||
]
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(1, block.inputList.length);
|
||||
assertEquals(1, block.inputList[0].fieldRow.length);
|
||||
var dropdown = block.inputList[0].fieldRow[0];
|
||||
assertEquals(dropdown, block.getField(FIELD_NAME));
|
||||
assertEquals(Blockly.FieldDropdown, dropdown.constructor);
|
||||
assertEquals(VALUE0, dropdown.getValue());
|
||||
|
||||
var options = dropdown.getOptions();
|
||||
assertEquals(LABEL0, options[0][0]);
|
||||
assertEquals(VALUE0, options[0][1]);
|
||||
assertEquals(LABEL1, options[1][0]);
|
||||
assertEquals(VALUE1, options[1][1]);
|
||||
} finally {
|
||||
block && block.dispose(); // Disposes of dropdown, too.
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
}
|
||||
}
|
||||
|
||||
function test_json_dropdown_image() {
|
||||
var BLOCK_TYPE = 'test_json_dropdown';
|
||||
var FIELD_NAME = 'FIELD_NAME';
|
||||
var IMAGE1_ALT_TEXT = 'Localized message.';
|
||||
Blockly.Msg['ALT_TEXT'] = IMAGE1_ALT_TEXT;
|
||||
var IMAGE0 = {
|
||||
'width': 12,
|
||||
'height': 34,
|
||||
'src': 'http://image0.src',
|
||||
'alt': 'IMAGE0 alt text'
|
||||
};
|
||||
var VALUE0 = 'VALUE0';
|
||||
var IMAGE1 = {
|
||||
'width': 56,
|
||||
'height': 78,
|
||||
'src': 'http://image1.src',
|
||||
'alt': '%{BKY_ALT_TEXT}'
|
||||
};
|
||||
var VALUE1 = 'VALUE1';
|
||||
var IMAGE2 = {
|
||||
'width': 90,
|
||||
'height': 123,
|
||||
'src': 'http://image2.src'
|
||||
};
|
||||
var VALUE2 = 'VALUE2';
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
var block;
|
||||
try {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE,
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": FIELD_NAME,
|
||||
"options": [
|
||||
[IMAGE0, VALUE0],
|
||||
[IMAGE1, VALUE1],
|
||||
[IMAGE2, VALUE2]
|
||||
]
|
||||
}
|
||||
]
|
||||
}]);
|
||||
|
||||
block = new Blockly.Block(workspace, BLOCK_TYPE);
|
||||
assertEquals(1, block.inputList.length);
|
||||
assertEquals(1, block.inputList[0].fieldRow.length);
|
||||
var dropdown = block.inputList[0].fieldRow[0];
|
||||
assertEquals(dropdown, block.getField(FIELD_NAME));
|
||||
assertEquals(Blockly.FieldDropdown, dropdown.constructor);
|
||||
assertEquals(VALUE0, dropdown.getValue());
|
||||
|
||||
var options = dropdown.getOptions();
|
||||
var image0 = options[0][0];
|
||||
assertEquals(IMAGE0.width, image0.width);
|
||||
assertEquals(IMAGE0.height, image0.height);
|
||||
assertEquals(IMAGE0.src, image0.src);
|
||||
assertEquals(IMAGE0.alt, image0.alt);
|
||||
assertEquals(VALUE0, options[0][1]);
|
||||
|
||||
var image1 = options[1][0];
|
||||
assertEquals(IMAGE1.width, image1.width);
|
||||
assertEquals(IMAGE1.height, image1.height);
|
||||
assertEquals(IMAGE1.src, image1.src);
|
||||
assertEquals(IMAGE1.alt, IMAGE1_ALT_TEXT); // Via Msg reference
|
||||
assertEquals(VALUE1, options[1][1]);
|
||||
|
||||
var image2 = options[2][0];
|
||||
assertEquals(IMAGE2.width, image2.width);
|
||||
assertEquals(IMAGE2.height, image2.height);
|
||||
assertEquals(IMAGE2.src, image2.src);
|
||||
assert(image2.alt == null); // No alt specified.
|
||||
assertEquals(VALUE2, options[2][1]);
|
||||
} finally {
|
||||
block && block.dispose(); // Disposes of dropdown, too.
|
||||
workspace.dispose();
|
||||
delete Blockly.Blocks[BLOCK_TYPE];
|
||||
delete Blockly.Msg['ALTTEXT'];
|
||||
}
|
||||
}
|
||||
|
||||
62
scratch-blocks/tests/jsunit/names_test.js
Normal file
62
scratch-blocks/tests/jsunit/names_test.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2012 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_safeName() {
|
||||
var varDB = new Blockly.Names('window,door');
|
||||
assertEquals('SafeName empty.', 'unnamed', varDB.safeName_(''));
|
||||
assertEquals('SafeName ok.', 'foobar', varDB.safeName_('foobar'));
|
||||
assertEquals('SafeName number start.', 'my_9lives',
|
||||
varDB.safeName_('9lives'));
|
||||
assertEquals('SafeName number end.', 'lives9', varDB.safeName_('lives9'));
|
||||
assertEquals('SafeName special chars.', '____', varDB.safeName_('!@#$'));
|
||||
assertEquals('SafeName reserved.', 'door', varDB.safeName_('door'));
|
||||
}
|
||||
|
||||
function test_getName() {
|
||||
var varDB = new Blockly.Names('window,door');
|
||||
assertEquals('Name add #1.', 'Foo_bar', varDB.getName('Foo.bar', 'var'));
|
||||
assertEquals('Name get #1.', 'Foo_bar', varDB.getName('Foo.bar', 'var'));
|
||||
assertEquals('Name add #2.', 'Foo_bar2', varDB.getName('Foo bar', 'var'));
|
||||
assertEquals('Name get #2.', 'Foo_bar2', varDB.getName('foo BAR', 'var'));
|
||||
assertEquals('Name add #3.', 'door2', varDB.getName('door', 'var'));
|
||||
assertEquals('Name add #4.', 'Foo_bar3', varDB.getName('Foo.bar', 'proc'));
|
||||
assertEquals('Name get #1b.', 'Foo_bar', varDB.getName('Foo.bar', 'var'));
|
||||
assertEquals('Name get #4.', 'Foo_bar3', varDB.getName('Foo.bar', 'proc'));
|
||||
}
|
||||
|
||||
function test_getDistinctName() {
|
||||
var varDB = new Blockly.Names('window,door');
|
||||
assertEquals('Name distinct #1.', 'Foo_bar',
|
||||
varDB.getDistinctName('Foo.bar', 'var'));
|
||||
assertEquals('Name distinct #2.', 'Foo_bar2',
|
||||
varDB.getDistinctName('Foo.bar', 'var'));
|
||||
assertEquals('Name distinct #3.', 'Foo_bar3',
|
||||
varDB.getDistinctName('Foo.bar', 'proc'));
|
||||
varDB.reset();
|
||||
assertEquals('Name distinct #4.', 'Foo_bar',
|
||||
varDB.getDistinctName('Foo.bar', 'var'));
|
||||
}
|
||||
|
||||
function test_nameEquals() {
|
||||
assertTrue('Name equals #1.', Blockly.Names.equals('Foo.bar', 'Foo.bar'));
|
||||
assertFalse('Name equals #2.', Blockly.Names.equals('Foo.bar', 'Foo_bar'));
|
||||
assertTrue('Name equals #3.', Blockly.Names.equals('Foo.bar', 'FOO.BAR'));
|
||||
}
|
||||
341
scratch-blocks/tests/jsunit/procedure_test.js
Normal file
341
scratch-blocks/tests/jsunit/procedure_test.js
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var workspace;
|
||||
//var mockControl_;
|
||||
|
||||
function procedureTest_setUp() {
|
||||
Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE] = {
|
||||
init: function() {
|
||||
this.procCode_ = '';
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
},
|
||||
getProcCode: function() {
|
||||
return this.procCode_;
|
||||
}
|
||||
};
|
||||
Blockly.Blocks['foo'] = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": "foo",
|
||||
"previousStatement": null,
|
||||
"nextStatement": null
|
||||
});
|
||||
this.setNextStatement(true);
|
||||
}
|
||||
};
|
||||
Blockly.Blocks['loop'] = {
|
||||
init: function() {
|
||||
this.jsonInit({ message0: 'forever %1',
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_statement",
|
||||
"name": "SUBSTACK"
|
||||
}
|
||||
]});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
workspace = new Blockly.Workspace();
|
||||
//mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function procedureTest_tearDown() {
|
||||
delete Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE];
|
||||
delete Blockly.Blocks['foo'];
|
||||
delete Blockly.Blocks['loop'];
|
||||
//mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_findCallers_simple_oneCaller() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<variables></variables>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516">' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
assertEquals(1, callers.length);
|
||||
assertEquals('test_1', callers[0].id);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_noRecursion() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<variables></variables>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516">' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var rootBlock = workspace.getBlockById('test_1');
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
rootBlock, false /* allowRecursion */);
|
||||
|
||||
// There was a single call to this function, but it was in a stack that
|
||||
// should be ignored.
|
||||
assertEquals(0, callers.length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_allowRecursion() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<variables></variables>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516">' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var rootBlock = workspace.getBlockById('test_1');
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
rootBlock, true /* allowRecursion */);
|
||||
|
||||
// There was a single call to this function, in the same stack as the
|
||||
// definition root. Recursion is allowed, so it should be found.
|
||||
assertEquals(1, callers.length);
|
||||
assertEquals('test_1', callers[0].id);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_simple_noCallers() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<variables></variables>' +
|
||||
'<block type="foo" id="test_1" x="301" y="516">' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
|
||||
// There weren't even blocks of type procedures_callnoreturn.
|
||||
assertEquals(0, callers.length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_wrongProcCode() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<variables></variables>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516">' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'wrong procedure';
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
|
||||
// There was a procedure_callnoreturn call, but it had the wrong procCode.
|
||||
assertEquals(0, callers.length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_onStatementInput() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="loop" id="test_1">' +
|
||||
'<statement name="SUBSTACK">' +
|
||||
'<block type="foo" id="test_2">' +
|
||||
'<next>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_3"></block>' +
|
||||
'</next></block>' +
|
||||
'</statement>' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_3').procCode_ = 'test_procedure';
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
|
||||
// There should be one caller, connected to a stack on a statement input.
|
||||
assertEquals(1, callers.length);
|
||||
assertEquals('test_3', callers[0].id);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_multipleStacks() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="foo" id="test_1"></block>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_2"></block>'+
|
||||
'<block type="foo" id="test_1"></block>' +
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_2').procCode_ = 'test_procedure';
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
|
||||
// There should be one caller, but multiple stacks in the workspace.
|
||||
assertEquals(1, callers.length);
|
||||
assertEquals('test_2', callers[0].id);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_findCallers_multipleCallers() {
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1"></block>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_2"></block>'+
|
||||
'</xml>';
|
||||
procedureTest_setUp();
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_2').procCode_ = 'test_procedure';
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var callers = Blockly.Procedures.getCallers('test_procedure', workspace,
|
||||
{id: ''}, false);
|
||||
|
||||
// There should be two callers, on two different stacks.
|
||||
assertEquals(2, callers.length);
|
||||
// Order doesn't matter.
|
||||
assertTrue(callers[0].id == 'test_1' || callers[0].id == 'test_2');
|
||||
assertTrue(callers[1].id == 'test_1' || callers[1].id == 'test_2');
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_deleteProcedure_noCallers() {
|
||||
// If there are no callers, the stack should be deleted.
|
||||
procedureTest_setUp();
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516"></block>' +
|
||||
'<block type="foo" id="test_2"></block>' +
|
||||
'<block type="foo" id="test_3"></block>' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var rootBlock = workspace.getBlockById('test_1');
|
||||
assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure',
|
||||
rootBlock));
|
||||
// The other two blocks should stick around.
|
||||
assertEquals(2, workspace.getTopBlocks().length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_deleteProcedure_recursiveCaller() {
|
||||
// If there is a caller but it's a part of stack starting with definitionRoot,
|
||||
// the stack should be deleted.
|
||||
|
||||
procedureTest_setUp();
|
||||
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="loop" id="test_1">' +
|
||||
'<statement name="SUBSTACK">' +
|
||||
'<block type="foo" id="test_2">' +
|
||||
'<next>' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_3"></block>' +
|
||||
'</next></block>' +
|
||||
'</statement>' +
|
||||
'</block>' +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_3').procCode_ = 'test_procedure';
|
||||
var rootBlock = workspace.getBlockById('test_1');
|
||||
assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure',
|
||||
rootBlock));
|
||||
assertEquals(0, workspace.getTopBlocks().length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_deleteProcedure_nonRecursiveCaller() {
|
||||
// If there is a caller and it's not part of the procedure definition, the
|
||||
// stack should not be deleted.
|
||||
|
||||
procedureTest_setUp();
|
||||
var xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
'<block type="' + Blockly.PROCEDURES_CALL_BLOCK_TYPE +
|
||||
'" id="test_1" x="301" y="516"></block>' +
|
||||
'<block type="foo" id="test_2"></block>' +
|
||||
'<block type="foo" id="test_3"></block>' +
|
||||
'</xml>';
|
||||
try {
|
||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
||||
workspace.getBlockById('test_1').procCode_ = 'test_procedure';
|
||||
var rootBlock = workspace.getBlockById('test_2');
|
||||
assertFalse(Blockly.Procedures.deleteProcedureDefCallback('test_procedure',
|
||||
rootBlock));
|
||||
// All blocks should stay on the workspace.
|
||||
assertEquals(3, workspace.getTopBlocks().length);
|
||||
}
|
||||
finally {
|
||||
procedureTest_tearDown();
|
||||
}
|
||||
}
|
||||
177
scratch-blocks/tests/jsunit/scratch_block_comment_test.js
Normal file
177
scratch-blocks/tests/jsunit/scratch_block_comment_test.js
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
|
||||
var block;
|
||||
var workspace;
|
||||
var originalCreateIcon = Blockly.ScratchBlockComment.createIcon;
|
||||
|
||||
function scratchBlockCommentTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
// Mock the resizeContents function on the workspace
|
||||
workspace.resizeContents = function () {};
|
||||
|
||||
var BLOCK_TYPE = 'test_json_minimal';
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": BLOCK_TYPE
|
||||
}]);
|
||||
|
||||
block = new Blockly.BlockSvg(workspace, BLOCK_TYPE);
|
||||
|
||||
// Mock createIcon function since it is not necessary for these tests
|
||||
Blockly.ScratchBlockComment.prototype.createIcon = function () {};
|
||||
}
|
||||
|
||||
function scratchBlockCommentTest_tearDown() {
|
||||
workspace.dispose();
|
||||
block.dispose();
|
||||
|
||||
// Restore original createIcon function
|
||||
Blockly.ScratchBlockComment.prototype.createIcon = originalCreateIcon;
|
||||
}
|
||||
|
||||
function test_blockWithNoBlockComments() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
assertEquals('Workspace has a block.', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Workspace does not have a comment.', 0, workspace.getTopComments(false).length);
|
||||
assertEquals('Block does not have a comment', null, block.comment);
|
||||
assertEquals('Block does not have comment text', '', block.getCommentText());
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_createBlockCommentMinimalArguments() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(block, 'Some comment text');
|
||||
assertEquals('Workspace has a block.', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Workspace has a comment.', 1, workspace.getTopComments(false).length);
|
||||
assertEquals('Comment knows about workspace.', workspace, comment.workspace);
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_createBlockCommentAllArguments() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(block, 'Some comment text', 'aMockComment', 10, 20, true);
|
||||
assertEquals('Workspace has a block.', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Workspace has a comment.', 1, workspace.getTopComments(false).length);
|
||||
assertEquals('Comment knows about workspace.', workspace, comment.workspace);
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_addCommentToBlock() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
block.setCommentText('Some comment text', 'aMockComment');
|
||||
assertEquals('Workspace has a block.', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Workspace has a comment.', 1, workspace.getTopComments(false).length);
|
||||
assertNotEquals('Block has a comment', null, block.comment);
|
||||
assertEquals('Block has comment text', 'Some comment text', block.getCommentText());
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockCommentXYWhenPositionProvided() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(block, 'Some comment text', 'aMockComment', 10, 20);
|
||||
var commentXY = comment.getXY();
|
||||
var commentX = commentXY.x;
|
||||
var commentY = commentXY.y;
|
||||
assertEquals('Comment x position is type number', 'number', typeof commentX);
|
||||
assertEquals('Comment y position is type number', 'number', typeof commentY);
|
||||
|
||||
assertEquals('Comment x position is what was provided', 10, commentX);
|
||||
assertEquals('Comment y position is what was provided', 20, commentY);
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockCommentXYWhenPositionNotProvided() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(
|
||||
block, 'Some comment text', 'aMockComment');
|
||||
var commentXY = comment.getXY();
|
||||
var commentX = commentXY.x;
|
||||
var commentY = commentXY.y;
|
||||
|
||||
console.log("COMMENT X: " + commentX);
|
||||
console.log("COMMENT Y: " + commentY);
|
||||
|
||||
assertEquals('Comment x position is type number', 'number', typeof commentX);
|
||||
assertEquals('Comment y position is type number', 'number', typeof commentY);
|
||||
|
||||
assertFalse('Comment x position is not NaN', isNaN(commentX));
|
||||
assertFalse('Comment y position is not NaN', isNaN(commentY));
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockCommentXYNaNPositionProvided() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(
|
||||
block, 'Some comment text', 'aMockComment', NaN, NaN);
|
||||
var commentXY = comment.getXY();
|
||||
var commentX = commentXY.x;
|
||||
var commentY = commentXY.y;
|
||||
|
||||
assertEquals('Comment x position is type number', 'number', typeof commentX);
|
||||
assertEquals('Comment y position is type number', 'number', typeof commentY);
|
||||
|
||||
assertFalse('Comment x position is not NaN', isNaN(commentX));
|
||||
assertFalse('Comment y position is not NaN', isNaN(commentY));
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockCommentXYNullPositionProvided() {
|
||||
scratchBlockCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.ScratchBlockComment(
|
||||
block, 'Some comment text', 'aMockComment', null, null);
|
||||
var commentXY = comment.getXY();
|
||||
var commentX = commentXY.x;
|
||||
var commentY = commentXY.y;
|
||||
|
||||
assertEquals('Comment x position is type number', 'number', typeof commentX);
|
||||
assertEquals('Comment y position is type number', 'number', typeof commentY);
|
||||
|
||||
assertFalse('Comment x position is not NaN', isNaN(commentX));
|
||||
assertFalse('Comment y position is not NaN', isNaN(commentY));
|
||||
} finally {
|
||||
scratchBlockCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
82
scratch-blocks/tests/jsunit/svg_test.js
Normal file
82
scratch-blocks/tests/jsunit/svg_test.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var svgTest_workspace;
|
||||
|
||||
function svgTest_setUp() {
|
||||
svgTest_workspace = Blockly.inject('blocklyDiv',
|
||||
{toolbox: document.getElementById('toolbox')});
|
||||
}
|
||||
|
||||
function svgTest_tearDown() {
|
||||
svgTest_workspace.dispose();
|
||||
svgTest_workspace = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a block with one field. Must be called after svgTest_setUp().
|
||||
* @return {!Blockly.Block} The new block with one field.
|
||||
*/
|
||||
function svgTest_newOneFieldBlock() {
|
||||
Blockly.Blocks['one_field_block'] = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
'message0': '%1',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_input',
|
||||
'name': 'FIELD'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var block = svgTest_workspace.newBlock('one_field_block');
|
||||
block.initSvg();
|
||||
block.render(false);
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a block with two fields. Must be called after svgTest_setUp().
|
||||
* @return {!Blockly.Block} The new block with two fields.
|
||||
*/
|
||||
function svgTest_newTwoFieldBlock() {
|
||||
Blockly.Blocks['two_field_block'] = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
'message0': 'text_field %1',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_input',
|
||||
'name': 'FIELD'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var block = svgTest_workspace.newBlock('two_field_block');
|
||||
block.initSvg();
|
||||
block.render(false);
|
||||
return block;
|
||||
}
|
||||
60
scratch-blocks/tests/jsunit/test_runner.js
Normal file
60
scratch-blocks/tests/jsunit/test_runner.js
Normal file
@@ -0,0 +1,60 @@
|
||||
require('chromedriver');
|
||||
var webdriver = require('selenium-webdriver');
|
||||
var chrome = require('selenium-webdriver/chrome');
|
||||
var builder = new webdriver.Builder().forBrowser('chrome');
|
||||
|
||||
if (process.env.CI) {
|
||||
const options = new chrome.Options().headless();
|
||||
if (process.platform === 'linux') {
|
||||
options.addArguments('no-sandbox');
|
||||
}
|
||||
builder.setChromeOptions(options);
|
||||
}
|
||||
|
||||
var browser = builder.build();
|
||||
|
||||
// Parse jsunit html report, exit(1) if there are any failures.
|
||||
var testHtml = function (htmlString) {
|
||||
var regex = /[\d]+\spassed,\s([\d]+)\sfailed./i;
|
||||
var numOfFailure = regex.exec(htmlString)[1];
|
||||
var regex2 = /Unit Tests for .*]/;
|
||||
var testStatus = regex2.exec(htmlString)[0];
|
||||
console.log("============Unit Test Summary=================");
|
||||
console.log(testStatus);
|
||||
var regex3 = /\d+ passed,\s\d+ failed/;
|
||||
var detail = regex3.exec(htmlString)[0];
|
||||
console.log(detail);
|
||||
console.log("============Unit Test Summary=================");
|
||||
if (parseInt(numOfFailure) !== 0) {
|
||||
console.log(htmlString);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
var path = process.cwd();
|
||||
|
||||
var runTests = async function () {
|
||||
try {
|
||||
var element, text;
|
||||
|
||||
await browser.get("file://" + path + "/tests/jsunit/vertical_tests.html");
|
||||
await browser.sleep(5000);
|
||||
element = await browser.findElement({id: "closureTestRunnerLog"});
|
||||
text = await element.getText();
|
||||
testHtml(text);
|
||||
|
||||
await browser.get("file://" + path + "/tests/jsunit/horizontal_tests.html");
|
||||
await browser.sleep(5000);
|
||||
element = await browser.findElement({id: "closureTestRunnerLog"});
|
||||
text = await element.getText();
|
||||
testHtml(text);
|
||||
}
|
||||
finally {
|
||||
await browser.quit();
|
||||
}
|
||||
};
|
||||
|
||||
runTests().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
159
scratch-blocks/tests/jsunit/test_utilities.js
Normal file
159
scratch-blocks/tests/jsunit/test_utilities.js
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Test utilities.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
|
||||
|
||||
/**
|
||||
* The normal blockly event fire function. We sometimes override this. This
|
||||
* handle lets us reset after an override.
|
||||
*/
|
||||
var savedFireFunc = Blockly.Events.fire;
|
||||
|
||||
/**
|
||||
* A helper function to replace Blockly.Events.fire in tests.
|
||||
*/
|
||||
function temporary_fireEvent(event) {
|
||||
if (!Blockly.Events.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
Blockly.Events.FIRE_QUEUE_.push(event);
|
||||
Blockly.Events.fireNow_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that two arrays have the same content.
|
||||
* @param {!Array.<string>} array1 The first array.
|
||||
* @param {!Array.<string>} array2 The second array.
|
||||
*/
|
||||
function isEqualArrays(array1, array2) {
|
||||
assertEquals(array1.length, array2.length);
|
||||
for (var i = 0; i < array1.length; i++) {
|
||||
assertEquals(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a controlled MethodMock. Sets the expected return values and
|
||||
* the parameters if any exist. Sets the method to replay.
|
||||
* @param {!goog.testing.MockControl} mockControl Object that holds a set
|
||||
* of mocks for this test.
|
||||
* @param {!Object} scope The scope of the method to be mocked out.
|
||||
* @param {!string} funcName The name of the function we're going to mock.
|
||||
* @param {Array<Object>} parameters The parameters to call the mock with.
|
||||
* @param {Array<!Object>} return_values The values to return when called.
|
||||
* @return {!goog.testing.MockInterface} The mocked method.
|
||||
*/
|
||||
function setUpMockMethod(mockControl, scope, funcName, parameters,
|
||||
return_values) {
|
||||
var mockMethod = mockControl.createMethodMock(scope, funcName);
|
||||
if (return_values) {
|
||||
for (var i = 0, return_value; return_value = return_values[i]; i++) {
|
||||
if (parameters && i < parameters.length) {
|
||||
mockMethod(parameters[i]).$returns(return_value);
|
||||
}
|
||||
else {
|
||||
mockMethod().$returns(return_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no return values but there are parameters, we are only
|
||||
// recording specific method calls.
|
||||
else if (parameters) {
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
mockMethod(parameters[i]);
|
||||
}
|
||||
}
|
||||
mockMethod.$replay();
|
||||
return mockMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a variable with the given values exists.
|
||||
* @param {Blockly.Workspace|Blockly.VariableMap} container The workspace or
|
||||
* variableMap the checked variable belongs to.
|
||||
* @param {!string} name The expected name of the variable.
|
||||
* @param {!string} type The expected type of the variable.
|
||||
* @param {!string} id The expected id of the variable.
|
||||
*/
|
||||
function checkVariableValues(container, name, type, id) {
|
||||
var variable = container.getVariableById(id);
|
||||
assertNotUndefined(variable);
|
||||
assertEquals(name, variable.name);
|
||||
assertEquals(type, variable.type);
|
||||
assertEquals(id, variable.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test get_var_block.
|
||||
* Will fail if get_var_block isn't defined.
|
||||
* TODO (fenichel): Rename to createMockVarBlock.
|
||||
* @param {!string} variable_id The id of the variable to reference.
|
||||
* @return {!Blockly.Block} The created block.
|
||||
*/
|
||||
function createMockBlock(variable_id) {
|
||||
if (!Blockly.Blocks['get_var_block']) {
|
||||
fail();
|
||||
}
|
||||
// Turn off events to avoid testing XML at the same time.
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'get_var_block');
|
||||
block.inputList[0].fieldRow[0].setValue(variable_id);
|
||||
Blockly.Events.enable();
|
||||
return block;
|
||||
}
|
||||
|
||||
function createTwoVariablesAndBlocks(workspace) {
|
||||
// Create two variables of different types.
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.createVariable('name2', 'type2', 'id2');
|
||||
// Create blocks to refer to both of them.
|
||||
createMockBlock('id1');
|
||||
createMockBlock('id2');
|
||||
}
|
||||
|
||||
function createVariableAndBlock(workspace) {
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
createMockBlock('id1');
|
||||
}
|
||||
|
||||
function defineGetVarBlock() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "get_var_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variableTypes": ["", "type1", "type2"]
|
||||
}
|
||||
]
|
||||
}]);
|
||||
}
|
||||
|
||||
function undefineGetVarBlock() {
|
||||
delete Blockly.Blocks['get_var_block'];
|
||||
}
|
||||
165
scratch-blocks/tests/jsunit/utils_test.js
Normal file
165
scratch-blocks/tests/jsunit/utils_test.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2011 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_genUid() {
|
||||
var uuids = {};
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var uuid = Blockly.utils.genUid();
|
||||
assertFalse('UUID different: ' + uuid, uuid in uuids);
|
||||
uuids[uuid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function test_addClass() {
|
||||
var p = document.createElement('p');
|
||||
Blockly.utils.addClass(p, 'one');
|
||||
assertEquals('Adding "one"', 'one', p.className);
|
||||
Blockly.utils.addClass(p, 'one');
|
||||
assertEquals('Adding duplicate "one"', 'one', p.className);
|
||||
Blockly.utils.addClass(p, 'two');
|
||||
assertEquals('Adding "two"', 'one two', p.className);
|
||||
Blockly.utils.addClass(p, 'two');
|
||||
assertEquals('Adding duplicate "two"', 'one two', p.className);
|
||||
Blockly.utils.addClass(p, 'three');
|
||||
assertEquals('Adding "three"', 'one two three', p.className);
|
||||
}
|
||||
|
||||
function test_hasClass() {
|
||||
var p = document.createElement('p');
|
||||
p.className = ' one three two three ';
|
||||
assertTrue('Has "one"', Blockly.utils.hasClass(p, 'one'));
|
||||
assertTrue('Has "two"', Blockly.utils.hasClass(p, 'two'));
|
||||
assertTrue('Has "three"', Blockly.utils.hasClass(p, 'three'));
|
||||
assertFalse('Has no "four"', Blockly.utils.hasClass(p, 'four'));
|
||||
assertFalse('Has no "t"', Blockly.utils.hasClass(p, 't'));
|
||||
}
|
||||
|
||||
function test_removeClass() {
|
||||
var p = document.createElement('p');
|
||||
p.className = ' one three two three ';
|
||||
Blockly.utils.removeClass(p, 'two');
|
||||
assertEquals('Removing "two"', 'one three three', p.className);
|
||||
Blockly.utils.removeClass(p, 'four');
|
||||
assertEquals('Removing "four"', 'one three three', p.className);
|
||||
Blockly.utils.removeClass(p, 'three');
|
||||
assertEquals('Removing "three"', 'one', p.className);
|
||||
Blockly.utils.removeClass(p, 'ne');
|
||||
assertEquals('Removing "ne"', 'one', p.className);
|
||||
Blockly.utils.removeClass(p, 'one');
|
||||
assertEquals('Removing "one"', '', p.className);
|
||||
Blockly.utils.removeClass(p, 'zero');
|
||||
assertEquals('Removing "zero"', '', p.className);
|
||||
}
|
||||
|
||||
function test_tokenizeInterpolation() {
|
||||
var tokens = Blockly.utils.tokenizeInterpolation('');
|
||||
assertArrayEquals('Null interpolation', [], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('Hello');
|
||||
assertArrayEquals('No interpolation', ['Hello'], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('Hello%World');
|
||||
assertArrayEquals('Unescaped %.', ['Hello%World'], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('Hello%%World');
|
||||
assertArrayEquals('Escaped %.', ['Hello%World'], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('Hello %1 World');
|
||||
assertArrayEquals('Interpolation.', ['Hello ', 1, ' World'], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%123Hello%456World%789');
|
||||
assertArrayEquals('Interpolations.', [123, 'Hello', 456, 'World', 789], tokens);
|
||||
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%%%x%%0%00%01%');
|
||||
assertArrayEquals('Torture interpolations.', ['%%x%0', 0, 1, '%'], tokens);
|
||||
|
||||
Blockly.Msg = Blockly.Msg || {};
|
||||
|
||||
Blockly.Msg.STRING_REF = 'test string';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%{bky_string_ref}');
|
||||
assertArrayEquals('String table reference, lowercase', ['test string'], tokens);
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%{BKY_STRING_REF}');
|
||||
assertArrayEquals('String table reference, uppercase', ['test string'], tokens);
|
||||
|
||||
Blockly.Msg.WITH_PARAM = 'before %1 after';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%{bky_with_param}');
|
||||
assertArrayEquals('String table reference, with parameter', ['before ', 1, ' after'], tokens);
|
||||
|
||||
Blockly.Msg.RECURSE = 'before %{bky_string_ref} after';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%{bky_recurse}');
|
||||
assertArrayEquals('String table reference, with subreference', ['before test string after'], tokens);
|
||||
|
||||
// Error cases...
|
||||
tokens = Blockly.utils.tokenizeInterpolation('%{bky_undefined}');
|
||||
assertArrayEquals('Undefined string table reference', ['%{bky_undefined}'], tokens);
|
||||
|
||||
Blockly.Msg['1'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{1} after');
|
||||
assertArrayEquals('Invalid initial digit in string table reference', ['before %{1} after'], tokens);
|
||||
|
||||
Blockly.Msg['TWO WORDS'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{two words} after');
|
||||
assertArrayEquals('Invalid character in string table reference: space', ['before %{two words} after'], tokens);
|
||||
|
||||
Blockly.Msg['TWO-WORDS'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{two-words} after');
|
||||
assertArrayEquals('Invalid character in string table reference: dash', ['before %{two-words} after'], tokens);
|
||||
|
||||
Blockly.Msg['TWO.WORDS'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{two.words} after');
|
||||
assertArrayEquals('Invalid character in string table reference: period', ['before %{two.words} after'], tokens);
|
||||
|
||||
Blockly.Msg['AB&C'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{ab&c} after');
|
||||
assertArrayEquals('Invalid character in string table reference: &', ['before %{ab&c} after'], tokens);
|
||||
|
||||
Blockly.Msg['UNCLOSED'] = 'Will not match';
|
||||
tokens = Blockly.utils.tokenizeInterpolation('before %{unclosed');
|
||||
assertArrayEquals('String table reference, with parameter', ['before %{unclosed'], tokens);
|
||||
}
|
||||
|
||||
function test_replaceMessageReferences() {
|
||||
Blockly.Msg = Blockly.Msg || {};
|
||||
Blockly.Msg.STRING_REF = 'test string';
|
||||
|
||||
var resultString = Blockly.utils.replaceMessageReferences('');
|
||||
assertEquals('Empty string produces empty string', '', resultString);
|
||||
|
||||
resultString = Blockly.utils.replaceMessageReferences('%{bky_string_ref}');
|
||||
assertEquals('Message ref dereferenced.', 'test string', resultString);
|
||||
resultString = Blockly.utils.replaceMessageReferences('before %{bky_string_ref} after');
|
||||
assertEquals('Message ref dereferenced.', 'before test string after', resultString);
|
||||
|
||||
resultString = Blockly.utils.replaceMessageReferences('%1');
|
||||
assertEquals('Interpolation tokens ignored.', '%1', resultString);
|
||||
resultString = Blockly.utils.replaceMessageReferences('%1 %2');
|
||||
assertEquals('Interpolation tokens ignored.', '%1 %2', resultString);
|
||||
resultString = Blockly.utils.replaceMessageReferences('before %1 after');
|
||||
assertEquals('Interpolation tokens ignored.', 'before %1 after', resultString);
|
||||
|
||||
resultString = Blockly.utils.replaceMessageReferences('%%');
|
||||
assertEquals('Escaped %', '%', resultString);
|
||||
resultString = Blockly.utils.replaceMessageReferences('%%{bky_string_ref}');
|
||||
assertEquals('Escaped %', '%{bky_string_ref}', resultString);
|
||||
|
||||
resultString = Blockly.utils.replaceMessageReferences('%a');
|
||||
assertEquals('Unrecognized % escape code treated as literal', '%a', resultString);
|
||||
}
|
||||
302
scratch-blocks/tests/jsunit/variable_map_test.js
Normal file
302
scratch-blocks/tests/jsunit/variable_map_test.js
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for variable map.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var variable_map;
|
||||
var mockControl_;
|
||||
var workspace;
|
||||
|
||||
function variableMapTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
variable_map = new Blockly.VariableMap(workspace);
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function variableMapTest_tearDown() {
|
||||
workspace.dispose();
|
||||
mockControl_.$tearDown();
|
||||
variable_map = null;
|
||||
}
|
||||
|
||||
function test_getVariable_ByNameAndType() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', 'type1', 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', 'type1', 'id2');
|
||||
var var_3 = variable_map.createVariable('name3', 'type2', 'id3');
|
||||
var result_1 = variable_map.getVariable('name1', 'type1');
|
||||
var result_2 = variable_map.getVariable('name2', 'type1');
|
||||
var result_3 = variable_map.getVariable('name3', 'type2');
|
||||
|
||||
// Searching by name + type is correct.
|
||||
assertEquals(var_1, result_1);
|
||||
assertEquals(var_2, result_2);
|
||||
assertEquals(var_3, result_3);
|
||||
|
||||
// Searching only by name defaults to the '' type.
|
||||
assertNull(variable_map.getVariable('name1'));
|
||||
assertNull(variable_map.getVariable('name2'));
|
||||
assertNull(variable_map.getVariable('name3'));
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariable_NotFound() {
|
||||
variableMapTest_setUp();
|
||||
var result = variable_map.getVariable('name1');
|
||||
assertNull(result);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariableById_Trivial() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', 'type1', 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', 'type1', 'id2');
|
||||
var var_3 = variable_map.createVariable('name3', 'type2', 'id3');
|
||||
var result_1 = variable_map.getVariableById('id1');
|
||||
var result_2 = variable_map.getVariableById('id2');
|
||||
var result_3 = variable_map.getVariableById('id3');
|
||||
|
||||
assertEquals(var_1, result_1);
|
||||
assertEquals(var_2, result_2);
|
||||
assertEquals(var_3, result_3);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariableById_NotFound() {
|
||||
variableMapTest_setUp();
|
||||
var result = variable_map.getVariableById('id1');
|
||||
assertNull(result);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableTrivial() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
checkVariableValues(variable_map, 'name1', 'type1', 'id1');
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableAlreadyExists() {
|
||||
// Expect that when the variable already exists, the variableMap_ is unchanged.
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
|
||||
// Assert there is only one variable in the variable_map.
|
||||
var keys = Object.keys(variable_map.variableMap_);
|
||||
assertEquals(1, keys.length);
|
||||
var varMapLength = variable_map.variableMap_[keys[0]].length;
|
||||
assertEquals(1, varMapLength);
|
||||
|
||||
variable_map.createVariable('name1', 'type1');
|
||||
checkVariableValues(variable_map, 'name1', 'type1', 'id1');
|
||||
// Check that the size of the variableMap_ did not change.
|
||||
keys = Object.keys(variable_map.variableMap_);
|
||||
assertEquals(1, keys.length);
|
||||
varMapLength = variable_map.variableMap_[keys[0]].length;
|
||||
assertEquals(1, varMapLength);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableNameAlreadyExists() {
|
||||
// Expect that when a variable with the same name but a different type already
|
||||
// exists, the new variable is created.
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
|
||||
// Assert there is only one variable in the variable_map.
|
||||
var keys = Object.keys(variable_map.variableMap_);
|
||||
assertEquals(1, keys.length);
|
||||
var varMapLength = variable_map.variableMap_[keys[0]].length;
|
||||
assertEquals(1, varMapLength);
|
||||
|
||||
variable_map.createVariable('name1', 'type2', 'id2');
|
||||
checkVariableValues(variable_map, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(variable_map, 'name1', 'type2', 'id2');
|
||||
// Check that the size of the variableMap_ did change.
|
||||
keys = Object.keys(variable_map.variableMap_);
|
||||
assertEquals(2, keys.length);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
function test_createVariableNullAndUndefinedType() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', null, 'id1');
|
||||
variable_map.createVariable('name2', undefined, 'id2');
|
||||
|
||||
checkVariableValues(variable_map, 'name1', '', 'id1');
|
||||
checkVariableValues(variable_map, 'name2', '', 'id2');
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableNullId() {
|
||||
variableMapTest_setUp();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
variable_map.createVariable('name1', 'type1', null);
|
||||
checkVariableValues(variable_map, 'name1', 'type1', '1');
|
||||
}
|
||||
finally {
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_createVariableUndefinedId() {
|
||||
variableMapTest_setUp();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
|
||||
try {
|
||||
variable_map.createVariable('name1', 'type1', undefined);
|
||||
checkVariableValues(variable_map, 'name1', 'type1', '1');
|
||||
}
|
||||
finally {
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_createVariableIdAlreadyExists() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
try {
|
||||
variable_map.createVariable('name2', 'type2', 'id1');
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableMismatchedIdAndType() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
try {
|
||||
variable_map.createVariable('name1', 'type2', 'id1');
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
variable_map.createVariable('name1', 'type1', 'id2');
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_createVariableTwoSameTypes() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
variable_map.createVariable('name2', 'type1', 'id2');
|
||||
|
||||
checkVariableValues(variable_map, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(variable_map, 'name2', 'type1', 'id2');
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariablesOfType_Trivial() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', 'type1', 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', 'type1', 'id2');
|
||||
variable_map.createVariable('name3', 'type2', 'id3');
|
||||
variable_map.createVariable('name4', 'type3', 'id4');
|
||||
var result_array_1 = variable_map.getVariablesOfType('type1');
|
||||
var result_array_2 = variable_map.getVariablesOfType('type5');
|
||||
isEqualArrays([var_1, var_2], result_array_1);
|
||||
isEqualArrays([], result_array_2);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariablesOfType_Null() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', '', 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', '', 'id2');
|
||||
var var_3 = variable_map.createVariable('name3', '', 'id3');
|
||||
variable_map.createVariable('name4', 'type1', 'id4');
|
||||
var result_array = variable_map.getVariablesOfType(null);
|
||||
isEqualArrays([var_1, var_2, var_3], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariablesOfType_EmptyString() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', null, 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', null, 'id2');
|
||||
var result_array = variable_map.getVariablesOfType('');
|
||||
isEqualArrays([var_1, var_2], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariablesOfType_Deleted() {
|
||||
variableMapTest_setUp();
|
||||
var variable = variable_map.createVariable('name1', null, 'id1');
|
||||
variable_map.deleteVariable(variable);
|
||||
var result_array = variable_map.getVariablesOfType('');
|
||||
isEqualArrays([], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariablesOfType_DoesNotExist() {
|
||||
variableMapTest_setUp();
|
||||
var result_array = variable_map.getVariablesOfType('type1');
|
||||
isEqualArrays([], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariableTypes_Trivial() {
|
||||
variableMapTest_setUp();
|
||||
variable_map.createVariable('name1', 'type1', 'id1');
|
||||
variable_map.createVariable('name2', 'type1', 'id2');
|
||||
variable_map.createVariable('name3', 'type2', 'id3');
|
||||
variable_map.createVariable('name4', 'type3', 'id4');
|
||||
var result_array = variable_map.getVariableTypes();
|
||||
// The empty string is always an option.
|
||||
isEqualArrays(['type1', 'type2', 'type3', ''], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getVariableTypes_None() {
|
||||
variableMapTest_setUp();
|
||||
// The empty string is always an option.
|
||||
var result_array = variable_map.getVariableTypes();
|
||||
isEqualArrays([''], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getAllVariables_Trivial() {
|
||||
variableMapTest_setUp();
|
||||
var var_1 = variable_map.createVariable('name1', 'type1', 'id1');
|
||||
var var_2 = variable_map.createVariable('name2', 'type1', 'id2');
|
||||
var var_3 = variable_map.createVariable('name3', 'type2', 'id3');
|
||||
var result_array = variable_map.getAllVariables();
|
||||
isEqualArrays([var_1, var_2, var_3], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
|
||||
function test_getAllVariables_None() {
|
||||
variableMapTest_setUp();
|
||||
var result_array = variable_map.getAllVariables();
|
||||
isEqualArrays([], result_array);
|
||||
variableMapTest_tearDown();
|
||||
}
|
||||
91
scratch-blocks/tests/jsunit/variable_model_test.js
Normal file
91
scratch-blocks/tests/jsunit/variable_model_test.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for variable model.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var variable;
|
||||
var workspace;
|
||||
|
||||
function variableModelTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
}
|
||||
|
||||
function variableModelTest_tearDown() {
|
||||
workspace.dispose();
|
||||
variable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* These tests check the constructor of the variable model.
|
||||
*/
|
||||
function testInit_Trivial() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test', 'test_type',
|
||||
'test_id');
|
||||
assertEquals('test', variable.name);
|
||||
assertEquals('test_type', variable.type);
|
||||
assertEquals('test_id', variable.id_);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
|
||||
function testInit_NullType() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test', null, 'test_id');
|
||||
assertEquals('', variable.type);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
|
||||
function testInit_UndefinedType() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test', undefined, 'test_id');
|
||||
assertEquals('', variable.type);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
|
||||
function testInit_NullId() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test', 'test_type', null);
|
||||
assertEquals('test', variable.name);
|
||||
assertEquals('test_type', variable.type);
|
||||
assertNotNull(variable.id_);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
|
||||
function testInit_UndefinedId() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test', 'test_type', undefined);
|
||||
assertEquals('test', variable.name);
|
||||
assertEquals('test_type', variable.type);
|
||||
assertNotNull(variable.id_);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
|
||||
function testInit_OnlyNameProvided() {
|
||||
variableModelTest_setUp();
|
||||
variable = new Blockly.VariableModel(workspace, 'test');
|
||||
assertEquals('test', variable.name);
|
||||
assertEquals('', variable.type);
|
||||
assertNotNull(variable.id_);
|
||||
variableModelTest_tearDown();
|
||||
}
|
||||
35
scratch-blocks/tests/jsunit/vertical_tests.html
Normal file
35
scratch-blocks/tests/jsunit/vertical_tests.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Unit Tests for Vertical Scratch-Blockly</title>
|
||||
<script src="../../blockly_uncompressed_vertical.js"></script>
|
||||
<script>goog.require('goog.testing.jsunit');</script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="test_utilities.js"></script>
|
||||
<script src="block_test.js"></script>
|
||||
<script src="connection_db_test.js"></script>
|
||||
<script src="connection_test.js"></script>
|
||||
<script src="event_test.js"></script>
|
||||
<script src="extensions_test.js"></script>
|
||||
<script src="field_test.js"></script>
|
||||
<script src="field_angle_test.js"></script>
|
||||
<script src="field_number_test.js"></script>
|
||||
<script src="field_variable_getter_test.js"></script>
|
||||
<script src="generator_test.js"></script>
|
||||
<script src="input_test.js"></script>
|
||||
<script src="json_test.js"></script>
|
||||
<script src="names_test.js"></script>
|
||||
<script src="procedure_test.js"></script>
|
||||
<script src="scratch_block_comment_test.js"></script>
|
||||
<script src="svg_test.js"></script>
|
||||
<script src="utils_test.js"></script>
|
||||
<script src="widget_div_test.js"></script>
|
||||
<script src="workspace_comment_test.js"></script>
|
||||
<script src="workspace_test.js"></script>
|
||||
<script src="xml_test.js"></script>
|
||||
<div id="blocklyDiv" style="display: none; height: 480px; width: 600px;"></div>
|
||||
<xml id="toolbox" style="display: none"></xml>
|
||||
</body>
|
||||
</html>
|
||||
154
scratch-blocks/tests/jsunit/widget_div_test.js
Normal file
154
scratch-blocks/tests/jsunit/widget_div_test.js
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
|
||||
function widgetdiv_testHelper_makeBBox(left, top, width, height) {
|
||||
return {
|
||||
left: left,
|
||||
right: left + width,
|
||||
top: top,
|
||||
bottom: top + height
|
||||
};
|
||||
}
|
||||
|
||||
function widgetdiv_testHelper_makeSize(width, height) {
|
||||
return {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
var widgetDiv_test_viewport = widgetdiv_testHelper_makeBBox(0, 0, 1000, 1000);
|
||||
var widgetDiv_test_widgetSize = widgetdiv_testHelper_makeSize(100, 100);
|
||||
|
||||
// Anchor is always 90 px wide and 90 px tall for this test.
|
||||
var widgetDiv_test_anchorSize = 90;
|
||||
|
||||
function widgetdiv_testHelper_makeAnchor(left, top) {
|
||||
return {
|
||||
left: left,
|
||||
right: left + widgetDiv_test_anchorSize,
|
||||
top: top,
|
||||
bottom: top + widgetDiv_test_anchorSize
|
||||
};
|
||||
}
|
||||
|
||||
function test_widgetDiv_topConflict() {
|
||||
var anchorTop = 50;
|
||||
// Anchor placed close to the top.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop);
|
||||
|
||||
// The widget div should be placed just below the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize);
|
||||
assertEquals(anchorTop + widgetDiv_test_anchorSize, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_bottomConflict() {
|
||||
var anchorTop = 900;
|
||||
// Anchor placed close to the bottom.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop);
|
||||
|
||||
// The widget div should be placed just above the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize);
|
||||
assertEquals(anchorTop - widgetDiv_test_widgetSize.height, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_noYConflict() {
|
||||
var anchorTop = 500;
|
||||
// Anchor placed in the middle.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop);
|
||||
|
||||
// The widget div should be placed just below the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize);
|
||||
assertEquals(anchorTop + widgetDiv_test_anchorSize, calculated);
|
||||
}
|
||||
|
||||
|
||||
function test_widgetDiv_leftConflict_LTR() {
|
||||
var anchorLeft = 50;
|
||||
// Anchor placed close to the left side.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
|
||||
// The widget div should be placed at the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, false /* rtl */);
|
||||
assertEquals(anchorLeft, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_rightConflict_LTR() {
|
||||
var anchorLeft = 950;
|
||||
// Anchor placed close to the right side.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
|
||||
// The widget div should be placed as far right as possible--at the edge of
|
||||
// the screen.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, false /* rtl */);
|
||||
assertEquals(1000 - widgetDiv_test_widgetSize.width, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_noXConflict_LTR() {
|
||||
var anchorLeft = 500;
|
||||
// Anchor in the middle
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
// The widget div should be placed just at the left side of the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, false /* rtl */);
|
||||
assertEquals(anchorLeft, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_leftConflict_RTL() {
|
||||
var anchorLeft = 10;
|
||||
// Anchor placed close to the left side.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
// The widget div should be placed as far left as possible--at the edge of
|
||||
// the screen.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, true /* rtl */);
|
||||
assertEquals(0, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_rightConflict_RTL() {
|
||||
var anchorLeft = 950;
|
||||
// Anchor placed close to the right side.
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
|
||||
// The widget div should be placed as far right as possible--at the edge of
|
||||
// the screen.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, true /* rtl */);
|
||||
assertEquals(1000 - widgetDiv_test_widgetSize.width, calculated);
|
||||
}
|
||||
|
||||
function test_widgetDiv_noXConflict_RTL() {
|
||||
var anchorLeft = 500;
|
||||
// anchor placed in the middle
|
||||
var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500);
|
||||
// The widget div should be placed at the right side of the anchor.
|
||||
var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport,
|
||||
anchorBBox, widgetDiv_test_widgetSize, true /* rtl */);
|
||||
assertEquals(anchorBBox.right - widgetDiv_test_widgetSize.width, calculated);
|
||||
}
|
||||
|
||||
186
scratch-blocks/tests/jsunit/workspace_comment_test.js
Normal file
186
scratch-blocks/tests/jsunit/workspace_comment_test.js
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
|
||||
var workspace;
|
||||
|
||||
function workspaceCommentTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
}
|
||||
|
||||
function workspaceCommentTest_tearDown() {
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_noWorkspaceComments() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
assertEquals('Empty workspace: no comments (1).', 0, workspace.getTopComments(true).length);
|
||||
assertEquals('Empty workspace: no comments (2).', 0, workspace.getTopComments(false).length);
|
||||
workspace.clear();
|
||||
assertEquals('Empty workspace: no comments (3).', 0, workspace.getTopComments(true).length);
|
||||
assertEquals('Empty workspace: no comments (4).', 0, workspace.getTopComments(false).length);
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_oneWorkspaceComment() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, false, 'comment id');
|
||||
assertEquals('One comment on workspace (1).', 1, workspace.getTopComments(true).length);
|
||||
assertEquals('One comment on workspace (2).', 1, workspace.getTopComments(false).length);
|
||||
assertEquals('Comment db contains this comment.', comment, workspace.commentDB_['comment id']);
|
||||
workspace.clear();
|
||||
assertEquals('Cleared workspace: no comments (3).', 0, workspace.getTopComments(true).length);
|
||||
assertEquals('Cleared workspace: no comments (4).', 0, workspace.getTopComments(false).length);
|
||||
assertFalse('Comment DB does not contain this comment.', 'comment id' in workspace.commentDB_);
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_getWorkspaceCommentById() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, false, 'comment id');
|
||||
assertEquals('Getting a comment by id.', comment, workspace.getCommentById('comment id'));
|
||||
assertEquals('No comment found.', null, workspace.getCommentById('not a comment'));
|
||||
comment.dispose();
|
||||
assertEquals('Can\'t find the comment.', null, workspace.getCommentById('comment id'));
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_disposeWsCommentTwice() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, false, 'comment id');
|
||||
comment.dispose();
|
||||
// Nothing should go wrong the second time dispose is called.
|
||||
comment.dispose();
|
||||
}finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_wsCommentHeightWidth() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment =
|
||||
new Blockly.WorkspaceComment(workspace, 'comment text', 10, 20, false, 'comment id');
|
||||
assertEquals('Initial width', 20, comment.getWidth());
|
||||
assertEquals('Initial height', 10, comment.getHeight());
|
||||
|
||||
comment.setWidth(30);
|
||||
assertEquals('New width should be different', 30, comment.getWidth());
|
||||
assertEquals('New height should not be different', 10, comment.getHeight());
|
||||
|
||||
comment.setHeight(40);
|
||||
assertEquals('New width should not be different', 30, comment.getWidth());
|
||||
assertEquals('New height should be different', 40, comment.getHeight());
|
||||
comment.dispose();
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_wsCommentXY() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment =
|
||||
new Blockly.WorkspaceComment(workspace, 'comment text', 10, 20, false, 'comment id');
|
||||
var xy = comment.getXY();
|
||||
assertEquals('Initial X position', 0, xy.x);
|
||||
assertEquals('Initial Y position', 0, xy.y);
|
||||
|
||||
comment.moveBy(10, 100);
|
||||
xy = comment.getXY();
|
||||
assertEquals('New X position', 10, xy.x);
|
||||
assertEquals('New Y position', 100, xy.y);
|
||||
comment.dispose();
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_wsCommentText() {
|
||||
workspaceCommentTest_setUp();
|
||||
|
||||
Blockly.Events.fire = temporary_fireEvent;
|
||||
temporary_fireEvent.firedEvents_ = [];
|
||||
try {
|
||||
var comment =
|
||||
new Blockly.WorkspaceComment(workspace, 'comment text', 10, 20, false, 'comment id');
|
||||
assertEquals(
|
||||
'Check comment text', 'comment text', comment.getText());
|
||||
assertEquals(
|
||||
'Workspace undo stack has one event', 1, workspace.undoStack_.length);
|
||||
|
||||
comment.setText('comment text');
|
||||
assertEquals(
|
||||
'Comment text has not changed', 'comment text', comment.getText());
|
||||
// Setting the text to the old value does not fire an event.
|
||||
assertEquals(
|
||||
'Workspace undo stack has one event', 1, workspace.undoStack_.length);
|
||||
|
||||
comment.setText('new comment text');
|
||||
assertEquals(
|
||||
'Comment text has changed', 'new comment text', comment.getText());
|
||||
assertEquals(
|
||||
'Workspace undo stack has two events', 2, workspace.undoStack_.length);
|
||||
comment.dispose();
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
Blockly.Events.fire = savedFireFunc;
|
||||
}
|
||||
}
|
||||
|
||||
function test_workspaceCommentMinimized() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, true, 'comment id');
|
||||
assertEquals('Comment is minimized', true, comment.isMinimized());
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_workspaceCommentMinimizedFromXml() {
|
||||
workspaceCommentTest_setUp();
|
||||
try {
|
||||
var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, true, 'comment id');
|
||||
var commentXml = comment.toXml();
|
||||
var xml = goog.dom.createDom('xml');
|
||||
xml.appendChild(commentXml);
|
||||
comment.dispose();
|
||||
assertEquals('Comment is no longer on workspace', null, workspace.getCommentById('comment id'));
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
var importedComment = workspace.getCommentById('comment id');
|
||||
assertNotEquals('Comment loaded from xml is on workspace', null, importedComment)
|
||||
assertEquals('Imported comment is minimized', true, importedComment.isMinimized());
|
||||
} finally {
|
||||
workspaceCommentTest_tearDown();
|
||||
}
|
||||
}
|
||||
371
scratch-blocks/tests/jsunit/workspace_test.js
Normal file
371
scratch-blocks/tests/jsunit/workspace_test.js
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2012 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var workspace;
|
||||
var mockControl_;
|
||||
|
||||
function workspaceTest_setUp() {
|
||||
defineGetVarBlock();
|
||||
workspace = new Blockly.Workspace();
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function workspaceTest_tearDown() {
|
||||
undefineGetVarBlock();
|
||||
mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_emptyWorkspace() {
|
||||
workspaceTest_setUp();
|
||||
try {
|
||||
assertEquals('Empty workspace (1).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Empty workspace (2).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Empty workspace (3).', 0, workspace.getAllBlocks().length);
|
||||
workspace.clear();
|
||||
assertEquals('Empty workspace (4).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Empty workspace (5).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Empty workspace (6).', 0, workspace.getAllBlocks().length);
|
||||
}
|
||||
finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_flatWorkspace() {
|
||||
workspaceTest_setUp();
|
||||
try {
|
||||
var blockA = workspace.newBlock('');
|
||||
assertEquals('One block workspace (1).', 1, workspace.getTopBlocks(true).length);
|
||||
assertEquals('One block workspace (2).', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('One block workspace (3).', 1, workspace.getAllBlocks().length);
|
||||
var blockB = workspace.newBlock('');
|
||||
assertEquals('Two block workspace (1).', 2, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Two block workspace (2).', 2, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Two block workspace (3).', 2, workspace.getAllBlocks().length);
|
||||
blockA.dispose();
|
||||
assertEquals('One block workspace (4).', 1, workspace.getTopBlocks(true).length);
|
||||
assertEquals('One block workspace (5).', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('One block workspace (6).', 1, workspace.getAllBlocks().length);
|
||||
workspace.clear();
|
||||
assertEquals('Cleared workspace (1).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Cleared workspace (2).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Cleared workspace (3).', 0, workspace.getAllBlocks().length);
|
||||
} finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_getWorkspaceById() {
|
||||
var workspaceA = new Blockly.Workspace();
|
||||
var workspaceB = new Blockly.Workspace();
|
||||
try {
|
||||
assertEquals('Find workspaceA.', workspaceA,
|
||||
Blockly.Workspace.getById(workspaceA.id));
|
||||
assertEquals('Find workspaceB.', workspaceB,
|
||||
Blockly.Workspace.getById(workspaceB.id));
|
||||
assertEquals('No workspace found.', null,
|
||||
Blockly.Workspace.getById('I do not exist.'));
|
||||
workspaceA.dispose();
|
||||
assertEquals('Can\'t find workspaceA.', null,
|
||||
Blockly.Workspace.getById(workspaceA.id));
|
||||
assertEquals('WorkspaceB exists.', workspaceB,
|
||||
Blockly.Workspace.getById(workspaceB.id));
|
||||
} finally {
|
||||
workspaceB.dispose();
|
||||
workspaceA.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_getBlockById() {
|
||||
workspaceTest_setUp();
|
||||
try {
|
||||
var blockA = workspace.newBlock('');
|
||||
var blockB = workspace.newBlock('');
|
||||
assertEquals('Find blockA.', blockA, workspace.getBlockById(blockA.id));
|
||||
assertEquals('Find blockB.', blockB, workspace.getBlockById(blockB.id));
|
||||
assertEquals('No block found.', null,
|
||||
workspace.getBlockById('I do not exist.'));
|
||||
blockA.dispose();
|
||||
assertEquals('Can\'t find blockA.', null, workspace.getBlockById(blockA.id));
|
||||
assertEquals('BlockB exists.', blockB, workspace.getBlockById(blockB.id));
|
||||
workspace.clear();
|
||||
assertEquals('Can\'t find blockB.', null, workspace.getBlockById(blockB.id));
|
||||
} finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_deleteVariable_InternalTrivial() {
|
||||
workspaceTest_setUp();
|
||||
var var_1 = workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.createVariable('name2', 'type2', 'id2');
|
||||
createMockBlock('id1');
|
||||
createMockBlock('id1');
|
||||
createMockBlock('id2');
|
||||
|
||||
var uses = workspace.getVariableUsesById(var_1.getId());
|
||||
workspace.deleteVariableInternal_(var_1, uses);
|
||||
|
||||
var variable = workspace.getVariableById('id1');
|
||||
var block_var_name = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
assertNull(variable);
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
assertEquals('name2', block_var_name);
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
// TODO(marisaleung): Test the alert for deleting a variable that is a procedure.
|
||||
|
||||
function test_addTopBlock_TrivialFlyoutIsTrue() {
|
||||
workspaceTest_setUp();
|
||||
var targetWorkspace = new Blockly.Workspace();
|
||||
workspace.isFlyout = true;
|
||||
workspace.targetWorkspace = targetWorkspace;
|
||||
targetWorkspace.createVariable('name1', '', '1');
|
||||
|
||||
// Flyout.init usually does this binding.
|
||||
workspace.variableMap_ = targetWorkspace.getVariableMap();
|
||||
|
||||
try {
|
||||
var block = createMockBlock('1');
|
||||
workspace.removeTopBlock(block);
|
||||
workspace.addTopBlock(block);
|
||||
checkVariableValues(workspace, 'name1', '', '1');
|
||||
} finally {
|
||||
workspaceTest_tearDown();
|
||||
// Have to dispose of the main workspace after the flyout workspace, because
|
||||
// it holds the variable map.
|
||||
// Normally the main workspace disposes of the flyout workspace.
|
||||
targetWorkspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_clear_Trivial() {
|
||||
workspaceTest_setUp();
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.createVariable('name2', 'type2', 'id2');
|
||||
setUpMockMethod(mockControl_, Blockly.Events, 'setGroup', [true, false],
|
||||
null);
|
||||
|
||||
try {
|
||||
workspace.clear();
|
||||
var topBlocks_length = workspace.topBlocks_.length;
|
||||
var varMapLength = Object.keys(workspace.variableMap_.variableMap_).length;
|
||||
assertEquals(0, topBlocks_length);
|
||||
assertEquals(0, varMapLength);
|
||||
}
|
||||
finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_clear_NoVariables() {
|
||||
workspaceTest_setUp();
|
||||
setUpMockMethod(mockControl_, Blockly.Events, 'setGroup', [true, false],
|
||||
null);
|
||||
|
||||
try {
|
||||
workspace.clear();
|
||||
var topBlocks_length = workspace.topBlocks_.length;
|
||||
var varMapLength = Object.keys(workspace.variableMap_.variableMap_).length;
|
||||
assertEquals(0, topBlocks_length);
|
||||
assertEquals(0, varMapLength);
|
||||
}
|
||||
finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_renameVariable_NoReference() {
|
||||
// Test renaming a variable in the simplest case: when no blocks refer to it.
|
||||
workspaceTest_setUp();
|
||||
var id = 'id1';
|
||||
var type = 'type1';
|
||||
var oldName = 'name1';
|
||||
var newName = 'name2';
|
||||
workspace.createVariable(oldName, type, id);
|
||||
|
||||
try {
|
||||
workspace.renameVariableById(id, newName);
|
||||
checkVariableValues(workspace, newName, type, id);
|
||||
// Renaming should not have created a new variable.
|
||||
assertEquals(1, workspace.getAllVariables().length);
|
||||
} finally {
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
function test_renameVariable_ReferenceExists() {
|
||||
// Test renaming a variable when a reference to it exists.
|
||||
// Expect 'renameVariable' to change oldName variable name to newName.
|
||||
workspaceTest_setUp();
|
||||
var newName = 'name2';
|
||||
|
||||
createVariableAndBlock(workspace);
|
||||
|
||||
workspace.renameVariableById('id1', newName);
|
||||
checkVariableValues(workspace, newName, 'type1', 'id1');
|
||||
// Renaming should not have created a new variable.
|
||||
assertEquals(1, workspace.getAllVariables().length);
|
||||
var block_var_name = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
assertEquals(newName, block_var_name);
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
function test_renameVariable_TwoVariablesSameType() {
|
||||
// Cannot rename variable to a name that already exists
|
||||
// for a variable of the same type.
|
||||
// Note: this behavior is different from that of blockly which allows
|
||||
// renaming variables to a name that already exists if the variables have the
|
||||
// same type.
|
||||
workspaceTest_setUp();
|
||||
var id1 = 'id1';
|
||||
var id2 = 'id2';
|
||||
var type = 'type1';
|
||||
|
||||
var oldName = 'name1';
|
||||
var newName = 'name2';
|
||||
// Create two variables of the same type.
|
||||
workspace.createVariable(oldName, type, id1);
|
||||
workspace.createVariable(newName, type, id2);
|
||||
// Create blocks to refer to both of them.
|
||||
createMockBlock(id1);
|
||||
createMockBlock(id2);
|
||||
|
||||
workspace.renameVariableById(id1, newName);
|
||||
|
||||
// Both variables should retain the same names/ids as before.
|
||||
checkVariableValues(workspace, oldName, type, id1);
|
||||
checkVariableValues(workspace, newName, type, id2);
|
||||
|
||||
// Both variables should remain on the workspace.
|
||||
assertEquals(2, workspace.getAllVariables().length);
|
||||
|
||||
// References should have the correct names.
|
||||
var block_var_name_1 = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
var block_var_name_2 = workspace.topBlocks_[1].getVarModels()[0].name;
|
||||
assertEquals(oldName, block_var_name_1);
|
||||
assertEquals(newName, block_var_name_2);
|
||||
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
function test_renameVariable_TwoVariablesDifferentType() {
|
||||
// Expect the rename to succeed, because variables with different types are
|
||||
// allowed to have the same name.
|
||||
workspaceTest_setUp();
|
||||
createTwoVariablesAndBlocks(workspace);
|
||||
|
||||
var newName = 'name2';
|
||||
workspace.renameVariableById('id1', newName);
|
||||
|
||||
checkVariableValues(workspace, newName, 'type1', 'id1');
|
||||
checkVariableValues(workspace, newName, 'type2', 'id2');
|
||||
|
||||
// References shoul have the correct names.
|
||||
var block_var_name_1 = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
var block_var_name_2 = workspace.topBlocks_[1].getVarModels()[0].name;
|
||||
assertEquals(newName, block_var_name_1);
|
||||
assertEquals(newName, block_var_name_2);
|
||||
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
function test_renameVariable_OldCase() {
|
||||
// Rename a variable with a single reference. Update only the capitalization.
|
||||
workspaceTest_setUp();
|
||||
var newName = 'Name1';
|
||||
|
||||
createVariableAndBlock(workspace);
|
||||
|
||||
workspace.renameVariableById('id1', newName);
|
||||
checkVariableValues(workspace, newName, 'type1', 'id1');
|
||||
var variable = workspace.getVariableById('id1');
|
||||
assertNotEquals('name1', variable.name);
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
function test_renameVariable_TwoVariablesAndOldCase() {
|
||||
// Test renaming a variable to an in-use name, but with different
|
||||
// capitalization. Two variables with different capitalizations should
|
||||
// co-exist.
|
||||
// Note: this behavior is different from that of blockly which does not allow
|
||||
// two variables of the same name to exist with different capitalization. In
|
||||
// blockly, variable names are case insensitive, so different capitalizations
|
||||
// of a variable name are treated as the same name.
|
||||
|
||||
workspaceTest_setUp();
|
||||
var oldName = 'name1';
|
||||
var oldCase = 'Name2';
|
||||
var newName = 'name2';
|
||||
|
||||
var id1 = 'id1';
|
||||
var id2 = 'id2';
|
||||
|
||||
var type = 'type1';
|
||||
|
||||
workspace.createVariable(oldName, type, id1);
|
||||
workspace.createVariable(oldCase, type, id2);
|
||||
createMockBlock(id1);
|
||||
createMockBlock(id2);
|
||||
|
||||
// Blocks should have the correct variable names
|
||||
var old_block_var_name_1 = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
var old_block_var_name_2 = workspace.topBlocks_[1].getVarModels()[0].name;
|
||||
assertEquals(oldName, old_block_var_name_1);
|
||||
assertEquals(oldCase, old_block_var_name_2);
|
||||
|
||||
workspace.renameVariableById(id1, newName);
|
||||
|
||||
// The old variable gets properly renamed to the new name,
|
||||
// since variables are case sensitive.
|
||||
checkVariableValues(workspace, newName, type, id1);
|
||||
|
||||
// Both variables should still exist
|
||||
assertEquals(2, workspace.getAllVariables().length);
|
||||
|
||||
// Block which had oldName should have been updated to use newName, while
|
||||
// block with oldCase should still have the same name.
|
||||
var block_var_name_1 = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
var block_var_name_2 = workspace.topBlocks_[1].getVarModels()[0].name;
|
||||
assertEquals(newName, block_var_name_1);
|
||||
assertNotEquals(old_block_var_name_1, block_var_name_1);
|
||||
assertEquals(oldCase, block_var_name_2);
|
||||
assertEquals(old_block_var_name_2, block_var_name_2);
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
|
||||
function test_deleteVariableById_Trivial() {
|
||||
workspaceTest_setUp();
|
||||
createTwoVariablesAndBlocks(workspace);
|
||||
|
||||
workspace.deleteVariableById('id1');
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
var variable = workspace.getVariableById('id1');
|
||||
var block_var_name = workspace.topBlocks_[0].getVarModels()[0].name;
|
||||
assertNull(variable);
|
||||
assertEquals('name2', block_var_name);
|
||||
workspaceTest_tearDown();
|
||||
}
|
||||
381
scratch-blocks/tests/jsunit/workspace_undo_redo_test.js
Normal file
381
scratch-blocks/tests/jsunit/workspace_undo_redo_test.js
Normal file
@@ -0,0 +1,381 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Tests for Blockly.Workspace.undo.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.events');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
|
||||
var workspace;
|
||||
var mockControl_;
|
||||
var savedFireFunc = Blockly.Events.fire;
|
||||
|
||||
function temporary_fireEvent(event) {
|
||||
if (!Blockly.Events.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
Blockly.Events.FIRE_QUEUE_.push(event);
|
||||
Blockly.Events.fireNow_();
|
||||
}
|
||||
|
||||
function undoRedoTest_setUp() {
|
||||
defineGetVarBlock();
|
||||
workspace = new Blockly.Workspace();
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
Blockly.Events.fire = temporary_fireEvent;
|
||||
}
|
||||
|
||||
function undoRedoTest_tearDown() {
|
||||
undefineGetVarBlock();
|
||||
mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
Blockly.Events.fire = savedFireFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the top block with the given index contains a variable with
|
||||
* the given name.
|
||||
* @param {number} blockIndex The index of the top block.
|
||||
* @param {string} name The expected name of the variable in the block.
|
||||
*/
|
||||
function undoRedoTest_checkBlockVariableName(blockIndex, name) {
|
||||
var blockVarName = workspace.topBlocks_[blockIndex].getVarModels()[0].name;
|
||||
assertEquals(name, blockVarName);
|
||||
}
|
||||
|
||||
function createTwoVarsEmptyType() {
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
workspace.createVariable('name2', '', 'id2');
|
||||
}
|
||||
|
||||
function createTwoVarsDifferentTypes() {
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.createVariable('name2', 'type2', 'id2');
|
||||
}
|
||||
|
||||
function test_undoCreateVariable_Trivial() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsDifferentTypes();
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
assertNull(workspace.getVariableById('id2'));
|
||||
workspace.undo();
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
assertNull(workspace.getVariableById('id2'));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_redoAndUndoCreateVariable_Trivial() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsDifferentTypes();
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
|
||||
// Expect that variable 'id2' is recreated
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
|
||||
// Expect that variable 'id1' is recreated
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
assertNull(workspace.getVariableById('id2'));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoDeleteVariable_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsDifferentTypes();
|
||||
workspace.deleteVariableById('id1');
|
||||
workspace.deleteVariableById('id2');
|
||||
|
||||
workspace.undo();
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoDeleteVariable_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
|
||||
createTwoVariablesAndBlocks(workspace);
|
||||
|
||||
workspace.deleteVariableById('id1');
|
||||
workspace.deleteVariableById('id2');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name2');
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name2');
|
||||
undoRedoTest_checkBlockVariableName(1, 'name1');
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_redoAndUndoDeleteVariable_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
|
||||
createTwoVarsDifferentTypes();
|
||||
|
||||
workspace.deleteVariableById('id1');
|
||||
workspace.deleteVariableById('id2');
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
// Expect that both variables are deleted
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
assertNull(workspace.getVariableById('id2'));
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
// Expect that variable 'id2' is recreated
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_redoAndUndoDeleteVariable_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
|
||||
createTwoVariablesAndBlocks(workspace);
|
||||
|
||||
workspace.deleteVariableById('id1');
|
||||
workspace.deleteVariableById('id2');
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
// Expect that both variables are deleted
|
||||
assertEquals(0, workspace.topBlocks_.length);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
assertNull(workspace.getVariableById('id2'));
|
||||
|
||||
workspace.undo();
|
||||
workspace.undo();
|
||||
workspace.undo(true);
|
||||
// Expect that variable 'id2' is recreated
|
||||
undoRedoTest_checkBlockVariableName(0, 'name2');
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_redoAndUndoDeleteVariableTwice_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.deleteVariableById('id1');
|
||||
workspace.deleteVariableById('id1');
|
||||
|
||||
// Check the undoStack only recorded one delete event.
|
||||
var undoStack = workspace.undoStack_;
|
||||
assertEquals('var_delete', undoStack[undoStack.length-1].type);
|
||||
assertNotEquals('var_delete', undoStack[undoStack.length-2].type);
|
||||
|
||||
// undo delete
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
|
||||
// redo delete
|
||||
workspace.undo(true);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
|
||||
// redo delete, nothing should happen
|
||||
workspace.undo(true);
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_redoAndUndoDeleteVariableTwice_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
var id = 'id1';
|
||||
workspace.createVariable('name1', 'type1', id);
|
||||
createMockBlock(id);
|
||||
workspace.deleteVariableById(id);
|
||||
workspace.deleteVariableById(id);
|
||||
|
||||
// Check the undoStack only recorded one delete event.
|
||||
var undoStack = workspace.undoStack_;
|
||||
assertEquals('var_delete', undoStack[undoStack.length-1].type);
|
||||
assertEquals('delete', undoStack[undoStack.length-2].type);
|
||||
assertNotEquals('var_delete', undoStack[undoStack.length-3].type);
|
||||
|
||||
// undo delete
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name1');
|
||||
checkVariableValues(workspace, 'name1', 'type1', id);
|
||||
|
||||
// redo delete
|
||||
workspace.undo(true);
|
||||
assertEquals(0, workspace.topBlocks_.length);
|
||||
assertNull(workspace.getVariableById(id));
|
||||
|
||||
// redo delete, nothing should happen
|
||||
workspace.undo(true);
|
||||
assertEquals(0, workspace.topBlocks_.length);
|
||||
assertNull(workspace.getVariableById(id));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_OneExists_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
workspace.renameVariableById('id1', 'name2');
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'name2', '', 'id1');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_OneExists_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
createMockBlock('id1');
|
||||
workspace.renameVariableById('id1', 'name2');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name1');
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'name2', '', 'id1');
|
||||
undoRedoTest_checkBlockVariableName(0, 'name2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_BothExist_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsEmptyType();
|
||||
workspace.renameVariableById('id1', 'name2');
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
checkVariableValues(workspace, 'name2', '', 'id2');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'name2', '', 'id2');
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_BothExist_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsEmptyType();
|
||||
createMockBlock('id1');
|
||||
createMockBlock('id2');
|
||||
workspace.renameVariableById('id1', 'name2');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name1');
|
||||
undoRedoTest_checkBlockVariableName(1, 'name2');
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
checkVariableValues(workspace, 'name2', '', 'id2');
|
||||
|
||||
workspace.undo(true);
|
||||
undoRedoTest_checkBlockVariableName(0, 'name2');
|
||||
undoRedoTest_checkBlockVariableName(1, 'name2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_BothExistCaseChange_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsEmptyType();
|
||||
workspace.renameVariableById('id1', 'Name2');
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
checkVariableValues(workspace, 'name2', '', 'id2');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'Name2', '', 'id2');
|
||||
assertNull(workspace.getVariable('name1'));
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_BothExistCaseChange_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
createTwoVarsEmptyType();
|
||||
createMockBlock('id1');
|
||||
createMockBlock('id2');
|
||||
workspace.renameVariableById('id1', 'Name2');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name1');
|
||||
undoRedoTest_checkBlockVariableName(1, 'name2');
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
checkVariableValues(workspace, 'name2', '', 'id2');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'Name2', '', 'id2');
|
||||
assertNull(workspace.getVariableById('id1'));
|
||||
undoRedoTest_checkBlockVariableName(0, 'Name2');
|
||||
undoRedoTest_checkBlockVariableName(1, 'Name2');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_OnlyCaseChange_NoBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
workspace.renameVariableById('id1', 'Name1');
|
||||
|
||||
workspace.undo();
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'Name1', '', 'id1');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
|
||||
function test_undoRedoRenameVariable_OnlyCaseChange_WithBlocks() {
|
||||
undoRedoTest_setUp();
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
createMockBlock('id1');
|
||||
workspace.renameVariableById('id1', 'Name1');
|
||||
|
||||
workspace.undo();
|
||||
undoRedoTest_checkBlockVariableName(0, 'name1');
|
||||
checkVariableValues(workspace, 'name1', '', 'id1');
|
||||
|
||||
workspace.undo(true);
|
||||
checkVariableValues(workspace, 'Name1', '', 'id1');
|
||||
undoRedoTest_checkBlockVariableName(0, 'Name1');
|
||||
undoRedoTest_tearDown();
|
||||
}
|
||||
443
scratch-blocks/tests/jsunit/xml_test.js
Normal file
443
scratch-blocks/tests/jsunit/xml_test.js
Normal file
@@ -0,0 +1,443 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2014 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
var mockControl_;
|
||||
var workspace;
|
||||
var XML_TEXT = ['<xml xmlns="http://www.w3.org/1999/xhtml">',
|
||||
' <block type="controls_repeat_ext" inline="true" x="21" y="23">',
|
||||
' <value name="TIMES">',
|
||||
' <block type="math_number">',
|
||||
' <field name="NUM">10</field>',
|
||||
' </block>',
|
||||
' </value>',
|
||||
' <statement name="DO">',
|
||||
' <block type="variables_set" inline="true">',
|
||||
' <field name="VAR">item</field>',
|
||||
' <value name="VALUE">',
|
||||
' <block type="lists_create_empty"></block>',
|
||||
' </value>',
|
||||
' <next>',
|
||||
' <block type="text_print" inline="false">',
|
||||
' <value name="TEXT">',
|
||||
' <block type="text">',
|
||||
' <field name="TEXT">Hello</field>',
|
||||
' </block>',
|
||||
' </value>',
|
||||
' </block>',
|
||||
' </next>',
|
||||
' </block>',
|
||||
' </statement>',
|
||||
' </block>',
|
||||
'</xml>'].join('\n');
|
||||
|
||||
function xmlTest_setUp() {
|
||||
workspace = new Blockly.Workspace();
|
||||
mockControl_ = new goog.testing.MockControl();
|
||||
}
|
||||
|
||||
function xmlTest_setUpWithMockBlocks() {
|
||||
xmlTest_setUp();
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
'type': 'field_variable_test_block',
|
||||
'message0': '%1',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_variable',
|
||||
'name': 'VAR',
|
||||
'variable': 'item'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'type': 'field_serializable_test_block',
|
||||
'message0': '%1 %2',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_label_serializable',
|
||||
'name': 'FIELD'
|
||||
},
|
||||
{
|
||||
"type": "field_input",
|
||||
"name": "TEXTINPUT",
|
||||
"text": "default"
|
||||
}
|
||||
]
|
||||
}]);
|
||||
}
|
||||
|
||||
function xmlTest_tearDown() {
|
||||
mockControl_.$tearDown();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function xmlTest_tearDownWithMockBlocks() {
|
||||
xmlTest_tearDown();
|
||||
delete Blockly.Blocks.field_variable_test_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the values of the non variable field dom.
|
||||
* @param {!Element} fieldDom The xml dom of the non variable field.
|
||||
* @param {!string} name The expected name of the variable.
|
||||
* @param {!string} text The expected text of the variable.
|
||||
*/
|
||||
function xmlTest_checkNonVariableField(fieldDom, name, text) {
|
||||
assertEquals(text, fieldDom.textContent);
|
||||
assertEquals(name, fieldDom.getAttribute('name'));
|
||||
assertNull(fieldDom.getAttribute('id'));
|
||||
assertNull(fieldDom.getAttribute('variabletype'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the values of the variable field DOM.
|
||||
* @param {!Element} fieldDom The xml dom of the variable field.
|
||||
* @param {!string} name The expected name of the variable.
|
||||
* @param {!string} type The expected type of the variable.
|
||||
* @param {!string} id The expected id of the variable.
|
||||
* @param {!string} text The expected text of the variable.
|
||||
*/
|
||||
function xmlTest_checkVariableFieldDomValues(fieldDom, name, type, id, text) {
|
||||
assertEquals(name, fieldDom.getAttribute('name'));
|
||||
assertEquals(type, fieldDom.getAttribute('variabletype'));
|
||||
assertEquals(id, fieldDom.getAttribute('id'));
|
||||
assertEquals(text, fieldDom.textContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the values of the variable DOM.
|
||||
* @param {!Element} variableDom The xml dom of the variable.
|
||||
* @param {!string} type The expected type of the variable.
|
||||
* @param {!string} id The expected id of the variable.
|
||||
* @param {!string} text The expected text of the variable.
|
||||
*/
|
||||
function xmlTest_checkVariableDomValues(variableDom, type, id, text) {
|
||||
assertEquals(type, variableDom.getAttribute('type'));
|
||||
assertEquals(id, variableDom.getAttribute('id'));
|
||||
assertEquals(text, variableDom.textContent);
|
||||
}
|
||||
|
||||
function test_textToDom() {
|
||||
var dom = Blockly.Xml.textToDom(XML_TEXT);
|
||||
assertEquals('XML tag', 'xml', dom.nodeName);
|
||||
assertEquals('Block tags', 6, dom.getElementsByTagName('block').length);
|
||||
}
|
||||
|
||||
function test_domToText() {
|
||||
var dom = Blockly.Xml.textToDom(XML_TEXT);
|
||||
var text = Blockly.Xml.domToText(dom);
|
||||
assertEquals('Round trip', XML_TEXT.replace(/\s+/g, ''),
|
||||
text.replace(/\s+/g, ''));
|
||||
}
|
||||
|
||||
function test_domToWorkspace_BackwardCompatibility() {
|
||||
// Expect that workspace still loads without serialized variables.
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '1']);
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <block type="field_variable_test_block" id="block_id">' +
|
||||
' <field name="VAR">name1</field>' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 1, workspace.getAllBlocks().length);
|
||||
checkVariableValues(workspace, 'name1', '', '1');
|
||||
} finally {
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_domToWorkspace_VariablesAtTop() {
|
||||
// Expect that unused variables are preserved.
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <variables>' +
|
||||
' <variable type="type1" id="id1">name1</variable>' +
|
||||
' <variable type="type2" id="id2">name2</variable>' +
|
||||
' <variable type="" id="id3">name3</variable>' +
|
||||
' </variables>' +
|
||||
' <block type="field_variable_test_block">' +
|
||||
' <field name="VAR" id="id3" variabletype="">name3</field>' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 1, workspace.getAllBlocks().length);
|
||||
checkVariableValues(workspace, 'name1', 'type1', 'id1');
|
||||
checkVariableValues(workspace, 'name2', 'type2', 'id2');
|
||||
checkVariableValues(workspace, 'name3', '', 'id3');
|
||||
} finally {
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_domToWorkspace_VariablesAtTop_DuplicateVariablesTag() {
|
||||
// Expect thrown Error because of duplicate 'variables' tag
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <variables>' +
|
||||
' </variables>' +
|
||||
' <variables>' +
|
||||
' </variables>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
fail();
|
||||
}
|
||||
catch (e) {
|
||||
// expected
|
||||
} finally {
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_domToWorkspace_VariablesAtTop_MissingType() {
|
||||
// Expect thrown error when a variable tag is missing the type attribute.
|
||||
workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <variables>' +
|
||||
' <variable id="id1">name1</variable>' +
|
||||
' </variables>' +
|
||||
' <block type="field_variable_test_block">' +
|
||||
' <field name="VAR" id="id1" variabletype="">name3</field>' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_domToWorkspace_VariablesAtTop_MismatchBlockType() {
|
||||
// Expect thrown error when the serialized type of a variable does not match
|
||||
// the type of a variable field that references it.
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml>' +
|
||||
' <variables>' +
|
||||
' <variable type="type1" id="id1">name1</variable>' +
|
||||
' </variables>' +
|
||||
' <block type="field_variable_test_block">' +
|
||||
' <field name="VAR" id="id1" variabletype="">name1</field>' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
fail();
|
||||
} catch (e) {
|
||||
// expected
|
||||
} finally {
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_domToPrettyText() {
|
||||
var dom = Blockly.Xml.textToDom(XML_TEXT);
|
||||
var text = Blockly.Xml.domToPrettyText(dom);
|
||||
assertEquals('Round trip', XML_TEXT.replace(/\s+/g, ''),
|
||||
text.replace(/\s+/g, ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the that appendDomToWorkspace works in a headless mode.
|
||||
* Also see test_appendDomToWorkspace() in workspace_svg_test.js.
|
||||
*/
|
||||
function test_appendDomToWorkspace() {
|
||||
Blockly.Blocks.test_block = {
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
message0: 'test',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var workspace = new Blockly.Workspace();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
' <block type="test_block" inline="true" x="21" y="23">' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
workspace = new Blockly.Workspace();
|
||||
Blockly.Xml.appendDomToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 1, workspace.getAllBlocks().length);
|
||||
var newBlockIds = Blockly.Xml.appendDomToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 2, workspace.getAllBlocks().length);
|
||||
assertEquals('Number of new block ids',1,newBlockIds.length);
|
||||
} finally {
|
||||
delete Blockly.Blocks.test_block;
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockToDom_fieldToDom_trivial() {
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
// TODO (#1199): make a similar test where the variable is given a non-empty
|
||||
// type.f
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
var block = new Blockly.Block(workspace, 'field_variable_test_block');
|
||||
block.inputList[0].fieldRow[0].setValue('id1');
|
||||
var resultFieldDom = Blockly.Xml.blockToDom(block).childNodes[0];
|
||||
xmlTest_checkVariableFieldDomValues(resultFieldDom, 'VAR', '', 'id1',
|
||||
'name1');
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_blockToDom_fieldToDom_defaultCase() {
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '1']);
|
||||
try {
|
||||
workspace.createVariable('name1');
|
||||
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'field_variable_test_block');
|
||||
block.inputList[0].fieldRow[0].setValue('1');
|
||||
Blockly.Events.enable();
|
||||
|
||||
var resultFieldDom = Blockly.Xml.blockToDom(block).childNodes[0];
|
||||
// Expect type is '' and id is '1' since we don't specify type and id.
|
||||
xmlTest_checkVariableFieldDomValues(resultFieldDom, 'VAR', '', '1', 'name1');
|
||||
} finally {
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
function test_blockToDom_fieldToDom_notAFieldVariable() {
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "field_angle_test_block",
|
||||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_angle",
|
||||
"name": "VAR",
|
||||
"angle": 90
|
||||
}
|
||||
],
|
||||
}]);
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
var block = new Blockly.Block(workspace, 'field_angle_test_block');
|
||||
var resultFieldDom = Blockly.Xml.blockToDom(block).childNodes[0];
|
||||
xmlTest_checkNonVariableField(resultFieldDom, 'VAR', '90');
|
||||
delete Blockly.Blocks.field_angle_block;
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_variablesToDom_oneVariable() {
|
||||
xmlTest_setUp();
|
||||
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
|
||||
|
||||
workspace.createVariable('name1');
|
||||
var resultDom = Blockly.Xml.variablesToDom(workspace.getAllVariables());
|
||||
assertEquals(1, resultDom.children.length);
|
||||
var resultVariableDom = resultDom.children[0];
|
||||
assertEquals('name1', resultVariableDom.textContent);
|
||||
assertEquals('', resultVariableDom.getAttribute('type'));
|
||||
assertEquals('1', resultVariableDom.getAttribute('id'));
|
||||
xmlTest_tearDown();
|
||||
}
|
||||
|
||||
function test_variablesToDom_twoVariables_oneBlock() {
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
|
||||
workspace.createVariable('name1', '', 'id1');
|
||||
workspace.createVariable('name2', 'type2', 'id2');
|
||||
// If events are enabled during block construction, it will create a default
|
||||
// variable.
|
||||
Blockly.Events.disable();
|
||||
var block = new Blockly.Block(workspace, 'field_variable_test_block');
|
||||
block.inputList[0].fieldRow[0].setValue('id1');
|
||||
Blockly.Events.enable();
|
||||
|
||||
var resultDom = Blockly.Xml.variablesToDom(workspace.getAllVariables());
|
||||
assertEquals(2, resultDom.children.length);
|
||||
xmlTest_checkVariableDomValues(resultDom.children[0], '', 'id1',
|
||||
'name1');
|
||||
xmlTest_checkVariableDomValues(resultDom.children[1], 'type2', 'id2',
|
||||
'name2');
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_variablesToDom_noVariables() {
|
||||
xmlTest_setUp();
|
||||
workspace.createVariable('name1');
|
||||
var resultDom = Blockly.Xml.variablesToDom(workspace.getAllVariables());
|
||||
assertEquals(1, resultDom.children.length);
|
||||
xmlTest_tearDown();
|
||||
}
|
||||
|
||||
function test_fieldIsSerialized() {
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
var block = new Blockly.Block(workspace, 'field_serializable_test_block');
|
||||
block.getField('FIELD').setValue('serialized');
|
||||
|
||||
var resultDom = Blockly.Xml.blockToDom(block).childNodes[0];
|
||||
assertEquals('serialized', resultDom.textContent);
|
||||
assertEquals('FIELD', resultDom.getAttribute('name'));
|
||||
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_fieldIsNotSerialized() {
|
||||
xmlTest_setUpWithMockBlocks();
|
||||
var block = new Blockly.Block(workspace, 'field_serializable_test_block');
|
||||
block.getField('FIELD').SERIALIZABLE = false;
|
||||
block.getField('FIELD').setValue('serialized');
|
||||
|
||||
var resultDom = Blockly.Xml.blockToDom(block).childNodes[0];
|
||||
assertEquals('default', resultDom.textContent);
|
||||
assertEquals('TEXTINPUT', resultDom.getAttribute('name'));
|
||||
|
||||
xmlTest_tearDownWithMockBlocks();
|
||||
}
|
||||
|
||||
function test_variableFieldXml_caseSensitive() {
|
||||
var id = 'testId';
|
||||
var type = 'testType';
|
||||
var name = 'testName';
|
||||
|
||||
var mockVariableModel = {
|
||||
type: type,
|
||||
name: name,
|
||||
getId: function() {
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
var generatedXml =
|
||||
Blockly.Variables.generateVariableFieldXml_(mockVariableModel);
|
||||
var goldenXml =
|
||||
'<field name="VARIABLE"' +
|
||||
' id="' + id + '"' +
|
||||
' variabletype="' + type + '"' +
|
||||
'>' + name + '</field>';
|
||||
assertEquals(goldenXml, generatedXml);
|
||||
}
|
||||
262
scratch-blocks/tests/multi_playground.html
Normal file
262
scratch-blocks/tests/multi_playground.html
Normal file
@@ -0,0 +1,262 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Multi-toolbox Playground</title>
|
||||
<script src="../blockly_uncompressed_horizontal.js"></script>
|
||||
<script src="../msg/messages.js"></script>
|
||||
<script src="../blocks_common/math.js"></script>
|
||||
<script src="../blocks_common/text.js"></script>
|
||||
<script src="../blocks_horizontal/control.js"></script>
|
||||
<script src="../blocks_horizontal/event.js"></script>
|
||||
<script src="../blocks_horizontal/wedo.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
var options = {
|
||||
comments: true,
|
||||
collapse: true,
|
||||
media: '../media/',
|
||||
oneBasedIndex: true,
|
||||
readOnly: false,
|
||||
//rtl: false,
|
||||
scrollbars: true,
|
||||
trashcan: true,
|
||||
//toolbox: null,
|
||||
//horizontalLayout: false,
|
||||
//toolboxPosition: 'start',
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: true,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
}
|
||||
};
|
||||
|
||||
function start() {
|
||||
var match = location.search.match(/toolbox=([^&]+)/);
|
||||
var toolbox =
|
||||
document.getElementById('toolbox-' + (match ? match[1] : 'categories'));
|
||||
document.forms.options.elements.toolbox.selectedIndex =
|
||||
Number(toolbox.getElementsByTagName('category').length == 0);
|
||||
startBlocklyInstance('VertStartLTR', false, false, 'start', toolbox);
|
||||
startBlocklyInstance('VertStartRTL', true, false, 'start', toolbox);
|
||||
|
||||
startBlocklyInstance('VertEndLTR', false, false, 'end', toolbox);
|
||||
startBlocklyInstance('VertEndRTL', true, false, 'end', toolbox);
|
||||
|
||||
startBlocklyInstance('HorizontalStartLTR', false, true, 'start', toolbox);
|
||||
startBlocklyInstance('HorizontalStartRTL', true, true, 'start', toolbox);
|
||||
|
||||
startBlocklyInstance('HorizontalEndLTR', false, true, 'end', toolbox);
|
||||
startBlocklyInstance('HorizontalEndRTL', true, true, 'end', toolbox);
|
||||
}
|
||||
|
||||
function startBlocklyInstance(suffix, rtl, horizontalLayout, position,
|
||||
toolbox) {
|
||||
options.rtl = rtl;
|
||||
options.toolbox = toolbox;
|
||||
options.horizontalLayout = horizontalLayout;
|
||||
options.toolboxPosition = position;
|
||||
Blockly.inject('blocklyDiv' + suffix, options);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
#octaweb {
|
||||
width: 100%;
|
||||
}
|
||||
#octaweb th {
|
||||
padding-top: 1em;
|
||||
width: 50%;
|
||||
}
|
||||
#octaweb td {
|
||||
width: 50%;
|
||||
}
|
||||
#octaweb td >div {
|
||||
height: 480px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="start()">
|
||||
<h1>Blockly Multi Playground</h1>
|
||||
|
||||
<form id="options">
|
||||
<select name="toolbox" onchange="document.forms.options.submit()">
|
||||
<option value="categories">Categories</option>
|
||||
<option value="simple">Simple</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<table id="octaweb">
|
||||
<tr>
|
||||
<th>LTR, Vertical, Start</th>
|
||||
<th>RTL, Vertical, Start</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="blocklyDivVertStartLTR"></div></td>
|
||||
<td><div id="blocklyDivVertStartRTL"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>LTR, Vertical, End</th>
|
||||
<th>RTL, Vertical, End</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="blocklyDivVertEndLTR"></div></td>
|
||||
<td><div id="blocklyDivVertEndRTL"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>LTR, Horizontal, Start</th>
|
||||
<th>RTL, Horizontal, Start</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="blocklyDivHorizontalStartLTR"></div></td>
|
||||
<td><div id="blocklyDivHorizontalStartRTL"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>LTR, Horizontal, End</th>
|
||||
<th>RTL, Horizontal, End</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="blocklyDivHorizontalEndLTR"></div></td>
|
||||
<td><div id="blocklyDivHorizontalEndRTL"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<xml id="toolbox-simple" style="display: none">
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="wedo_motorclockwise"></block>
|
||||
<!-- <block type="control_repeat"></block> -->
|
||||
<block type="control_forever"></block>
|
||||
<block type="control_repeat">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<xml id="toolbox-categories" style="display: none">
|
||||
<category name="Events" colour="210" secondaryColour="0">
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenflagclicked"></block>
|
||||
</category>
|
||||
<sep></sep>
|
||||
<!-- <block type="control_repeat"></block> -->
|
||||
<category name="Pants" secondaryColour="0">
|
||||
<block type="control_forever"></block>
|
||||
<block type="control_repeat">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml id="toolbox_categories" style="display: none">
|
||||
<category name="Events" secondaryColour="0">
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenbroadcastreceived">
|
||||
<value name="CHOICE">
|
||||
<shadow type="dropdown_whenbroadcast">
|
||||
<field name="CHOICE">blue</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="event_broadcast">
|
||||
<value name="CHOICE">
|
||||
<shadow type="dropdown_broadcast">
|
||||
<field name="CHOICE">blue</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
|
||||
<category name="Control" secondaryColour="0">
|
||||
<block type="control_forever"></block>
|
||||
<block type="control_repeat">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_stop"></block>
|
||||
<block type="control_wait">
|
||||
<value name="DURATION">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
|
||||
<category name="Wedo" secondaryColour="0">
|
||||
<block type="wedo_setcolor">
|
||||
<value name="CHOICE">
|
||||
<shadow type="dropdown_wedo_setcolor">
|
||||
<field name="CHOICE">mystery</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="wedo_motorclockwise">
|
||||
<value name="DURATION">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="wedo_motorcounterclockwise">
|
||||
<value name="DURATION">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="wedo_motorspeed">
|
||||
<value name="CHOICE">
|
||||
<shadow type="dropdown_wedo_motorspeed">
|
||||
<field name="CHOICE">fast</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="wedo_whentilt">
|
||||
<value name="CHOICE">
|
||||
<shadow type="dropdown_wedo_whentilt">
|
||||
<field name="CHOICE">forward</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="wedo_whendistanceclose"></block>
|
||||
</category>
|
||||
</xml>
|
||||
</body>
|
||||
</html>
|
||||
585
scratch-blocks/tests/vertical_playground.html
Normal file
585
scratch-blocks/tests/vertical_playground.html
Normal file
@@ -0,0 +1,585 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<title>Vertical Playground</title>
|
||||
|
||||
<script src="../blockly_uncompressed_vertical.js"></script>
|
||||
<script src="../msg/messages.js"></script>
|
||||
<script src="../msg/scratch_msgs.js"></script>
|
||||
<script src="../blocks_vertical/vertical_extensions.js"></script>
|
||||
<script src="../blocks_common/math.js"></script>
|
||||
<script src="../blocks_common/matrix.js"></script>
|
||||
<script src="../blocks_common/note.js"></script>
|
||||
<script src="../blocks_common/text.js"></script>
|
||||
<script src="../blocks_common/colour.js"></script>
|
||||
<script src="../blocks_vertical/control.js"></script>
|
||||
<script src="../blocks_vertical/event.js"></script>
|
||||
<script src="../blocks_vertical/motion.js"></script>
|
||||
<script src="../blocks_vertical/looks.js"></script>
|
||||
<script src="../blocks_vertical/procedures.js"></script>
|
||||
<script src="../blocks_vertical/operators.js"></script>
|
||||
<script src="../blocks_vertical/pen.js"></script>
|
||||
<script src="../blocks_vertical/sound.js"></script>
|
||||
<script src="../blocks_vertical/sensing.js"></script>
|
||||
<script src="../blocks_vertical/data.js"></script>
|
||||
<script src="../blocks_vertical/extensions.js"></script>
|
||||
<script src="../blocks_vertical/default_toolbox.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
window.ScratchBlocks = Blockly;
|
||||
|
||||
var workspace = null;
|
||||
|
||||
function start() {
|
||||
var soundsEnabled = null;
|
||||
if (sessionStorage) {
|
||||
// Restore sounds state.
|
||||
soundsEnabled = sessionStorage.getItem('soundsEnabled');
|
||||
if (soundsEnabled === null) {
|
||||
soundsEnabled = true;
|
||||
} else {
|
||||
soundsEnabled = (soundsEnabled === 'true');
|
||||
}
|
||||
} else {
|
||||
soundsEnabled = true;
|
||||
}
|
||||
setSoundsEnabled(soundsEnabled);
|
||||
|
||||
// Setup blocks
|
||||
// Parse the URL arguments.
|
||||
var match = location.search.match(/dir=([^&]+)/);
|
||||
var rtl = match && match[1] == 'rtl';
|
||||
document.forms.options.elements.dir.selectedIndex = Number(rtl);
|
||||
var toolbox = getToolboxElement();
|
||||
document.forms.options.elements.toolbox.selectedIndex =
|
||||
toolbox ? 1: 0;
|
||||
|
||||
match = location.search.match(/side=([^&]+)/);
|
||||
|
||||
var side = match ? match[1] : 'start';
|
||||
|
||||
document.forms.options.elements.side.value = side;
|
||||
|
||||
match = location.search.match(/locale=([^&]+)/);
|
||||
var locale = match ? match[1] : 'en';
|
||||
Blockly.ScratchMsgs.setLocale(locale);
|
||||
document.forms.options.elements.locale.value = locale;
|
||||
|
||||
// Create main workspace.
|
||||
workspace = Blockly.inject('blocklyDiv', {
|
||||
comments: true,
|
||||
disable: false,
|
||||
collapse: false,
|
||||
media: '../media/',
|
||||
readOnly: false,
|
||||
rtl: rtl,
|
||||
scrollbars: true,
|
||||
toolbox: toolbox,
|
||||
toolboxPosition: side == 'top' || side == 'start' ? 'start' : 'end',
|
||||
horizontalLayout: side == 'top' || side == 'bottom',
|
||||
sounds: soundsEnabled,
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: true,
|
||||
startScale: 0.675,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
},
|
||||
colours: {
|
||||
fieldShadow: 'rgba(255, 255, 255, 0.3)',
|
||||
dragShadowOpacity: 0.6
|
||||
}
|
||||
});
|
||||
|
||||
if (sessionStorage) {
|
||||
// Restore previously displayed text.
|
||||
var text = sessionStorage.getItem('textarea');
|
||||
if (text) {
|
||||
document.getElementById('importExport').value = text;
|
||||
}
|
||||
taChange();
|
||||
}
|
||||
|
||||
if (sessionStorage) {
|
||||
// Restore event logging state.
|
||||
var state = sessionStorage.getItem('logEvents');
|
||||
logEvents(Boolean(state));
|
||||
|
||||
// Restore flyout event logging state.
|
||||
state = sessionStorage.getItem('logFlyoutEvents');
|
||||
logFlyoutEvents(Boolean(state));
|
||||
}
|
||||
}
|
||||
|
||||
function getToolboxElement() {
|
||||
var match = location.search.match(/toolbox=([^&]+)/);
|
||||
return document.getElementById('toolbox-' + (match ? match[1] : 'categories'));
|
||||
}
|
||||
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
taChange();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
workspace.refreshToolboxSelection_();
|
||||
taChange();
|
||||
}
|
||||
|
||||
// Disable the "Import from XML" button if the XML is invalid.
|
||||
// Preserve text between page reloads.
|
||||
function taChange() {
|
||||
var textarea = document.getElementById('importExport');
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('textarea', textarea.value)
|
||||
}
|
||||
var valid = true;
|
||||
try {
|
||||
Blockly.Xml.textToDom(textarea.value);
|
||||
} catch (e) {
|
||||
valid = false;
|
||||
}
|
||||
document.getElementById('import').disabled = !valid;
|
||||
}
|
||||
|
||||
function logEvents(state) {
|
||||
var checkbox = document.getElementById('logCheck');
|
||||
checkbox.checked = state;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logEvents', state ? 'checked' : '');
|
||||
}
|
||||
if (state) {
|
||||
workspace.addChangeListener(logger);
|
||||
} else {
|
||||
workspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logFlyoutEvents(state) {
|
||||
var checkbox = document.getElementById('logFlyoutCheck');
|
||||
checkbox.checked = state;
|
||||
var soundsEnabled = null;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logFlyoutEvents', state ? 'checked' : '');
|
||||
}
|
||||
var flyoutWorkspace = (workspace.flyout_) ? workspace.flyout_.workspace_ :
|
||||
workspace.toolbox_.flyout_.workspace_;
|
||||
if (state) {
|
||||
flyoutWorkspace.addChangeListener(logger);
|
||||
} else {
|
||||
flyoutWorkspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logger(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function glowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function glowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function sprinkles(n) {
|
||||
var prototypes = [];
|
||||
var toolbox = workspace.options.languageTree;
|
||||
if (!toolbox) {
|
||||
console.error('Toolbox not found; add a toolbox element to the DOM.');
|
||||
return;
|
||||
}
|
||||
var blocks = toolbox.getElementsByTagName('block');
|
||||
for (var i = 0; i < n; i++) {
|
||||
var blockXML = blocks[Math.floor(Math.random() * blocks.length)];
|
||||
var block = Blockly.Xml.domToBlock(blockXML, workspace);
|
||||
block.initSvg();
|
||||
block.moveBy(
|
||||
Math.round(Math.random() * 450 + 40),
|
||||
Math.round(Math.random() * 600 + 40)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var equalsXml = [
|
||||
' <shadow type="operator_equals">',
|
||||
' <value name="OPERAND1">',
|
||||
' <shadow type="text">',
|
||||
' <field name="TEXT">foo</field>',
|
||||
' </shadow>',
|
||||
' </value>',
|
||||
' <value name="OPERAND2">',
|
||||
' <shadow type="operator_equals"></shadow>',
|
||||
' </value>',
|
||||
' </shadow>'
|
||||
].join('\n');
|
||||
|
||||
var spaghettiXml = [
|
||||
' <block type="control_if_else">',
|
||||
' <value name="CONDITION">',
|
||||
' <shadow type="operator_equals"></shadow>',
|
||||
' </value>',
|
||||
' <statement name="SUBSTACK"></statement>',
|
||||
' <statement name="SUBSTACK2"></statement>',
|
||||
' <next></next>',
|
||||
' </block>'
|
||||
].join('\n');
|
||||
|
||||
function spaghetti(n) {
|
||||
console.log("Starting spaghetti. This may take some time...");
|
||||
var xml = spaghettiXml;
|
||||
// Nest if/else statements deeply.
|
||||
for(var i = 0; i < 2 * n; i++) {
|
||||
xml = xml.replace(/(<statement name="SUBSTACK2?"?>)<\//g,
|
||||
'$1' + spaghettiXml + '</');
|
||||
}
|
||||
// Stack a bit.
|
||||
for(var i = 0; i < n; i++) {
|
||||
xml = xml.replace(/(<next>)<\//g,
|
||||
'$1' + spaghettiXml + '</');
|
||||
}
|
||||
|
||||
// Nest boolean comparisons.
|
||||
var equalsBlock = equalsXml;
|
||||
for (var i = 0; i < n; i++) {
|
||||
equalsBlock = equalsBlock.replace(
|
||||
/(<shadow( type="operator_equals")?>)<\/shadow>/g, equalsXml);
|
||||
}
|
||||
|
||||
// Put the nested boolean comparisons into if/else statements.
|
||||
xml = xml.replace(/(<shadow( type="operator_equals")?>)<\/shadow>/g,
|
||||
equalsBlock);
|
||||
|
||||
xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + xml + '</xml>';
|
||||
var dom = Blockly.Xml.textToDom(xml);
|
||||
console.time('Spaghetti domToWorkspace');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
console.timeEnd('Spaghetti domToWorkspace');
|
||||
}
|
||||
|
||||
function setSoundsEnabled(state) {
|
||||
var checkbox = document.getElementById('soundsEnabled');
|
||||
checkbox.checked = (state) ? 'checked' : '';
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('soundsEnabled', state);
|
||||
}
|
||||
}
|
||||
|
||||
function reportDemo() {
|
||||
if (Blockly.selected) {
|
||||
workspace.reportValue(
|
||||
Blockly.selected.id,
|
||||
document.getElementById('reportValue').value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setLocale(locale) {
|
||||
workspace.getFlyout().setRecyclingEnabled(false);
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
Blockly.ScratchMsgs.setLocale(locale);
|
||||
Blockly.Xml.clearWorkspaceAndLoadFromXml(xml, workspace);
|
||||
workspace.getFlyout().setRecyclingEnabled(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
#blocklyDiv {
|
||||
float: right;
|
||||
height: 95%;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#collaborators {
|
||||
float: right;
|
||||
width: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#collaborators > img {
|
||||
margin-right: 5px;
|
||||
height: 30px;
|
||||
padding-bottom: 5px;
|
||||
width: 30px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#importExport {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="start()">
|
||||
<div id="collaborators"></div>
|
||||
<div id="blocklyDiv"></div>
|
||||
<!-- Simple toolbox -->
|
||||
<xml id="toolbox-simple" style="display: none">
|
||||
<block type="operator_random">
|
||||
<value name="FROM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="TO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_lt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_equals">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_gt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_and"></block>
|
||||
<block type="operator_or"></block>
|
||||
<block type="operator_not"></block>
|
||||
<block type="operator_join">
|
||||
<value name="STRING1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">hello</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_letter_of">
|
||||
<value name="LETTER">
|
||||
<shadow type="math_whole_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_length">
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mod">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_round">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mathop">
|
||||
<value name="OPERATOR">
|
||||
<shadow type="operator_mathop_menu"></shadow>
|
||||
</value>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<h1>Vertical Blocks</h1>
|
||||
<p>
|
||||
<a href="javascript:void(workspace.setVisible(true))">Show</a>
|
||||
- <a href="javascript:void(workspace.setVisible(false))">Hide</a>
|
||||
</p>
|
||||
|
||||
<form id="options">
|
||||
<select name="dir" onchange="document.forms.options.submit()">
|
||||
<option value="ltr">LTR</option>
|
||||
<option value="rtl">RTL</option>
|
||||
</select>
|
||||
<select name="toolbox" onchange="document.forms.options.submit()">
|
||||
<option value="categories">Categories</option>
|
||||
<option value="simple">Simple</option>
|
||||
</select>
|
||||
<select name="side" onchange="document.forms.options.submit()">
|
||||
<option value="start">Start</option>
|
||||
<option value="end">End</option>
|
||||
<option value="top">Top</option>
|
||||
<option value="bottom">Bottom</option>
|
||||
</select>
|
||||
<select name="locale" onchange="setLocale(this.value)">
|
||||
<option value="en">English</option>
|
||||
<option value="ca">Catalan</option>
|
||||
<option value="cs">Czech</option>
|
||||
<option value="da">Danish</option>
|
||||
<option value="nl">Dutch</option>
|
||||
<option value="fi">Finnish</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="de">German</option>
|
||||
<option value="el">Greek</option>
|
||||
<option value="he">Hebrew</option>
|
||||
<option value="hu">Hungarian</option>
|
||||
<option value="id">Indonesian</option>
|
||||
<option value="ga">Irish Gaelic</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="ja-Hira">Japanese(Hira)</option>
|
||||
<option value="mi">Maori</option>
|
||||
<option value="nb">Norwegian (Bokmal)</option>
|
||||
<option value="pt">Portuguese</option>
|
||||
<option value="pt-br">Portuguese (Brazil)</option>
|
||||
<option value="gd">Scottish Gaelic</option>
|
||||
<option value="sr">Serbian</option>
|
||||
<option value="sl">Slovenian</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="es-419">Spanish (Latin America)</option>
|
||||
<option value="sv">Swedish</option>
|
||||
<option value="tr">Turkish</option>
|
||||
<option value="uk">Ukranian</option>
|
||||
<option value="vi">Vietnamese</option>
|
||||
<option value="cy">Welsh</option>
|
||||
<option value="zh-cn">Chinese (China)</option>
|
||||
<option value="zh-tw">Chinese (TW)</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
|
||||
<input type="button" value="Import from XML" onclick="fromXml()" id="import">
|
||||
<br>
|
||||
<textarea id="importExport" style="width: 26%; height: 12em"
|
||||
onchange="taChange();" onkeyup="taChange()"></textarea>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
Log events:
|
||||
<input type="checkbox" onclick="logEvents(this.checked)" id="logCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Stress test:
|
||||
<input type="button" value="Sprinkles!" onclick="sprinkles(100)">
|
||||
<input type="button" value="Spaghetti!" onclick="spaghetti(3)">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Glows:
|
||||
<input type="button" value="Glow last clicked block" onclick="glowBlock()" />
|
||||
<input type="button" value="Unglow last clicked block" onclick="unglowBlock()" />
|
||||
<input type="button" value="Stack glow last clicked block" onclick="glowStack()" />
|
||||
<input type="button" value="Stack unglow last clicked block" onclick="unglowStack()" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Log flyout events:
|
||||
<input type="checkbox" onclick="logFlyoutEvents(this.checked)" id="logFlyoutCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Enable sounds (after refresh):
|
||||
<input type="checkbox" onclick="setSoundsEnabled(this.checked)" id="soundsEnabled">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Undo" onclick="workspace.undo()" />
|
||||
<input type="button" value="Redo" onclick="workspace.undo(true)" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Report:
|
||||
<input id="reportValue" type="text" value="123" />
|
||||
<input type="button" value="Report last clicked block" onclick="reportDemo()" />
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
726
scratch-blocks/tests/vertical_playground_compressed.html
Normal file
726
scratch-blocks/tests/vertical_playground_compressed.html
Normal file
@@ -0,0 +1,726 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<title>Compressed Vertical Playground</title>
|
||||
|
||||
<script src="../blockly_compressed_vertical.js"></script>
|
||||
<script src="../msg/messages.js"></script>
|
||||
<script src="../msg/scratch_msgs.js"></script>
|
||||
<script src="../blocks_compressed.js"></script>
|
||||
<script src="../blocks_compressed_vertical.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
window.ScratchBlocks = Blockly;
|
||||
|
||||
var fakeDragStack = [];
|
||||
var workspace = null;
|
||||
|
||||
function start() {
|
||||
var soundsEnabled = null;
|
||||
if (sessionStorage) {
|
||||
// Restore sounds state.
|
||||
soundsEnabled = sessionStorage.getItem('soundsEnabled');
|
||||
if (soundsEnabled === null) {
|
||||
soundsEnabled = true;
|
||||
} else {
|
||||
soundsEnabled = (soundsEnabled === 'true');
|
||||
}
|
||||
} else {
|
||||
soundsEnabled = true;
|
||||
}
|
||||
setSoundsEnabled(soundsEnabled);
|
||||
|
||||
// Setup blocks
|
||||
// Parse the URL arguments.
|
||||
var match = location.search.match(/dir=([^&]+)/);
|
||||
var rtl = match && match[1] == 'rtl';
|
||||
document.forms.options.elements.dir.selectedIndex = Number(rtl);
|
||||
var toolbox = getToolboxElement();
|
||||
document.forms.options.elements.toolbox.selectedIndex =
|
||||
toolbox ? 1: 0;
|
||||
|
||||
match = location.search.match(/side=([^&]+)/);
|
||||
|
||||
var side = match ? match[1] : 'start';
|
||||
|
||||
document.forms.options.elements.side.value = side;
|
||||
|
||||
match = location.search.match(/locale=([^&]+)/);
|
||||
var locale = match ? match[1] : 'en';
|
||||
Blockly.ScratchMsgs.setLocale(locale);
|
||||
document.forms.options.elements.locale.value = locale;
|
||||
|
||||
// Create main workspace.
|
||||
workspace = Blockly.inject('blocklyDiv', {
|
||||
comments: true,
|
||||
disable: false,
|
||||
collapse: false,
|
||||
media: '../media/',
|
||||
readOnly: false,
|
||||
rtl: rtl,
|
||||
scrollbars: true,
|
||||
toolbox: toolbox,
|
||||
toolboxPosition: side == 'top' || side == 'start' ? 'start' : 'end',
|
||||
horizontalLayout: side == 'top' || side == 'bottom',
|
||||
trashcan: true,
|
||||
sounds: soundsEnabled,
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: true,
|
||||
startScale: 0.75,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
},
|
||||
colours: {
|
||||
fieldShadow: 'rgba(255, 255, 255, 0.3)',
|
||||
dragShadowOpacity: 0.6
|
||||
}
|
||||
});
|
||||
|
||||
// Restore previously displayed text.
|
||||
var text = sessionStorage.getItem('textarea');
|
||||
if (text) {
|
||||
document.getElementById('importExport').value = text;
|
||||
}
|
||||
taChange();
|
||||
|
||||
if (sessionStorage) {
|
||||
// Restore event logging state.
|
||||
var state = sessionStorage.getItem('logEvents');
|
||||
logEvents(Boolean(state));
|
||||
|
||||
// Restore flyout event logging state.
|
||||
state = sessionStorage.getItem('logFlyoutEvents');
|
||||
logFlyoutEvents(Boolean(state));
|
||||
}
|
||||
}
|
||||
|
||||
function getToolboxElement() {
|
||||
var match = location.search.match(/toolbox=([^&]+)/);
|
||||
return document.getElementById('toolbox-' + (match ? match[1] : 'categories'));
|
||||
}
|
||||
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
taChange();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
taChange();
|
||||
}
|
||||
|
||||
function toCode(lang) {
|
||||
var output = document.getElementById('importExport');
|
||||
output.value = Blockly[lang].workspaceToCode(workspace);
|
||||
taChange();
|
||||
}
|
||||
|
||||
// Disable the "Import from XML" button if the XML is invalid.
|
||||
// Preserve text between page reloads.
|
||||
function taChange() {
|
||||
var textarea = document.getElementById('importExport');
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('textarea', textarea.value);
|
||||
}
|
||||
var valid = true;
|
||||
try {
|
||||
Blockly.Xml.textToDom(textarea.value);
|
||||
} catch (e) {
|
||||
valid = false;
|
||||
}
|
||||
document.getElementById('import').disabled = !valid;
|
||||
}
|
||||
|
||||
function logEvents(state) {
|
||||
var checkbox = document.getElementById('logCheck');
|
||||
checkbox.checked = state;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logEvents', state ? 'checked' : '');
|
||||
}
|
||||
if (state) {
|
||||
workspace.addChangeListener(logger);
|
||||
} else {
|
||||
workspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logFlyoutEvents(state) {
|
||||
var checkbox = document.getElementById('logFlyoutCheck');
|
||||
checkbox.checked = state;
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('logFlyoutEvents', state ? 'checked' : '');
|
||||
}
|
||||
var flyoutWorkspace = (workspace.flyout_) ? workspace.flyout_.workspace_ :
|
||||
workspace.toolbox_.flyout_.workspace_;
|
||||
if (state) {
|
||||
flyoutWorkspace.addChangeListener(logger);
|
||||
} else {
|
||||
flyoutWorkspace.removeChangeListener(logger);
|
||||
}
|
||||
}
|
||||
|
||||
function logger(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function glowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowBlock() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowBlock(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function glowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
function unglowStack() {
|
||||
if (Blockly.selected) {
|
||||
workspace.glowStack(Blockly.selected.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
function sprinkles(n) {
|
||||
var prototypes = [];
|
||||
var toolbox = workspace.options.languageTree;
|
||||
if (!toolbox) {
|
||||
console.error('Toolbox not found; add a toolbox element to the DOM.');
|
||||
return;
|
||||
}
|
||||
var blocks = toolbox.getElementsByTagName('block');
|
||||
for (var i = 0; i < n; i++) {
|
||||
var blockXML = blocks[Math.floor(Math.random() * blocks.length)];
|
||||
var block = Blockly.Xml.domToBlock(blockXML, workspace);
|
||||
block.initSvg();
|
||||
block.moveBy(
|
||||
Math.round(Math.random() * 450 + 40),
|
||||
Math.round(Math.random() * 600 + 40)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var equalsXml = [
|
||||
' <shadow type="operator_equals">',
|
||||
' <value name="OPERAND1">',
|
||||
' <shadow type="text">',
|
||||
' <field name="TEXT">foo</field>',
|
||||
' </shadow>',
|
||||
' </value>',
|
||||
' <value name="OPERAND2">',
|
||||
' <shadow type="operator_equals"></shadow>',
|
||||
' </value>',
|
||||
' </shadow>'
|
||||
].join('\n');
|
||||
|
||||
var spaghettiXml = [
|
||||
' <block type="control_if_else">',
|
||||
' <value name="CONDITION">',
|
||||
' <shadow type="operator_equals"></shadow>',
|
||||
' </value>',
|
||||
' <statement name="SUBSTACK"></statement>',
|
||||
' <statement name="SUBSTACK2"></statement>',
|
||||
' <next></next>',
|
||||
' </block>'
|
||||
].join('\n');
|
||||
|
||||
function spaghetti(n) {
|
||||
console.log("Starting spaghetti. This may take some time...");
|
||||
var xml = spaghettiXml;
|
||||
// Nest if/else statements deeply.
|
||||
for(var i = 0; i < 2 * n; i++) {
|
||||
xml = xml.replace(/(<statement name="SUBSTACK2?"?>)<\//g,
|
||||
'$1' + spaghettiXml + '</');
|
||||
}
|
||||
// Stack a bit.
|
||||
for(var i = 0; i < n; i++) {
|
||||
xml = xml.replace(/(<next>)<\//g,
|
||||
'$1' + spaghettiXml + '</');
|
||||
}
|
||||
|
||||
// Nest boolean comparisons.
|
||||
var equalsBlock = equalsXml;
|
||||
for (var i = 0; i < n; i++) {
|
||||
equalsBlock = equalsBlock.replace(
|
||||
/(<shadow( type="operator_equals")?>)<\/shadow>/g, equalsXml);
|
||||
}
|
||||
|
||||
// Put the nested boolean comparisons into if/else statements.
|
||||
xml = xml.replace(/(<shadow( type="operator_equals")?>)<\/shadow>/g,
|
||||
equalsBlock);
|
||||
|
||||
xml = '<xml xmlns="http://www.w3.org/1999/xhtml">' + xml + '</xml>';
|
||||
var dom = Blockly.Xml.textToDom(xml);
|
||||
console.time('Spaghetti domToWorkspace');
|
||||
Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
console.timeEnd('Spaghetti domToWorkspace');
|
||||
}
|
||||
|
||||
function setSoundsEnabled(state) {
|
||||
var checkbox = document.getElementById('soundsEnabled');
|
||||
checkbox.checked = (state) ? 'checked' : '';
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('soundsEnabled', state);
|
||||
}
|
||||
}
|
||||
|
||||
function fakeDrag(id, dx, dy, opt_workspace) {
|
||||
var ws = opt_workspace || Blockly.getMainWorkspace();
|
||||
var blockToDrag = ws.getBlockById(id);
|
||||
|
||||
if (!blockToDrag) {
|
||||
fakeDragWrapper();
|
||||
return;
|
||||
}
|
||||
var blockTop = blockToDrag.svgGroup_.getBoundingClientRect().top;
|
||||
var blockLeft = blockToDrag.svgGroup_.getBoundingClientRect().left;
|
||||
|
||||
// Click somewhere on the block.
|
||||
var mouseDownEvent = new MouseEvent('mousedown',
|
||||
{clientX: blockLeft + 5, clientY: blockTop + 5});
|
||||
blockToDrag.onMouseDown_(mouseDownEvent);
|
||||
|
||||
// Throw in a move for good measure.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseMoveEvent = new MouseEvent('mousemove',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseMove_(mouseMoveEvent);
|
||||
|
||||
// Drop at dx, dy.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseUpEvent = new MouseEvent('mouseup',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseUp_(mouseUpEvent);
|
||||
|
||||
setTimeout(fakeDragWrapper(), 100);
|
||||
}, 30);
|
||||
}, 30);
|
||||
};
|
||||
|
||||
function fakeDragWrapper() {
|
||||
var dragInfo = fakeDragStack.pop();
|
||||
if (dragInfo) {
|
||||
fakeDrag(dragInfo.id, dragInfo.dx, dragInfo.dy, dragInfo.workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function fakeManyDrags() {
|
||||
var blockList = workspace.getAllBlocks();
|
||||
for (var i = 0; i < 2 * blockList.length; i++) {
|
||||
fakeDragStack.push(
|
||||
{
|
||||
id: blockList[Math.round(Math.random() * (blockList.length - 1))].id,
|
||||
// Move some blocks up and to the left, but mostly down and to the right.
|
||||
dx: Math.round((Math.random() - 0.25) * 200),
|
||||
dy: Math.round((Math.random() - 0.25) * 200),
|
||||
workspace: workspace
|
||||
});
|
||||
}
|
||||
fakeDragWrapper();
|
||||
}
|
||||
|
||||
function reportDemo() {
|
||||
if (Blockly.selected) {
|
||||
workspace.reportValue(
|
||||
Blockly.selected.id,
|
||||
document.getElementById('reportValue').value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setLocale(locale) {
|
||||
workspace.getFlyout().setRecyclingEnabled(false);
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
Blockly.ScratchMsgs.setLocale(locale);
|
||||
Blockly.Xml.clearWorkspaceAndLoadFromXml(xml, workspace);
|
||||
workspace.getFlyout().setRecyclingEnabled(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
#blocklyDiv {
|
||||
float: right;
|
||||
height: 95%;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#collaborators {
|
||||
float: right;
|
||||
width: 30px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#collaborators > img {
|
||||
margin-right: 5px;
|
||||
height: 30px;
|
||||
padding-bottom: 5px;
|
||||
width: 30px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#importExport {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="start()">
|
||||
<div id="collaborators"></div>
|
||||
<div id="blocklyDiv"></div>
|
||||
<!-- Toolbox -->
|
||||
<xml id="toolbox-simple" style="display: none">
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenbroadcastreceived">
|
||||
</block>
|
||||
<block type="event_broadcast">
|
||||
<value name="BROADCAST_OPTION">
|
||||
<shadow type="event_broadcast_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="event_broadcastandwait">
|
||||
<value name="BROADCAST_OPTION">
|
||||
<shadow type="event_broadcast_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_wait">
|
||||
<value name="DURATION">
|
||||
<shadow type="math_positive_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_repeat">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_whole_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_forever"></block>
|
||||
<block type="control_if"></block>
|
||||
<block type="control_if_else"></block>
|
||||
<block type="control_wait_until"></block>
|
||||
<block type="control_repeat_until"></block>
|
||||
<block type="control_stop">
|
||||
<value name="STOP_OPTION">
|
||||
<shadow type="control_stop_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_start_as_clone"></block>
|
||||
<block type="control_create_clone_of">
|
||||
<value name="CLONE_OPTION">
|
||||
<shadow type="control_create_clone_of_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_delete_this_clone"></block>
|
||||
<block type="operator_add">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_subtract">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_multiply">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_divide">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_random">
|
||||
<value name="FROM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="TO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_lt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_equals">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_gt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_and"></block>
|
||||
<block type="operator_or"></block>
|
||||
<block type="operator_not"></block>
|
||||
<block type="operator_join">
|
||||
<value name="STRING1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">hello</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_letter_of">
|
||||
<value name="LETTER">
|
||||
<shadow type="math_whole_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_length">
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mod">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_round">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mathop">
|
||||
<value name="OPERATOR">
|
||||
<shadow type="operator_mathop_menu"></shadow>
|
||||
</value>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<h1>Vertical Blocks</h1>
|
||||
<p>
|
||||
<a href="javascript:void(workspace.setVisible(true))">Show</a>
|
||||
- <a href="javascript:void(workspace.setVisible(false))">Hide</a>
|
||||
</p>
|
||||
|
||||
<form id="options">
|
||||
<select name="dir" onchange="document.forms.options.submit()">
|
||||
<option value="ltr">LTR</option>
|
||||
<option value="rtl">RTL</option>
|
||||
</select>
|
||||
<select name="toolbox" onchange="document.forms.options.submit()">
|
||||
<option value="categories">Categories</option>
|
||||
<option value="simple">Simple</option>
|
||||
</select>
|
||||
<select name="side" onchange="document.forms.options.submit()">
|
||||
<option value="start">Start</option>
|
||||
<option value="end">End</option>
|
||||
<option value="top">Top</option>
|
||||
<option value="bottom">Bottom</option>
|
||||
</select>
|
||||
<select name="locale" onchange="setLocale(this.value)">
|
||||
<option value="en">English</option>
|
||||
<option value="ca">Catalan</option>
|
||||
<option value="cs">Czech</option>
|
||||
<option value="da">Danish</option>
|
||||
<option value="nl">Dutch</option>
|
||||
<option value="fi">Finnish</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="de">German</option>
|
||||
<option value="el">Greek</option>
|
||||
<option value="he">Hebrew</option>
|
||||
<option value="hu">Hungarian</option>
|
||||
<option value="id">Indonesian</option>
|
||||
<option value="ga">Irish Gaelic</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="ja-Hira">Japanese(Hira)</option>
|
||||
<option value="mi">Maori</option>
|
||||
<option value="nb">Norwegian (Bokmal)</option>
|
||||
<option value="pt">Portuguese</option>
|
||||
<option value="pt-br">Portuguese (Brazil)</option>
|
||||
<option value="gd">Scottish Gaelic</option>
|
||||
<option value="sr">Serbian</option>
|
||||
<option value="sl">Slovenian</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="es-419">Spanish (Latin America)</option>
|
||||
<option value="sv">Swedish</option>
|
||||
<option value="tr">Turkish</option>
|
||||
<option value="uk">Ukranian</option>
|
||||
<option value="vi">Vietnamese</option>
|
||||
<option value="cy">Welsh</option>
|
||||
<option value="zh-cn">Chinese (China)</option>
|
||||
<option value="zh-tw">Chinese (TW)</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
|
||||
<input type="button" value="Import from XML" onclick="fromXml()" id="import">
|
||||
<br>
|
||||
<input type="button" value="To JavaScript" onclick="toCode('JavaScript')">
|
||||
<br>
|
||||
<textarea id="importExport" style="width: 26%; height: 12em"
|
||||
onchange="taChange();" onkeyup="taChange()"></textarea>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
Log events:
|
||||
<input type="checkbox" onclick="logEvents(this.checked)" id="logCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Stress test:
|
||||
<input type="button" value="Sprinkles!" onclick="sprinkles(100)">
|
||||
<input type="button" value="Spaghetti!" onclick="spaghetti(3)">
|
||||
<input type="button" value="Fake some drags!" onclick="fakeManyDrags()">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Glows:
|
||||
<input type="button" value="Glow last clicked block" onclick="glowBlock()" />
|
||||
<input type="button" value="Unglow last clicked block" onclick="unglowBlock()" />
|
||||
<input type="button" value="Stack glow last clicked block" onclick="glowStack()" />
|
||||
<input type="button" value="Stack unglow last clicked block" onclick="unglowStack()" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Log flyout events:
|
||||
<input type="checkbox" onclick="logFlyoutEvents(this.checked)" id="logFlyoutCheck">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Enable sounds (after refresh):
|
||||
<input type="checkbox" onclick="setSoundsEnabled(this.checked)" id="soundsEnabled">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Undo" onclick="workspace.undo()" />
|
||||
<input type="button" value="Redo" onclick="workspace.undo(true)" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Report:
|
||||
<input id="reportValue" type="text" value="123" />
|
||||
<input type="button" value="Report last clicked block" onclick="reportDemo()" />
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
75
scratch-blocks/tests/workspace_svg/index.html
Normal file
75
scratch-blocks/tests/workspace_svg/index.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Workspace SVG testing</title>
|
||||
<script src="../../blockly_uncompressed_vertical.js"></script>
|
||||
<script src="../../blocks_vertical/vertical_extensions.js"></script>
|
||||
<script src="../../blocks_vertical/looks.js"></script>
|
||||
<script>goog.require('goog.testing.jsunit');</script>
|
||||
<script src="../../msg/messages.js"></script>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
#blocklyDiv {
|
||||
float: right;
|
||||
height: 95%;
|
||||
width: 70%;
|
||||
}
|
||||
#importExport {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.ioLabel>.blocklyFlyoutLabelText {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="blocklyDiv"></div>
|
||||
|
||||
<h1>Blockly Workspace testing</h1>
|
||||
<script src="../jsunit/test_utilities.js"></script>
|
||||
<script src="workspace_svg_test.js"></script>
|
||||
|
||||
|
||||
<xml id="toolbox-categories" style="display: none">
|
||||
<category name="Looks" colour="#9966FF" secondaryColour="#774DCB">
|
||||
<block type="looks_show" id="lABCD"></block>
|
||||
<block type="looks_hide" id="looks_hide"></block>
|
||||
<block type="looks_switchcostumeto" id="looks_switchcostumeto">
|
||||
<value name="COSTUME">
|
||||
<shadow type="looks_costume"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_nextcostume" id="looks_nextcostume"></block>
|
||||
<block type="looks_nextbackdrop" id="looks_nextbackdrop"></block>
|
||||
<block type="looks_switchbackdropto" id="looks_switchbackdropto">
|
||||
<value name="BACKDROP">
|
||||
<shadow type="looks_backdrops"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<sep></sep>
|
||||
<category name="Variables" colour="330" custom="VARIABLE"></category>
|
||||
<category name="Functions" colour="290" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
</body>
|
||||
</html>
|
||||
135
scratch-blocks/tests/workspace_svg/workspace_svg_test.js
Normal file
135
scratch-blocks/tests/workspace_svg/workspace_svg_test.js
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.MockControl');
|
||||
|
||||
function helper_createWorkspaceWithToolbox() {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
return Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
}
|
||||
|
||||
function test_createWorkspace() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
workspace.dispose();
|
||||
}
|
||||
|
||||
function test_emptyWorkspace() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
try {
|
||||
assertEquals('Empty workspace (1).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Empty workspace (2).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Empty workspace (3).', 0, workspace.getAllBlocks().length);
|
||||
workspace.clear();
|
||||
assertEquals('Empty workspace (4).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Empty workspace (5).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Empty workspace (6).', 0, workspace.getAllBlocks().length);
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_flatWorkspace() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
var blockA, blockB;
|
||||
try {
|
||||
blockA = workspace.newBlock('');
|
||||
assertEquals('One block workspace (1).', 1, workspace.getTopBlocks(true).length);
|
||||
assertEquals('One block workspace (2).', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('One block workspace (3).', 1, workspace.getAllBlocks().length);
|
||||
blockB = workspace.newBlock('');
|
||||
assertEquals('Two block workspace (1).', 2, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Two block workspace (2).', 2, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Two block workspace (3).', 2, workspace.getAllBlocks().length);
|
||||
blockA.dispose();
|
||||
assertEquals('One block workspace (4).', 1, workspace.getTopBlocks(true).length);
|
||||
assertEquals('One block workspace (5).', 1, workspace.getTopBlocks(false).length);
|
||||
assertEquals('One block workspace (6).', 1, workspace.getAllBlocks().length);
|
||||
workspace.clear();
|
||||
assertEquals('Cleared workspace (1).', 0, workspace.getTopBlocks(true).length);
|
||||
assertEquals('Cleared workspace (2).', 0, workspace.getTopBlocks(false).length);
|
||||
assertEquals('Cleared workspace (3).', 0, workspace.getAllBlocks().length);
|
||||
} finally {
|
||||
blockB && blockB.dispose();
|
||||
blockA && blockA.dispose();
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests the alignment of appendDomToWorkspace with WorkspaceSvg. */
|
||||
function test_appendDomToWorkspace() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
try {
|
||||
var dom = Blockly.Xml.textToDom(
|
||||
'<xml xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
' <block type="looks_show" inline="true" x="21" y="23">' +
|
||||
' </block>' +
|
||||
'</xml>');
|
||||
Blockly.Xml.appendDomToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 1, workspace.getAllBlocks().length);
|
||||
Blockly.Xml.appendDomToWorkspace(dom, workspace);
|
||||
assertEquals('Block count', 2, workspace.getAllBlocks().length);
|
||||
var blocks = workspace.getAllBlocks();
|
||||
assertEquals('Block 1 position x',21,blocks[0].getRelativeToSurfaceXY().x);
|
||||
assertEquals('Block 1 position y',23,blocks[0].getRelativeToSurfaceXY().y);
|
||||
assertEquals('Block 2 position x',21,blocks[1].getRelativeToSurfaceXY().x);
|
||||
assertEquals('Block 2 position y',23 + blocks[0].getHeightWidth().height + Blockly.BlockSvg.SEP_SPACE_Y,blocks[1].getRelativeToSurfaceXY().y);
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_addNewVariableRefreshToolbox() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
|
||||
var mockControl = new goog.testing.MockControl();
|
||||
var mockMethod = mockControl.createMethodMock(Blockly.WorkspaceSvg.prototype,
|
||||
'refreshToolboxSelection_');
|
||||
try {
|
||||
mockMethod().$once();
|
||||
mockControl.$replayAll();
|
||||
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
mockControl.$verifyAll();
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function test_addDuplicateVariableDoNotRefreshToolbox() {
|
||||
var workspace = helper_createWorkspaceWithToolbox();
|
||||
|
||||
var mockControl = new goog.testing.MockControl();
|
||||
var mockMethod = mockControl.createMethodMock(Blockly.WorkspaceSvg.prototype,
|
||||
'refreshToolboxSelection_');
|
||||
try {
|
||||
mockMethod().$once();
|
||||
mockControl.$replayAll();
|
||||
// Create same variable twice. The first should refresh the toolbox,
|
||||
// the second should not.
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
workspace.createVariable('name1', 'type1', 'id1');
|
||||
|
||||
mockControl.$verifyAll();
|
||||
} finally {
|
||||
workspace.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user