/** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.module('Blockly.test.logicTernary'); const eventUtils = goog.require('Blockly.Events.utils'); const {runSerializationTestSuite} = goog.require('Blockly.test.helpers.serialization'); const {sharedTestSetup, sharedTestTeardown} = goog.require('Blockly.test.helpers.setupTeardown'); suite('Logic ternary', function() { setup(function() { sharedTestSetup.call(this); this.workspace = new Blockly.Workspace(); }); teardown(function() { sharedTestTeardown.call(this); }); /** * Asserts that the logic ternary block has the expected inputs and fields. * @param {!Blockly.Block} block The block to check. * @param {boolean=} inputsInline Whether the inputs are expected to be * inline. */ function assertBlockStructure(block, inputsInline = false) { chai.assert.equal(block.type, 'logic_ternary'); const inputs = block.inputList; chai.assert.exists(inputs, 'Has inputList'); chai.assert.lengthOf(inputs, 3); const ifInput = block.getInput('IF'); chai.assert.exists(ifInput, 'Has "IF" input'); chai.assert.equal(ifInput.connection.check_.length, 1); chai.assert.equal(ifInput.connection.check_[0], 'Boolean'); chai.assert.exists(block.onchangeWrapper_, 'Has onchange handler'); if (inputsInline) { chai.assert.isTrue(block.inputsInline); } else { // inputsInline can be undefined chai.assert.isNotTrue(block.inputsInline); } } test('Structure', function() { const block = this.workspace.newBlock('logic_ternary'); assertBlockStructure(block); }); /** * Test cases for serialization tests. * @type {Array} */ const testCases = [ {title: 'Empty XML', xml: '', expectedXml: '', assertBlockStructure: (block) => { assertBlockStructure(block); }, }, {title: 'Inputs inline', xml: '', assertBlockStructure: (block) => { assertBlockStructure(block, true); }, }, ]; runSerializationTestSuite(testCases); suite('Connections', function() { function connectParentAndCheckConnections( block, parent, parentInputName, opt_thenInput, opt_elseInput) { parent.getInput(parentInputName).connection.connect(block.outputConnection); eventUtils.TEST_ONLY.fireNow(); // Force synchronous onchange() call. chai.assert.equal(block.getParent(), parent, 'Successful connection to parent'); if (opt_thenInput) { chai.assert.equal(opt_thenInput.getParent(), block, 'Input THEN still connected after connecting parent'); } if (opt_elseInput) { chai.assert.equal(opt_elseInput.getParent(), block, 'Input ELSE still connected after connecting parent'); } } function connectThenInputAndCheckConnections( block, thenInput, opt_elseInput, opt_parent) { block.getInput('THEN').connection.connect(thenInput.outputConnection); eventUtils.TEST_ONLY.fireNow(); // Force synchronous onchange() call. chai.assert.equal(thenInput.getParent(), block, 'THEN is connected'); if (opt_parent) { chai.assert.equal(block.getParent(), opt_parent, 'Still connected to parent after connecting THEN'); } if (opt_elseInput) { chai.assert.equal(opt_elseInput.getParent(), block, 'Input ELSE still connected after connecting THEN'); } } function connectElseInputAndCheckConnections( block, elseInput, opt_thenInput, opt_parent) { block.getInput('ELSE').connection.connect(elseInput.outputConnection); eventUtils.TEST_ONLY.fireNow(); // Force synchronous onchange() call. chai.assert.equal(elseInput.getParent(), block, 'ELSE is connected'); if (opt_parent) { chai.assert.equal(block.getParent(), opt_parent, 'Still connected to parent after connecting ELSE'); } if (opt_thenInput) { chai.assert.equal(opt_thenInput.getParent(), block, 'Input THEN still connected after connecting ELSE'); } } function connectInputsAndCheckConnections( block, thenInput, elseInput, opt_parent) { connectThenInputAndCheckConnections(block, thenInput, null, opt_parent); connectElseInputAndCheckConnections(block, elseInput, thenInput, opt_parent); } setup(function() { this.block = this.workspace.newBlock('logic_ternary'); }); suite('No parent', function() { test('Attach inputs same type', function() { const string1 = this.workspace.newBlock('text'); const string2 = this.workspace.newBlock('text_charAt'); connectInputsAndCheckConnections(this.block, string1, string2); }); test('Attach inputs different types', function() { const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectInputsAndCheckConnections(this.block, string, number); }); }); suite('With parent already attached', function() { test('Attach inputs same type with matching parent', function() { const parent = this.workspace.newBlock('text_trim'); connectParentAndCheckConnections(this.block, parent, 'TEXT'); const string1 = this.workspace.newBlock('text'); const string2 = this.workspace.newBlock('text_charAt'); connectInputsAndCheckConnections(this.block, string1, string2, parent); }); test('Attach inputs different types with unchecked parent', function() { const parent = this.workspace.newBlock('text_print'); connectParentAndCheckConnections(this.block, parent, 'TEXT'); const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectInputsAndCheckConnections(this.block, string, number, parent); }); test('Attach inputs different types with permissive parent', function() { const parent = this.workspace.newBlock('text_length'); // Allows String or Array connectParentAndCheckConnections(this.block, parent, 'VALUE'); const string = this.workspace.newBlock('text'); const array = this.workspace.newBlock('lists_create_empty'); connectInputsAndCheckConnections(this.block, string, array, parent); }); test('Attach mismatch type to then causes break with parent', function() { const parent = this.workspace.newBlock('text_length'); // Allows String or Array connectParentAndCheckConnections(this.block, parent, 'VALUE'); const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectElseInputAndCheckConnections(this.block, string, null, parent); // Adding mismatching number. connectThenInputAndCheckConnections(this.block, number, string); chai.assert.equal(this.block.getRootBlock(), this.block, 'Disconnected from parent'); }); test('Attach mismatch type to else causes break with parent', function() { const parent = this.workspace.newBlock('text_length'); // Allows String or Array connectParentAndCheckConnections(this.block, parent, 'VALUE'); const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectThenInputAndCheckConnections(this.block, string, null, parent); // Adding mismatching number. connectElseInputAndCheckConnections(this.block, number, string); chai.assert.equal(this.block.getRootBlock(), this.block, 'Disconnected from parent'); }); }); suite('Attaching parent after inputs', function() { test('Unchecked parent with inputs different types', function() { const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectInputsAndCheckConnections(this.block, string, number); const parent = this.workspace.newBlock('text_print'); connectParentAndCheckConnections( this.block, parent, 'TEXT', string, number); }); test('Permissive parent with inputs different types', function() { const string = this.workspace.newBlock('text'); const array = this.workspace.newBlock('lists_create_empty'); connectInputsAndCheckConnections(this.block, string, array); const parent = this.workspace.newBlock('text_print'); connectParentAndCheckConnections( this.block, parent, 'TEXT', string, array); }); test('Mismatch with then causes break with then', function() { const number = this.workspace.newBlock('math_number'); const string = this.workspace.newBlock('text'); connectInputsAndCheckConnections(this.block, number, string); const parent = this.workspace.newBlock('text_trim'); connectParentAndCheckConnections( this.block, parent, 'TEXT', null, string); chai.assert.equal(number.getRootBlock(), number, 'Input THEN disconnected'); }); test('Mismatch with else causes break with else', function() { const string = this.workspace.newBlock('text'); const number = this.workspace.newBlock('math_number'); connectInputsAndCheckConnections(this.block, string, number); const parent = this.workspace.newBlock('text_trim'); connectParentAndCheckConnections(this.block, parent, 'TEXT', string); chai.assert.equal(number.getRootBlock(), number, 'Input ELSE disconnected'); }); }); }); });