/** * @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); }