From dd0d5aee53902a2bd271b167c79da8c6af502cba Mon Sep 17 00:00:00 2001 From: Monica Kozbial Date: Fri, 6 Nov 2020 11:48:48 -0800 Subject: [PATCH] Adding procedure tests and handling procedures instantiated without name (#4428) * Expand procedure tests and fix bug with default ids * Add tests * Remove xml_procedures_test.js and add non-overlapping test cases into procedures_test.js --- blocks/procedures.js | 18 +- tests/mocha/.eslintrc.json | 2 + tests/mocha/index.html | 2 +- tests/mocha/procedures_test.js | 428 +++++++++++++++++++++++-- tests/mocha/procedures_test_helpers.js | 135 ++++++++ tests/mocha/xml_procedures_test.js | 308 ------------------ 6 files changed, 560 insertions(+), 333 deletions(-) create mode 100644 tests/mocha/procedures_test_helpers.js delete mode 100644 tests/mocha/xml_procedures_test.js diff --git a/blocks/procedures.js b/blocks/procedures.js index d918d92d6..ad5d78042 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -28,7 +28,8 @@ Blockly.Blocks['procedures_defnoreturn'] = { * @this {Blockly.Block} */ init: function() { - var nameField = new Blockly.FieldTextInput('', + var initName = Blockly.Procedures.findLegalName('', this); + var nameField = new Blockly.FieldTextInput(initName, Blockly.Procedures.rename); nameField.setSpellcheck(false); this.appendDummyInput() @@ -405,7 +406,8 @@ Blockly.Blocks['procedures_defreturn'] = { * @this {Blockly.Block} */ init: function() { - var nameField = new Blockly.FieldTextInput('', + var initName = Blockly.Procedures.findLegalName('', this); + var nameField = new Blockly.FieldTextInput(initName, Blockly.Procedures.rename); nameField.setSpellcheck(false); this.appendDummyInput() @@ -592,7 +594,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { */ init: function() { this.appendDummyInput('TOPROW') - .appendField(this.id, 'NAME'); + .appendField('', 'NAME'); this.setPreviousStatement(true); this.setNextStatement(true); this.setStyle('procedure_blocks'); @@ -873,8 +875,13 @@ Blockly.Blocks['procedures_callnoreturn'] = { block.appendChild(mutation); var field = Blockly.utils.xml.createElement('field'); field.setAttribute('name', 'NAME'); - field.appendChild(Blockly.utils.xml.createTextNode( - this.getProcedureCall())); + var callName = this.getProcedureCall(); + if (!callName) { + // Rename if name is empty string. + callName = Blockly.Procedures.findLegalName('', this); + this.renameProcedure('', callName); + } + field.appendChild(Blockly.utils.xml.createTextNode(callName)); block.appendChild(field); xml.appendChild(block); Blockly.Xml.domToWorkspace(xml, this.workspace); @@ -955,6 +962,7 @@ Blockly.Blocks['procedures_callreturn'] = { // Tooltip is set in domToMutation. this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLRETURN_HELPURL']); this.arguments_ = []; + this.argumentVarModels_ = []; this.quarkConnections_ = {}; this.quarkIds_ = null; this.previousEnabledState_ = true; diff --git a/tests/mocha/.eslintrc.json b/tests/mocha/.eslintrc.json index 33f4bcc42..34a32aba8 100644 --- a/tests/mocha/.eslintrc.json +++ b/tests/mocha/.eslintrc.json @@ -10,6 +10,8 @@ "addBlockTypeToCleanup": true, "addMessageToCleanup": true, "assertArrayEquals": true, + "assertCallBlockStructure": true, + "assertDefBlockStructure": true, "assertDeprecationWarningCall": true, "assertEventEquals": true, "assertEventFired": true, diff --git a/tests/mocha/index.html b/tests/mocha/index.html index 36fc09426..bd9cde0ce 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -88,6 +88,7 @@ + @@ -102,7 +103,6 @@ - diff --git a/tests/mocha/procedures_test.js b/tests/mocha/procedures_test.js index 1d44be77d..95791ef63 100644 --- a/tests/mocha/procedures_test.js +++ b/tests/mocha/procedures_test.js @@ -11,11 +11,14 @@ suite('Procedures', function() { setup(function() { sharedTestSetup.call(this); this.workspace = new Blockly.Workspace(); + this.workspace.createVariable('preCreatedVar', '', 'preCreatedVarId'); + this.workspace.createVariable( + 'preCreatedTypedVar', 'type', 'preCreatedTypedVarId'); }); teardown(function() { sharedTestTeardown.call(this); }); - + suite('allProcedures', function() { test('Only Procedures', function() { var noReturnBlock = new Blockly.Block(this.workspace, 'procedures_defnoreturn'); @@ -190,6 +193,180 @@ suite('Procedures', function() { }); }); + suite('Multiple block serialization', function() { + function assertDefAndCallBlocks(workspace, noReturnNames, returnNames, hasCallers) { + const allProcedures = Blockly.Procedures.allProcedures(workspace); + const defNoReturnBlocks = allProcedures[0]; + chai.assert.lengthOf(defNoReturnBlocks, noReturnNames.length); + for (let i = 0; i < noReturnNames.length; i++) { + const expectedName = noReturnNames[i]; + chai.assert.equal(defNoReturnBlocks[i][0], expectedName); + if (hasCallers) { + const callers = + Blockly.Procedures.getCallers(expectedName, workspace); + chai.assert.lengthOf(callers, 1); + } + } + const defReturnBlocks = allProcedures[1]; + chai.assert.lengthOf(defReturnBlocks, returnNames.length); + for (let i = 0; i < returnNames.length; i++) { + const expectedName = returnNames[i]; + chai.assert.equal(defReturnBlocks[i][0], expectedName); + if (hasCallers) { + const callers = + Blockly.Procedures.getCallers(expectedName, workspace); + chai.assert.lengthOf(callers, 1); + } + } + + // Expecting def and caller blocks are the only blocks on workspace + let expectedCount = noReturnNames.length + returnNames.length; + if (hasCallers) { + expectedCount *= 2; + } + const blocks = workspace.getAllBlocks(false); + chai.assert.lengthOf(blocks, expectedCount); + } + suite('no name renamed to unnamed', function() { + test('defnoreturn and defreturn', function() { + var xml = Blockly.Xml.textToDom(` + + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, ['unnamed'], ['unnamed2'], false); + }); + test('defreturn and defnoreturn', function() { + var xml = Blockly.Xml.textToDom(` + + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, ['unnamed2'], ['unnamed'], false); + }); + test('callnoreturn (no def in xml)', function() { + var xml = Blockly.Xml.textToDom(` + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, ['unnamed'], [], true); + }); + test('callreturn (no def in xml)', function() { + var xml = Blockly.Xml.textToDom(` + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, [], ['unnamed'], true); + }); + test('callnoreturn and callreturn (no def in xml)', function() { + var xml = Blockly.Xml.textToDom(` + + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, ['unnamed'], ['unnamed2'], true); + }); + test('callreturn and callnoreturn (no def in xml)', function() { + var xml = Blockly.Xml.textToDom(` + + + + `); + Blockly.Xml.domToWorkspace(xml, this.workspace); + assertDefAndCallBlocks( + this.workspace, ['unnamed2'], ['unnamed'], true); + }); + }); + suite('caller param mismatch', function() { + test.skip('callreturn with missing args', function() { + // TODO: How do we want it to behave in this situation? + var defBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + do something + + + + + `), this.workspace); + var callBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + '' + ), this.workspace); + assertDefBlockStructure(defBlock, true, ['x'], ['arg']); + assertCallBlockStructure(callBlock, ['x'], ['arg']); + }); + test.skip('callreturn with bad args', function() { + // TODO: How do we want it to behave in this situation? + var defBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + do something + + + + + `), this.workspace); + var callBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + + + + + `), this.workspace); + assertDefBlockStructure(defBlock, true, ['x'], ['arg']); + assertCallBlockStructure(callBlock, ['x'], ['arg']); + }); + test.skip('callnoreturn with missing args', function() { + // TODO: How do we want it to behave in this situation? + var defBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + do something + + + + + `), this.workspace); + var callBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + '' + ), this.workspace); + assertDefBlockStructure(defBlock, false, ['x'], ['arg']); + assertCallBlockStructure(callBlock, ['x'], ['arg']); + }); + test.skip('callnoreturn with bad args', function() { + // TODO: How do we want it to behave in this situation? + var defBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + do something + + + + + `), this.workspace); + var callBlock = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(` + + + + + + `), this.workspace); + assertDefBlockStructure(defBlock, false, ['x'], ['arg']); + assertCallBlockStructure(callBlock, ['x'], ['arg']); + }); + }); + }); + const testSuites = [ {title: 'procedures_defreturn', hasReturn: true, defType: 'procedures_defreturn', callType: 'procedures_callreturn'}, @@ -199,15 +376,30 @@ suite('Procedures', function() { testSuites.forEach((testSuite) => { suite(testSuite.title, function() { - setup(function() { - this.defType = testSuite.defType; - this.callType = testSuite.callType; - this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); - this.defBlock.setFieldValue('proc name', 'NAME'); - this.callBlock = new Blockly.Block(this.workspace, testSuite.callType); - this.callBlock.setFieldValue('proc name', 'NAME'); + suite('Structure', function() { + setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + }); + test('Definition block', function() { + assertDefBlockStructure(this.defBlock, testSuite.hasReturn); + }); + + test('Call block', function() { + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); + assertCallBlockStructure(this.callBlock); + }); }); suite('isNameUsed', function() { + setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); + }); test('True', function() { chai.assert.isTrue( Blockly.Procedures.isNameUsed('proc name', this.workspace)); @@ -219,6 +411,11 @@ suite('Procedures', function() { }); suite('rename', function() { setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); sinon.stub(this.defBlock.getField('NAME'), 'resizeEditor_'); }); test('Simple, Programmatic', function() { @@ -323,7 +520,7 @@ suite('Procedures', function() { defInput.htmlInput_.value = ''; defInput.onHtmlInputChange_(null); - var newDefBlock = new Blockly.Block(this.workspace, this.defType); + var newDefBlock = new Blockly.Block(this.workspace, testSuite.defType); newDefBlock.setFieldValue('new name', 'NAME'); chai.assert.equal( this.defBlock.getFieldValue('NAME'), @@ -334,6 +531,13 @@ suite('Procedures', function() { }); }); suite('getCallers', function() { + setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); + }); test('Simple', function() { var callers = Blockly.Procedures.getCallers('proc name', this.workspace); @@ -341,9 +545,9 @@ suite('Procedures', function() { chai.assert.equal(callers[0], this.callBlock); }); test('Multiple Callers', function() { - var caller2 = new Blockly.Block(this.workspace, this.callType); + var caller2 = new Blockly.Block(this.workspace, testSuite.callType); caller2.setFieldValue('proc name', 'NAME'); - var caller3 = new Blockly.Block(this.workspace, this.callType); + var caller3 = new Blockly.Block(this.workspace, testSuite.callType); caller3.setFieldValue('proc name', 'NAME'); var callers = @@ -354,9 +558,9 @@ suite('Procedures', function() { chai.assert.equal(callers[2], caller3); }); test('Multiple Procedures', function() { - var def2 = new Blockly.Block(this.workspace, this.defType); + var def2 = new Blockly.Block(this.workspace, testSuite.defType); def2.setFieldValue('proc name2', 'NAME'); - var caller2 = new Blockly.Block(this.workspace, this.callType); + var caller2 = new Blockly.Block(this.workspace, testSuite.callType); caller2.setFieldValue('proc name2', 'NAME'); var callers = @@ -382,9 +586,9 @@ suite('Procedures', function() { test('Multiple Workspaces', function() { var workspace = new Blockly.Workspace(); try { - var def2 = new Blockly.Block(workspace, this.defType); + var def2 = new Blockly.Block(workspace, testSuite.defType); def2.setFieldValue('proc name', 'NAME'); - var caller2 = new Blockly.Block(workspace, this.callType); + var caller2 = new Blockly.Block(workspace, testSuite.callType); caller2.setFieldValue('proc name', 'NAME'); var callers = @@ -401,15 +605,22 @@ suite('Procedures', function() { }); }); suite('getDefinition', function() { + setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); + }); test('Simple', function() { var def = Blockly.Procedures.getDefinition('proc name', this.workspace); chai.assert.equal(def, this.defBlock); }); test('Multiple Procedures', function() { - var def2 = new Blockly.Block(this.workspace, this.defType); + var def2 = new Blockly.Block(this.workspace, testSuite.defType); def2.setFieldValue('proc name2', 'NAME'); - var caller2 = new Blockly.Block(this.workspace, this.callType); + var caller2 = new Blockly.Block(this.workspace, testSuite.callType); caller2.setFieldValue('proc name2', 'NAME'); var def = @@ -419,9 +630,9 @@ suite('Procedures', function() { test('Multiple Workspaces', function() { var workspace = new Blockly.Workspace(); try { - var def2 = new Blockly.Block(workspace, this.defType); + var def2 = new Blockly.Block(workspace, testSuite.defType); def2.setFieldValue('proc name', 'NAME'); - var caller2 = new Blockly.Block(workspace, this.callType); + var caller2 = new Blockly.Block(workspace, testSuite.callType); caller2.setFieldValue('proc name', 'NAME'); var def = @@ -555,6 +766,11 @@ suite('Procedures', function() { }); suite('Mutation', function() { setup(function() { + this.defBlock = new Blockly.Block(this.workspace, testSuite.defType); + this.defBlock.setFieldValue('proc name', 'NAME'); + this.callBlock = new Blockly.Block( + this.workspace, testSuite.callType); + this.callBlock.setFieldValue('proc name', 'NAME'); this.findParentStub = sinon.stub(Blockly.Mutator, 'findParentWs') .returns(this.workspace); }); @@ -764,6 +980,180 @@ suite('Procedures', function() { }); }); }); + /** + * Test cases for serialization tests. + * @type {Array} + */ + const testCases = [ + { + title: 'Minimal definition', + xml: '', + expectedXml: + '\n' + + ' unnamed\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure(block, testSuite.hasReturn); + }, + }, + { + title: 'Common definition', + xml: + '' + + ' do something' + + '', + expectedXml: + '\n' + + ' do something\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure(block, testSuite.hasReturn); + }, + }, + { + title: 'With vars definition', + xml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + expectedXml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure( + block, testSuite.hasReturn, ['x', 'y'], ['arg1', 'arg2']); + }, + }, + { + title: 'With pre-created vars definition', + xml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + expectedXml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure(block, testSuite.hasReturn, + ['preCreatedVar'], ['preCreatedVarId']); + }, + }, + { + title: 'With pre-created typed vars definition', + xml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + expectedXml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' do something\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure(block, testSuite.hasReturn, + ['preCreatedTypedVar'], ['preCreatedTypedVarId']); + }, + }, + { + title: 'No statements definition', + xml: + '\n' + + ' \n' + + ' do something\n' + + '', + expectedXml: + '\n' + + ' \n' + + ' do something\n' + + '', + assertBlockStructure: + (block) => { + assertDefBlockStructure(block, true, [], [], false); + }, + }, + { + title: 'Minimal caller', + xml: '', + expectedXml: + '\n' + + ' \n' + + '', + assertBlockStructure: + (block) => { + assertCallBlockStructure(block); + }, + }, + { + title: 'Common caller', + xml: + '\n' + + ' \n' + + '', + expectedXml: + '\n' + + ' \n' + + '', + assertBlockStructure: + (block) => { + assertCallBlockStructure(block); + }, + }, + { + title: 'With pre-created vars caller', + xml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + '', + expectedXml: + '\n' + + ' \n' + + ' \n' + + ' \n' + + '', + assertBlockStructure: + (block) => { + assertCallBlockStructure(block, ['preCreatedVar'], ['preCreatedVarId']); + }, + }, + ]; + testHelpers.runSerializationTestSuite(testCases); }); }); }); diff --git a/tests/mocha/procedures_test_helpers.js b/tests/mocha/procedures_test_helpers.js new file mode 100644 index 000000000..3abe470ca --- /dev/null +++ b/tests/mocha/procedures_test_helpers.js @@ -0,0 +1,135 @@ +/** + * Asserts that the procedure definition or call block has the expected var + * models. + * @param {!Blockly.Block} block The procedure definition or call block to + * check. + * @param {!Array} varIds An array of variable ids. + * @private + */ +function assertBlockVarModels(block, varIds) { + const expectedVarModels = []; + for (let i = 0; i < varIds.length; i++) { + expectedVarModels.push(block.workspace.getVariableById(varIds[i])); + } + chai.assert.sameDeepOrderedMembers(block.getVarModels(), expectedVarModels); +} + +/** + * Asserts that the procedure call block has the expected arguments. + * @param {!Blockly.Block} callBlock The procedure definition block. + * @param {Array=} args An array of argument names. + * @private + */ +function assertCallBlockArgsStructure_(callBlock, args) { + // inputList also contains "TOPROW" + chai.assert.equal(callBlock.inputList.length - 1, args.length, + 'call block has the expected number of args'); + + for (let i = 0; i < args.length; i++) { + const expectedName = args[i]; + const callInput = callBlock.inputList[i + 1]; + chai.assert.equal(callInput.type, Blockly.INPUT_VALUE); + chai.assert.equal(callInput.name, 'ARG' + i); + chai.assert.equal(callInput.fieldRow[0].getValue(), expectedName, + 'Call block consts did not match expected.'); + } + chai.assert.sameOrderedMembers(callBlock.getVars(), args); +} + +/** + * Asserts that the procedure definition block has the expected inputs and + * fields. + * @param {!Blockly.Block} defBlock The procedure definition block. + * @param {boolean=} hasReturn If we expect the procedure def to have a return + * input or not. + * @param {Array=} args An array of argument names. + * @param {Array=} varIds An array of variable ids. + * @param {boolean=} hasStatements If we expect the procedure def to have a + * statement input or not. + */ +function assertDefBlockStructure(defBlock, hasReturn = false, + args = [], varIds = [], hasStatements = true) { + if (hasStatements) { + chai.assert.isNotNull(defBlock.getInput('STACK'), + 'Def block should have STACK input'); + } else { + chai.assert.isNull(defBlock.getInput('STACK'), + 'Def block should not have STACK input'); + } + if (hasReturn) { + chai.assert.isNotNull(defBlock.getInput('RETURN'), + 'Def block should have RETURN input'); + } else { + chai.assert.isNull(defBlock.getInput('RETURN'), + 'Def block should not have RETURN input'); + } + if (args.length) { + chai.assert.include(defBlock.toString(), 'with', + 'Def block string should include "with"'); + } else { + chai.assert.notInclude(defBlock.toString(), 'with', + 'Def block string should not include "with"'); + } + + chai.assert.sameOrderedMembers(defBlock.getVars(), args); + assertBlockVarModels(defBlock, varIds); +} + +/** + * Asserts that the procedure definition block has the expected inputs and + * fields. + * @param {!Blockly.Block} callBlock The procedure call block. + * @param {Array=} args An array of argument names. + * @param {Array=} varIds An array of variable ids. + */ +function assertCallBlockStructure(callBlock, args = [], varIds = []) { + if (args.length) { + chai.assert.include(callBlock.toString(), 'with'); + } else { + chai.assert.notInclude(callBlock.toString(), 'with'); + } + + assertCallBlockArgsStructure_(callBlock, args); + assertBlockVarModels(callBlock, varIds); +} + +/** + * Creates procedure definition block using domToBlock call. + * @param {!Blockly.Workspace} workspace The Blockly workspace. + * @param {boolean=} hasReturn Whether the procedure definition should have + * return. + * @param {Array=} args An array of argument names. + * @return {Blockly.Block} The created block. + */ +function createProcDefBlock( + workspace, hasReturn = false, args = []) { + const type = hasReturn ? + 'procedures_defreturn' : 'procedures_defnoreturn'; + let xml = ''; + for (let i = 0; i < args.length; i ++) { + xml += + ' \n'; + } + xml += + ' proc name' + + ''; + return Blockly.Xml.domToBlock(Blockly.Xml.textToDom(xml), workspace); +} + +/** + * Creates procedure call block using domToBlock call. + * @param {!Blockly.Workspace} workspace The Blockly workspace. + * @param {boolean=} hasReturn Whether the corresponding procedure definition + * has return. + * @return {Blockly.Block} The created block. + */ +function createProcCallBlock( + workspace, hasReturn = false) { + const type = hasReturn ? + 'procedures_callreturn' : 'procedures_callnoreturn'; + return Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + + ' ' + + '' + ), workspace); +} diff --git a/tests/mocha/xml_procedures_test.js b/tests/mocha/xml_procedures_test.js deleted file mode 100644 index 26a0da858..000000000 --- a/tests/mocha/xml_procedures_test.js +++ /dev/null @@ -1,308 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -goog.require('Blockly.Blocks.procedures'); -goog.require('Blockly.Msg'); - -suite('Procedures XML', function() { - setup(function() { - sharedTestSetup.call(this); - }); - teardown(function() { - sharedTestTeardown.call(this); - }); - - suite('Deserialization', function() { - setup(function() { - this.workspace = new Blockly.Workspace(); - - this.callForAllTypes = function(func) { - var typesArray = [ - ['procedures_defnoreturn', 'procedures_callnoreturn'], - ['procedures_defreturn', 'procedures_callreturn'] - ]; - - for (var i = 0, types; (types = typesArray[i]); i++) { - var context = Object.create(null); - context.workspace = this.workspace; - context.defType = types[0]; - context.callType = types[1]; - - func.call(context); - - this.workspace.clear(); - } - }; - }); - teardown(function() { - workspaceTeardown.call(this, this.workspace); - }); - - suite('Definition Blocks', function() { - test('Minimal', function() { - this.callForAllTypes(function() { - var xml = Blockly.Xml.textToDom( - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - // TODO: Is this how you want this to work? or do you want it to - // be 'unnamed'? - chai.assert.equal(block.getFieldValue('NAME'), ''); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - // This is like what's in the toolbox. - test('Common', function() { - this.callForAllTypes(function() { - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - test('Arg Vars Pre-Created', function() { - this.callForAllTypes(function() { - this.workspace.createVariable('x', '', 'arg'); - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.deepEqual(block.arguments_, ['x']); - chai.assert.deepEqual(block.argumentVarModels_, - [this.workspace.getVariableById('arg')]); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - test('Arg Vars Not Created', function() { - this.callForAllTypes(function() { - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.deepEqual(block.arguments_, ['x']); - chai.assert.deepEqual(block.argumentVarModels_, - [this.workspace.getVariableById('arg')]); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - // TODO: I don't know a lot about typing vars, and even less out it in - // this context. Is allowing typed vars to be args the correct behavior? - test('Arg Vars Pre-Created Typed', function() { - this.callForAllTypes(function() { - this.workspace.createVariable('x', 'type', 'arg'); - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.deepEqual(block.arguments_, ['x']); - chai.assert.deepEqual(block.argumentVarModels_, - [this.workspace.getVariableById('arg')]); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - test('Statements False', function() { - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); - chai.assert.isNull(block.getInput('STACK')); - }); - test('Statements True', function() { - var xml = Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(xml, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); - chai.assert.isNotNull(block.getInput('STACK')); - }); - }); - suite('Call Blocks', function() { - test('Caller W/ Def', function() { - this.callForAllTypes(function() { - Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' do something' + - '' - ), this.workspace); - var callerXML = Blockly.Xml.textToDom( - '' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(callerXML, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - // TODO: argumentVarModels_ is undefined for call_return, but - // defined for call_noreturn. Make it defined for both. - /* chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); */ - }); - }); - // TODO: I couldn't get this test (of creating a definition) to work - // b/c of the events delay. - test.skip('Caller No Def', function() { - this.callForAllTypes(function() { - var callerXML = Blockly.Xml.textToDom( - '' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(callerXML, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.isArray(block.arguments_); - chai.assert.isEmpty(block.arguments_); - // TODO: argumentVarModels_ is undefined for call_return, but - // defined for call_noreturn. Make it defined for both. - /* chai.assert.isArray(block.argumentVarModels_); - chai.assert.isEmpty(block.argumentVarModels_); */ - chai.assert.equal(this.workspace.getAllBlocks(false).count, 2); - }); - }); - test('Caller W/ Params', function() { - this.callForAllTypes(function() { - Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ), this.workspace); - var callerXML = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - var block = Blockly.Xml.domToBlock(callerXML, this.workspace); - - chai.assert.equal( - block.getFieldValue('NAME'), - 'do something'); - chai.assert.deepEqual(block.arguments_, ['x']); - chai.assert.deepEqual(block.argumentVarModels_, - [this.workspace.getVariableById('arg')]); - }); - }); - // TODO: How do you want it to behave in this situation? - test.skip('Caller W/out Params', function() { - this.callForAllTypes(function() { - Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ), this.workspace); - var callerXML = Blockly.Xml.textToDom( - '' + - ' ' + - '' - ); - // TODO: Remove this when you fix this test. - // eslint-disable-next-line no-unused-vars - var block = Blockly.Xml.domToBlock(callerXML, this.workspace); - }); - }); - // TODO: How do you want it to behave in this situation? - test.skip('Caller W/ Bad Params', function() { - this.callForAllTypes(function() { - Blockly.Xml.domToBlock(Blockly.Xml.textToDom( - '' + - ' do something' + - ' ' + - ' ' + - ' ' + - '' - ), this.workspace); - var callerXML = Blockly.Xml.textToDom( - '' + - ' ' + - ' ' + - ' ' + - '' - ); - // TODO: Remove this when you fix this test. - // eslint-disable-next-line no-unused-vars - var block = Blockly.Xml.domToBlock(callerXML, this.workspace); - }); - }); - }); - }); -});