diff --git a/core/input.js b/core/input.js index ddfa1d772..3da031451 100644 --- a/core/input.js +++ b/core/input.js @@ -106,8 +106,9 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) { throw Error('index ' + index + ' out of bounds.'); } - // Empty string, Null or undefined generates no field, unless field is named. - if (!field && !opt_name) { + // Falsy field values don't generate a field, unless the field is an empty + // string and named. + if (!field && !(field == '' && opt_name)) { return index; } // Generate a FieldLabel when given a plain text field. diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index 2f037075c..7510fd7eb 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -24,7 +24,6 @@ - diff --git a/tests/jsunit/input_test.js b/tests/jsunit/input_test.js deleted file mode 100644 index 35bdd4f30..000000000 --- a/tests/jsunit/input_test.js +++ /dev/null @@ -1,202 +0,0 @@ -/** - * @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_suffix() { - 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]); -} diff --git a/tests/mocha/index.html b/tests/mocha/index.html index 862398539..49339b471 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -45,6 +45,7 @@ + diff --git a/tests/mocha/input_test.js b/tests/mocha/input_test.js new file mode 100644 index 000000000..b4f95f8b6 --- /dev/null +++ b/tests/mocha/input_test.js @@ -0,0 +1,311 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 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. + */ + +suite('Inputs', function() { + setup(function() { + Blockly.defineBlocksWithJsonArray([ + { + "type": "empty_block", + "message0": "", + "args0": [] + }, + ]); + + this.workspace = Blockly.inject('blocklyDiv'); + this.block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom( + '' + ), this.workspace); + + this.renderStub = sinon.stub(this.block, 'render'); + this.bumpNeighboursStub = sinon.stub(this.block, 'bumpNeighbours_'); + + this.dummy = this.block.appendDummyInput('DUMMY'); + this.value = this.block.appendValueInput('VALUE'); + this.statement = this.block.appendStatementInput('STATEMENT'); + + this.renderStub.resetHistory(); + this.bumpNeighboursStub.resetHistory(); + }); + teardown(function() { + this.renderStub.restore(); + this.bumpNeighboursStub.restore(); + + delete Blockly.Blocks['empty_block']; + this.workspace.dispose(); + }); + suite('Insert Field At', function() { + suite('Index Bounds', function() { + test('< 0', function() { + var field = new Blockly.FieldLabel('field'); + chai.assert.throws(function() { + this.dummy.insertFieldAt(-1, field); + }); + }); + test('> length', function() { + var field = new Blockly.FieldLabel('field'); + chai.assert.throws(function() { + this.dummy.insertFieldAt(1, field); + }); + }); + }); + suite('Values', function() { + // We're mostly just testing that it doesn't throw errors. + test('Field', function() { + var field = new Blockly.FieldLabel('field'); + this.dummy.insertFieldAt(0, field); + chai.assert.equal(this.dummy.fieldRow[0], field); + }); + test('String', function() { + this.dummy.insertFieldAt(0, 'field'); + chai.assert.instanceOf(this.dummy.fieldRow[0], Blockly.FieldLabel); + }); + test('Empty String', function() { + this.dummy.insertFieldAt(0, ''); + chai.assert.isEmpty(this.dummy.fieldRow); + }); + test('Empty String W/ Name', function() { + this.dummy.insertFieldAt(0, '', 'NAME'); + chai.assert.instanceOf(this.dummy.fieldRow[0], Blockly.FieldLabel); + }); + test('Null', function() { + this.dummy.insertFieldAt(0, null); + chai.assert.isEmpty(this.dummy.fieldRow); + }); + test('Undefined', function() { + this.dummy.insertFieldAt(0, undefined); + chai.assert.isEmpty(this.dummy.fieldRow); + }); + }); + suite('Prefixes and Suffixes', function() { + test('Prefix', function() { + var field = new Blockly.FieldLabel('field'); + var prefix = new Blockly.FieldLabel('prefix'); + field.prefixField = prefix; + + this.dummy.appendField(field); + chai.assert.deepEqual(this.dummy.fieldRow, [prefix, field]); + }); + test('Suffix', function() { + var field = new Blockly.FieldLabel('field'); + var suffix = new Blockly.FieldLabel('suffix'); + field.suffixField = suffix; + + this.dummy.appendField(field); + chai.assert.deepEqual(this.dummy.fieldRow, [field, suffix]); + }); + test('Prefix and Suffix', function() { + var field = new Blockly.FieldLabel('field'); + var prefix = new Blockly.FieldLabel('prefix'); + var suffix = new Blockly.FieldLabel('suffix'); + field.prefixField = prefix; + field.suffixField = suffix; + + this.dummy.appendField(field); + chai.assert.deepEqual(this.dummy.fieldRow, [prefix, field, suffix]); + }); + test('Dropdown - Prefix', function() { + var field = new Blockly.FieldDropdown( + [ + ['prefix option1', 'OPTION1'], + ['prefix option2', 'OPTION2'] + ] + ); + + this.dummy.appendField(field); + chai.assert.equal(this.dummy.fieldRow.length, 2); + }); + test('Dropdown - Suffix', function() { + var field = new Blockly.FieldDropdown( + [ + ['option1 suffix', 'OPTION1'], + ['option2 suffix', 'OPTION2'] + ] + ); + + this.dummy.appendField(field); + chai.assert.equal(this.dummy.fieldRow.length, 2); + }); + test('Dropdown - Prefix and Suffix', function() { + var field = new Blockly.FieldDropdown( + [ + ['prefix option1 suffix', 'OPTION1'], + ['prefix option2 suffix', 'OPTION2'] + ] + ); + + this.dummy.appendField(field); + chai.assert.equal(this.dummy.fieldRow.length, 3); + }); + }); + suite('Field Initialization', function() { + test('Rendered', function() { + var field = new Blockly.FieldLabel('field'); + var setBlockSpy = sinon.spy(field, 'setSourceBlock'); + var initSpy = sinon.spy(field, 'init'); + + this.dummy.insertFieldAt(0, field); + chai.assert(setBlockSpy.calledOnce); + chai.assert.equal(setBlockSpy.getCall(0).args[0], this.block); + chai.assert(initSpy.calledOnce); + console.log(this.renderStub.callCount); + chai.assert(this.renderStub.calledOnce); + chai.assert(this.bumpNeighboursStub.calledOnce); + + setBlockSpy.restore(); + initSpy.restore(); + }); + // TODO: InsertFieldAt does not properly handle initialization in + // headless mode. + test.skip('Headless', function() { + var field = new Blockly.FieldLabel('field'); + var setBlockSpy = sinon.spy(field, 'setSourceBlock'); + var initModelSpy = sinon.spy(field, 'initModel'); + + this.block.rendered = false; + + this.dummy.insertFieldAt(0, field); + chai.assert(setBlockSpy.calledOnce); + chai.assert.equal(setBlockSpy.getCall(0).args[0], this.block); + chai.assert(initModelSpy.calledOnce); + chai.assert(this.renderStub.notCalled); + chai.assert(this.bumpNeighboursStub.notCalled); + + setBlockSpy.restore(); + initModelSpy.restore(); + }); + }); + }); + suite('Remove Field', function() { + test('Field Not Found', function() { + chai.assert.throws(function() { + this.dummy.removeField('FIELD'); + }); + }); + test('Rendered', function() { + var field = new Blockly.FieldLabel('field'); + var disposeSpy = sinon.spy(field, 'dispose'); + this.dummy.appendField(field, 'FIELD'); + + this.renderStub.resetHistory(); + this.bumpNeighboursStub.resetHistory(); + + this.dummy.removeField('FIELD'); + chai.assert(disposeSpy.calledOnce); + chai.assert(this.renderStub.calledOnce); + chai.assert(this.bumpNeighboursStub.calledOnce); + }); + test('Headless', function() { + var field = new Blockly.FieldLabel('field'); + var disposeSpy = sinon.spy(field, 'dispose'); + this.dummy.appendField(field, 'FIELD'); + + this.renderStub.resetHistory(); + this.bumpNeighboursStub.resetHistory(); + + this.block.rendered = false; + + this.dummy.removeField('FIELD'); + chai.assert(disposeSpy.calledOnce); + chai.assert(this.renderStub.notCalled); + chai.assert(this.bumpNeighboursStub.notCalled); + }); + }); + suite('Field Ordering/Manipulation', function() { + setup(function() { + this.a = new Blockly.FieldLabel('a'); + this.b = new Blockly.FieldLabel('b'); + this.c = new Blockly.FieldLabel('c'); + }); + test('Append A, B, C', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.b, this.c]); + }); + test('Append B, C; Insert A at Start', function() { + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + this.dummy.insertFieldAt(0, this.a, 'A'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.b, this.c]); + }); + test('Append A, C; Insert B Between', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.c, 'C'); + this.dummy.insertFieldAt(1, this.b, 'B'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.b, this.c]); + }); + test('Append A, B; Insert C at End', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.insertFieldAt(2, this.c, 'C'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.b, this.c]); + }); + test('Append A, B, C; Remove A, B, C', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + + this.dummy.removeField('A'); + this.dummy.removeField('B'); + this.dummy.removeField('C'); + + chai.assert.isEmpty(this.dummy.fieldRow); + }); + test('Append A, B, C; Remove A', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + + this.dummy.removeField('A'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.b, this.c]); + }); + test('Append A, B, C; Remove B', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + + this.dummy.removeField('B'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.c]); + }); + test('Append A, B, C; Remove C', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.appendField(this.c, 'C'); + + this.dummy.removeField('C'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.a, this.b]); + }); + test('Append A, B; Remove A; Append C', function() { + this.dummy.appendField(this.a, 'A'); + this.dummy.appendField(this.b, 'B'); + this.dummy.removeField('A'); + this.dummy.appendField(this.c, 'C'); + + chai.assert.deepEqual(this.dummy.fieldRow, [this.b, this.c]); + }); + }); +});