diff --git a/core/field_checkbox.js b/core/field_checkbox.js
index b37217068..f096b1e9a 100644
--- a/core/field_checkbox.js
+++ b/core/field_checkbox.js
@@ -32,7 +32,8 @@ goog.require('Blockly.utils');
/**
* Class for a checkbox field.
- * @param {string} state The initial state of the field ('TRUE' or 'FALSE').
+ * @param {string=} opt_state The initial state of the field ('TRUE' or
+ * 'FALSE'), defaults to 'FALSE'.
* @param {Function=} opt_validator A function that is executed when a new
* option is selected. Its sole argument is the new checkbox state. If
* it returns a value, this becomes the new checkbox state, unless the
@@ -40,10 +41,10 @@ goog.require('Blockly.utils');
* @extends {Blockly.Field}
* @constructor
*/
-Blockly.FieldCheckbox = function(state, opt_validator) {
+Blockly.FieldCheckbox = function(opt_state, opt_validator) {
Blockly.FieldCheckbox.superClass_.constructor.call(this, '', opt_validator);
// Set the initial state.
- this.setValue(state);
+ this.setValue(opt_state);
};
goog.inherits(Blockly.FieldCheckbox, Blockly.Field);
diff --git a/core/field_colour.js b/core/field_colour.js
index a6a501efa..d0796acf0 100644
--- a/core/field_colour.js
+++ b/core/field_colour.js
@@ -34,7 +34,8 @@ goog.require('goog.math.Size');
/**
* Class for a colour input field.
- * @param {string} colour The initial colour in '#rrggbb' format.
+ * @param {string=} opt_colour The initial colour in '#rrggbb' format, defaults
+ * to the first value in the default colour array.
* @param {Function=} opt_validator A function that is executed when a new
* colour is selected. Its sole argument is the new colour value. Its
* return value becomes the selected colour, unless it is undefined, in
@@ -43,8 +44,10 @@ goog.require('goog.math.Size');
* @extends {Blockly.Field}
* @constructor
*/
-Blockly.FieldColour = function(colour, opt_validator) {
- Blockly.FieldColour.superClass_.constructor.call(this, colour, opt_validator);
+Blockly.FieldColour = function(opt_colour, opt_validator) {
+ opt_colour = opt_colour || Blockly.FieldColour.COLOURS[0];
+ Blockly.FieldColour.superClass_.constructor
+ .call(this, opt_colour, opt_validator);
this.setText(Blockly.Field.NBSP + Blockly.Field.NBSP + Blockly.Field.NBSP);
};
goog.inherits(Blockly.FieldColour, Blockly.Field);
diff --git a/core/field_date.js b/core/field_date.js
index de80d026b..7cbe31a16 100644
--- a/core/field_date.js
+++ b/core/field_date.js
@@ -40,7 +40,7 @@ goog.require('goog.ui.DatePicker');
/**
* Class for a date input field.
- * @param {string} date The initial date.
+ * @param {string=} opt_date The initial date, defaults to the current day.
* @param {Function=} opt_validator A function that is executed when a new
* date is selected. Its sole argument is the new date value. Its
* return value becomes the selected date, unless it is undefined, in
@@ -49,12 +49,12 @@ goog.require('goog.ui.DatePicker');
* @extends {Blockly.Field}
* @constructor
*/
-Blockly.FieldDate = function(date, opt_validator) {
- if (!date) {
- date = new goog.date.Date().toIsoString(true);
+Blockly.FieldDate = function(opt_date, opt_validator) {
+ if (!opt_date) {
+ opt_date = new goog.date.Date().toIsoString(true);
}
- Blockly.FieldDate.superClass_.constructor.call(this, date, opt_validator);
- this.setValue(date);
+ Blockly.FieldDate.superClass_.constructor.call(this, opt_date, opt_validator);
+ this.setValue(opt_date);
};
goog.inherits(Blockly.FieldDate, Blockly.Field);
diff --git a/core/field_image.js b/core/field_image.js
index dfebb2d2c..dda922364 100644
--- a/core/field_image.js
+++ b/core/field_image.js
@@ -34,9 +34,9 @@ goog.require('goog.math.Size');
/**
* Class for an image on a block.
- * @param {string} src The URL of the image.
- * @param {number} width Width of the image.
- * @param {number} height Height of the image.
+ * @param {string=} src The URL of the image, defaults to an empty string.
+ * @param {!(string|number)} width Width of the image.
+ * @param {!(string|number)} height Height of the image.
* @param {string=} opt_alt Optional alt text for when block is collapsed.
* @param {Function=} opt_onClick Optional function to be called when the image
* is clicked. If opt_onClick is defined, opt_alt must also be defined.
@@ -48,15 +48,26 @@ Blockly.FieldImage = function(src, width, height,
opt_alt, opt_onClick, opt_flipRtl) {
this.sourceBlock_ = null;
+
+ if (isNaN(height) || isNaN(width)) {
+ throw Error('Height and width values of an image field must cast to' +
+ ' numbers.');
+ }
+
// Ensure height and width are numbers. Strings are bad at math.
this.height_ = Number(height);
this.width_ = Number(width);
+ if (this.height_ <= 0 || this.width_ <= 0) {
+ throw Error('Height and width values of an image field must be greater' +
+ ' than 0.');
+ }
this.size_ = new goog.math.Size(this.width_,
this.height_ + 2 * Blockly.BlockSvg.INLINE_PADDING_Y);
+
this.flipRtl_ = opt_flipRtl;
this.tooltip_ = '';
- this.setValue(src);
- this.setText(opt_alt);
+ this.setValue(src || '');
+ this.setText(opt_alt || '');
if (typeof opt_onClick == 'function') {
this.clickHandler_ = opt_onClick;
diff --git a/core/field_label.js b/core/field_label.js
index cecf2b8c0..2db51c03a 100644
--- a/core/field_label.js
+++ b/core/field_label.js
@@ -36,7 +36,8 @@ goog.require('goog.math.Size');
/**
* Class for a non-editable, non-serializable text field.
- * @param {string} text The initial content of the field.
+ * @param {string=} text The initial content of the field, defaults to an
+ * empty string.
* @param {string=} opt_class Optional CSS class for the field's text.
* @extends {Blockly.Field}
* @constructor
@@ -44,7 +45,10 @@ goog.require('goog.math.Size');
Blockly.FieldLabel = function(text, opt_class) {
this.size_ = new goog.math.Size(0, 17.5);
this.class_ = opt_class;
- this.setValue(text);
+ if (text === null || text === undefined) {
+ text = '';
+ }
+ this.setValue(String(text));
this.tooltip_ = '';
};
goog.inherits(Blockly.FieldLabel, Blockly.Field);
diff --git a/core/field_label_serializable.js b/core/field_label_serializable.js
index 182ecbbd4..b8f579f7f 100644
--- a/core/field_label_serializable.js
+++ b/core/field_label_serializable.js
@@ -33,8 +33,8 @@ goog.require('Blockly.utils');
/**
* Class for a non-editable, serializable text field.
- * @param {string} text The initial content of the field.
- * @param {string} opt_class Optional CSS class for the field's text.
+ * @param {!string} text The initial content of the field.
+ * @param {string=} opt_class Optional CSS class for the field's text.
* @extends {Blockly.FieldLabel}
* @constructor
*
diff --git a/core/field_textinput.js b/core/field_textinput.js
index 3fd38c028..088a28109 100644
--- a/core/field_textinput.js
+++ b/core/field_textinput.js
@@ -37,7 +37,8 @@ goog.require('goog.math.Coordinate');
/**
* Class for an editable text field.
- * @param {string} text The initial content of the field.
+ * @param {string=} text The initial content of the field, defaults to an
+ * empty string.
* @param {Function=} opt_validator An optional function that is called
* to validate any constraints on what the user entered. Takes the new
* text as an argument and returns either the accepted text, a replacement
@@ -46,7 +47,10 @@ goog.require('goog.math.Coordinate');
* @constructor
*/
Blockly.FieldTextInput = function(text, opt_validator) {
- Blockly.FieldTextInput.superClass_.constructor.call(this, text,
+ if (text === null || text === undefined) {
+ text = '';
+ }
+ Blockly.FieldTextInput.superClass_.constructor.call(this, String(text),
opt_validator);
};
goog.inherits(Blockly.FieldTextInput, Blockly.Field);
diff --git a/tests/mocha/field_angle_test.js b/tests/mocha/field_angle_test.js
new file mode 100644
index 000000000..de468e48f
--- /dev/null
+++ b/tests/mocha/field_angle_test.js
@@ -0,0 +1,248 @@
+/**
+ * @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 ('Angle Fields', function() {
+ function assertValue(angleField, expectedValue, opt_expectedText) {
+ var actualValue = angleField.getValue();
+ var actualText = angleField.getText();
+ opt_expectedText = opt_expectedText || String(expectedValue);
+ assertEquals(String(actualValue), String(expectedValue));
+ assertEquals(parseFloat(actualValue), expectedValue);
+ assertEquals(actualText, opt_expectedText);
+ }
+ function assertValueDefault(angleField) {
+ assertValue(angleField, 0);
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var angleField = new Blockly.FieldAngle();
+ assertValueDefault(angleField);
+ });
+ test('Null', function() {
+ var angleField = new Blockly.FieldAngle(null);
+ assertValueDefault(angleField);
+ });
+ test('Undefined', function() {
+ var angleField = new Blockly.FieldAngle(undefined);
+ assertValueDefault(angleField);
+ });
+ test('Non-Parsable String', function() {
+ var angleField = new Blockly.FieldAngle('bad');
+ assertValueDefault(angleField);
+ });
+ test('NaN', function() {
+ var angleField = new Blockly.FieldAngle(NaN);
+ assertValueDefault(angleField);
+ });
+ test('Integer', function() {
+ var angleField = new Blockly.FieldAngle(1);
+ assertValue(angleField, 1);
+ });
+ test('Float', function() {
+ var angleField = new Blockly.FieldAngle(1.5);
+ assertValue(angleField, 1.5);
+ });
+ test('Integer String', function() {
+ var angleField = new Blockly.FieldAngle('1');
+ assertValue(angleField, 1);
+ });
+ test('Float String', function() {
+ var angleField = new Blockly.FieldAngle('1.5');
+ assertValue(angleField, 1.5);
+ });
+ test('> 360°', function() {
+ var angleField = new Blockly.FieldAngle(362);
+ assertValue(angleField, 2);
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var angleField = Blockly.FieldAngle.fromJson({});
+ assertValueDefault(angleField);
+ });
+ test('Null', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:null });
+ assertValueDefault(angleField);
+ });
+ test('Undefined', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:undefined });
+ assertValueDefault(angleField);
+ });
+ test('Non-Parsable String', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:'bad' });
+ assertValueDefault(angleField);
+ });
+ test('NaN', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:NaN });
+ assertValueDefault(angleField);
+ });
+ test('Integer', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:1 });
+ assertValue(angleField, 1);
+ });
+ test('Float', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:1.5 });
+ assertValue(angleField, 1.5);
+ });
+ test('Integer String', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:'1' });
+ assertValue(angleField, 1);
+ });
+ test('Float String', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:'1.5' });
+ assertValue(angleField, 1.5);
+ });
+ test('> 360°', function() {
+ var angleField = Blockly.FieldAngle.fromJson({ angle:362 });
+ assertValue(angleField, 2);
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.angleField = new Blockly.FieldAngle();
+ });
+ test('Null', function() {
+ this.angleField.setValue(null);
+ assertValueDefault(this.angleField);
+ });
+ test('Undefined', function() {
+ this.angleField.setValue(undefined);
+ assertValueDefault(this.angleField);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.angleField.setValue('bad');
+ assertValueDefault(this.angleField);
+ });
+ test('NaN', function() {
+ this.angleField.setValue(NaN);
+ assertValueDefault(this.angleField);
+ });
+ test('Integer', function() {
+ this.angleField.setValue(2);
+ assertValue(this.angleField, 2);
+ });
+ test('Float', function() {
+ this.angleField.setValue(2.5);
+ assertValue(this.angleField, 2.5);
+ });
+ test('Integer String', function() {
+ this.angleField.setValue('2');
+ assertValue(this.angleField, 2);
+ });
+ test('Float', function() {
+ this.angleField.setValue('2.5');
+ assertValue(this.angleField, 2.5);
+ });
+ test('>360°', function() {
+ this.angleField.setValue(362);
+ assertValue(this.angleField, 2);
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.angleField = new Blockly.FieldAngle(1);
+ });
+ test('Null', function() {
+ this.angleField.setValue(null);
+ assertValue(this.angleField, 1);
+ });
+ test.skip('Undefined', function() {
+ this.angleField.setValue(undefined);
+ assertValue(this.angleField, 1);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.angleField.setValue('bad');
+ assertValue(this.angleField, 1);
+ });
+ test.skip('NaN', function() {
+ this.angleField.setValue(NaN);
+ assertValue(this.angleField, 1);
+ });
+ test('Integer', function() {
+ this.angleField.setValue(2);
+ assertValue(this.angleField, 2);
+ });
+ test('Float', function() {
+ this.angleField.setValue(2.5);
+ assertValue(this.angleField, 2.5);
+ });
+ test('Integer String', function() {
+ this.angleField.setValue('2');
+ assertValue(this.angleField, 2);
+ });
+ test('Float', function() {
+ this.angleField.setValue('2.5');
+ assertValue(this.angleField, 2.5);
+ });
+ test('>360°', function() {
+ this.angleField.setValue(362);
+ assertValue(this.angleField, 2);
+ });
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.angleField = new Blockly.FieldAngle(1);
+ Blockly.FieldTextInput.htmlInput_ = Object.create(null);
+ Blockly.FieldTextInput.htmlInput_.oldValue_ = '1';
+ Blockly.FieldTextInput.htmlInput_.untypedDefaultValue_ = 1;
+ });
+ teardown(function() {
+ Blockly.FieldTextInput.htmlInput_ = null;
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.angleField.setValidator(function() {
+ return null;
+ });
+ });
+ test('When Editing', function() {
+ this.angleField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = '2';
+ this.angleField.onHtmlInputChange_(null);
+ assertValue(this.angleField, 1, '2');
+ this.angleField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.angleField.setValue(2);
+ assertValue(this.angleField, 1);
+ });
+ });
+ suite('Force Mult of 30 Validator', function() {
+ setup(function() {
+ this.angleField.setValidator(function(newValue) {
+ return Math.round(newValue / 30) * 30;
+ });
+ });
+ test('When Editing', function() {
+ this.angleField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = '25';
+ this.angleField.onHtmlInputChange_(null);
+ assertValue(this.angleField, 30, '25');
+ this.angleField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.angleField.setValue(25);
+ assertValue(this.angleField, 30);
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_checkbox_test.js b/tests/mocha/field_checkbox_test.js
new file mode 100644
index 000000000..0c909df82
--- /dev/null
+++ b/tests/mocha/field_checkbox_test.js
@@ -0,0 +1,173 @@
+/**
+ * @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.skip('Checkbox Fields', function() {
+ function assertValue(checkboxField, expectedValue, expectedText) {
+ var actualValue = checkboxField.getValue();
+ var actualText = checkboxField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedText);
+ }
+ function assertValueDefault(checkboxField) {
+ assertValue(checkboxField, 'FALSE', 'false');
+ }
+ suite('Constructor', function() {
+ test('Null', function() {
+ var checkboxField = new Blockly.FieldCheckbox(null);
+ assertValueDefault(checkboxField);
+ });
+ test('Undefined', function() {
+ var checkboxField = new Blockly.FieldCheckbox(undefined);
+ assertValueDefault(checkboxField);
+ });
+ test('Non-Parsable String', function() {
+ var checkboxField = new Blockly.FieldCheckbox('bad');
+ assertValueDefault(checkboxField);
+ });
+ test('True', function() {
+ var checkboxField = new Blockly.FieldCheckbox(true);
+ assertValue(checkboxField, 'TRUE', 'true');
+ });
+ test('False', function() {
+ var checkboxField = new Blockly.FieldCheckbox(false);
+ assertValue(checkboxField, 'FALSE', 'false');
+ });
+ test('String TRUE', function() {
+ var checkboxField = new Blockly.FieldCheckbox('TRUE');
+ assertValue(checkboxField, 'TRUE', 'true');
+ });
+ test('String FALSE', function() {
+ var checkboxField = new Blockly.FieldCheckbox('FALSE');
+ assertValue(checkboxField, 'FALSE', 'false');
+ });
+ });
+ suite('fromJson', function() {
+ test('Null', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: null});
+ assertValueDefault(checkboxField);
+ });
+ test('Undefined', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: undefined});
+ assertValueDefault(checkboxField);
+ });
+ test('Non-Parsable String', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: 'bad'});
+ assertValueDefault(checkboxField);
+ });
+ test('True', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: true});
+ assertValue(checkboxField, 'TRUE', 'true');
+ });
+ test('False', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: false});
+ assertValue(checkboxField, 'FALSE', 'false');
+ });
+ test('String TRUE', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: 'TRUE'});
+ assertValue(checkboxField, 'TRUE', 'true');
+ });
+ test('String FALSE', function() {
+ var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: 'FALSE'});
+ assertValue(checkboxField, 'FALSE', 'false');
+ });
+ });
+ suite('setValue', function() {
+ suite('True -> New Value', function() {
+ setup(function() {
+ this.checkboxField = new Blockly.FieldCheckbox('TRUE');
+ });
+ test('Null', function() {
+ this.checkboxField.setValue(null);
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ test('Undefined', function() {
+ this.checkboxField.setValue(undefined);
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ test('Non-Parsable String', function() {
+ this.checkboxField.setValue('bad');
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ test('False', function() {
+ this.checkboxField.setValue('FALSE');
+ assertValue(this.checkboxField, 'FALSE', 'false');
+ });
+ });
+ suite('False -> New Value', function() {
+ setup(function() {
+ this.checkboxField = new Blockly.FieldCheckbox('FALSE');
+ });
+ test('Null', function() {
+ this.checkboxField.setValue(null);
+ assertValue(this.checkboxField, 'FALSE', 'false');
+ });
+ test('Undefined', function() {
+ this.checkboxField.setValue(undefined);
+ assertValue(this.checkboxField, 'FALSE', 'false');
+ });
+ test('Non-Parsable String', function() {
+ this.checkboxField.setValue('bad');
+ assertValue(this.checkboxField, 'FALSE', 'false');
+ });
+ test('True', function() {
+ this.checkboxField.setValue('TRUE');
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ });
+ });
+ suite('Validators', function() {
+ setup(function() {
+ this.checkboxField = new Blockly.FieldCheckbox(true);
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.checkboxField.setValidator(function() {
+ return null;
+ });
+ });
+ test('New Value', function() {
+ this.checkboxField.setValue('FALSE');
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ });
+ suite('Always True Validator', function() {
+ setup(function() {
+ this.checkboxField.setValidator(function() {
+ return 'TRUE';
+ });
+ });
+ test('New Value', function() {
+ this.checkboxField.setValue('FALSE');
+ assertValue(this.checkboxField, 'TRUE', 'true');
+ });
+ });
+ suite('Always False Validator', function() {
+ setup(function() {
+ this.checkboxField.setValidator(function() {
+ return 'FALSE';
+ });
+ });
+ test('New Value', function() {
+ this.checkboxField.setValue('TRUE');
+ assertValue(this.checkboxField, 'FALSE', 'false');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_colour_test.js b/tests/mocha/field_colour_test.js
new file mode 100644
index 000000000..2979bf0ca
--- /dev/null
+++ b/tests/mocha/field_colour_test.js
@@ -0,0 +1,210 @@
+/**
+ * @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 ('Colour Fields', function() {
+ function assertValue(colourField, expectedValue, expectedText) {
+ var actualValue = colourField.getValue();
+ var actualText = colourField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedText);
+ }
+ function assertValueDefault(colourField) {
+ var expectedValue = Blockly.FieldColour.COLOURS[0];
+ var expectedText = expectedValue;
+ var m = expectedValue.match(/^#(.)\1(.)\2(.)\3$/);
+ if (m) {
+ expectedText = '#' + m[1] + m[2] + m[3];
+ }
+ assertValue(colourField, expectedValue, expectedText);
+ }
+
+ setup(function() {
+ this.previousColours = Blockly.FieldColour.COLOURS;
+ Blockly.FieldColour.Colours = [
+ '#ffffff', '#ff0000', '#00ff00', '#0000ff', '#ffffff'
+ ];
+ });
+ teardown(function() {
+ Blockly.FieldColour.Colours = this.previousColours;
+ });
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var colourField = new Blockly.FieldColour();
+ assertValueDefault(colourField);
+ });
+ test('Null', function() {
+ var colourField = new Blockly.FieldColour(null);
+ assertValueDefault(colourField);
+ });
+ test('Undefined', function() {
+ var colourField = new Blockly.FieldColour(undefined);
+ assertValueDefault(colourField);
+ });
+ test.skip('Non-Parsable String', function() {
+ var colourField = new Blockly.FieldColour('bad');
+ assertValueDefault(colourField);
+ });
+ test.skip('#AAAAAA', function() {
+ var colourField = new Blockly.FieldColour('#AAAAAA');
+ assertValue(colourField, '#aaaaaa', '#aaa');
+ });
+ test('#aaaaaa', function() {
+ var colourField = new Blockly.FieldColour('#aaaaaa');
+ assertValue(colourField, '#aaaaaa', '#aaa');
+ });
+ test.skip('#AAAA00', function() {
+ var colourField = new Blockly.FieldColour('#AAAA00');
+ assertValue(colourField, '#aaaa00', '#aa0');
+ });
+ test('#aaaa00', function() {
+ var colourField = new Blockly.FieldColour('#aaaa00');
+ assertValue(colourField, '#aaaa00', '#aa0');
+ });
+ test.skip('#BCBCBC', function() {
+ var colourField = new Blockly.FieldColour('#BCBCBC');
+ assertValue(colourField, '#bcbcbc', '#bcbcbc');
+ });
+ test('#bcbcbc', function() {
+ var colourField = new Blockly.FieldColour('#bcbcbc');
+ assertValue(colourField, '#bcbcbc', '#bcbcbc');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var colourField = new Blockly.FieldColour.fromJson({});
+ assertValueDefault(colourField);
+ });
+ test('Null', function() {
+ var colourField = new Blockly.FieldColour.fromJson({ colour:null });
+ assertValueDefault(colourField);
+ });
+ test('Undefined', function() {
+ var colourField = new Blockly.FieldColour.fromJson({ colour:undefined });
+ assertValueDefault(colourField);
+ });
+ test.skip('Non-Parsable String', function() {
+ var colourField = new Blockly.FieldColour.fromJson({ colour:'bad' });
+ assertValueDefault(colourField);
+ });
+ test.skip('#AAAAAA', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#AAAAAA' });
+ assertValue(colourField, '#aaaaaa', '#aaa');
+ });
+ test('#aaaaaa', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#aaaaaa' });
+ assertValue(colourField, '#aaaaaa', '#aaa');
+ });
+ test.skip('#AAAA00', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#AAAA00' });
+ assertValue(colourField, '#aaaa00', '#aa0');
+ });
+ test('#aaaa00', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#aaaa00' });
+ assertValue(colourField, '#aaaa00', '#aa0');
+ });
+ test.skip('#BCBCBC', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#BCBCBC' });
+ assertValue(colourField, '#bcbcbc', '#bcbcbc');
+ });
+ test('#bcbcbc', function() {
+ var colourField = Blockly.FieldColour.fromJson({ colour: '#bcbcbc' });
+ assertValue(colourField, '#bcbcbc', '#bcbcbc');
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.colourField = new Blockly.FieldColour();
+ });
+ test.skip('Null', function() {
+ this.colourField.setValue(null);
+ assertValueDefault(this.colourField);
+ });
+ test.skip('Undefined', function() {
+ this.colourField.setValue(undefined);
+ assertValueDefault(this.colourField);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.colourField.setValue('bad');
+ assertValueDefault(this.colourField);
+ });
+ test('#000000', function() {
+ this.colourField.setValue('#000000');
+ assertValue(this.colourField, '#000000', '#000');
+ });
+ test('#bcbcbc', function() {
+ this.colourField.setValue('#bcbcbc');
+ assertValue(this.colourField, '#bcbcbc', '#bcbcbc');
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.colourField = new Blockly.FieldColour('#aaaaaa');
+ });
+ test.skip('Null', function() {
+ this.colourField.setValue(null);
+ assertValue(this.colourField, '#aaaaaa', '#aaa');
+ });
+ test.skip('Undefined', function() {
+ this.colourField.setValue(undefined);
+ assertValue(this.colourField, '#aaaaaa', '#aaa');
+ });
+ test.skip('Non-Parsable String', function() {
+ this.colourField.setValue('bad');
+ assertValue(this.colourField, '#aaaaaa', '#aaa');
+ });
+ test('#000000', function() {
+ this.colourField.setValue('#000000');
+ assertValue(this.colourField, '#000000', '#000');
+ });
+ test('#bcbcbc', function() {
+ this.colourField.setValue('#bcbcbc');
+ assertValue(this.colourField, '#bcbcbc', '#bcbcbc');
+ });
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.colourField = new Blockly.FieldColour('#aaaaaa');
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.colourField.setValidator(function() {
+ return null;
+ });
+ });
+ test('New Value', function() {
+ this.colourField.setValue('#000000');
+ assertValue(this.colourField, '#aaaaaa', '#aaa');
+ });
+ });
+ suite('Force Full Red Validator', function() {
+ setup(function() {
+ this.colourField.setValidator(function(newValue) {
+ return '#ff' + newValue.substr(3, 4);
+ });
+ });
+ test('New Value', function() {
+ this.colourField.setValue('#000000');
+ assertValue(this.colourField, '#ff0000', '#f00');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_date_test.js b/tests/mocha/field_date_test.js
new file mode 100644
index 000000000..cc95b36ba
--- /dev/null
+++ b/tests/mocha/field_date_test.js
@@ -0,0 +1,179 @@
+/**
+ * @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 ('Date Fields', function() {
+ function assertValue(dateField, expectedValue) {
+ var actualValue = dateField.getValue();
+ var actualText = dateField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedValue);
+ }
+ function assertValueDefault(dateField) {
+ var today = new goog.date.Date().toIsoString(true);
+ assertValue(dateField, today);
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var dateField = new Blockly.FieldDate();
+ assertValueDefault(dateField);
+ });
+ test('Null', function() {
+ var dateField = new Blockly.FieldDate(null);
+ assertValueDefault(dateField);
+ });
+ test('Undefined', function() {
+ var dateField = new Blockly.FieldDate(undefined);
+ assertValueDefault(dateField);
+ });
+ test.skip('Non-Parsable String', function() {
+ var dateField = new Blockly.FieldDate('bad');
+ assertValueDefault(dateField);
+ });
+ test('2020-02-20', function() {
+ var dateField = new Blockly.FieldDate('2020-02-20');
+ assertValue(dateField, '2020-02-20');
+ });
+ test.skip('Invalid Date - Month(2020-13-20)', function() {
+ var dateField = new Blockly.FieldDate('2020-13-20');
+ assertValueDefault(dateField);
+ });
+ test.skip('Invalid Date - Day(2020-02-32)', function() {
+ var dateField = new Blockly.FieldDate('2020-02-32');
+ assertValueDefault(dateField);
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var dateField = Blockly.FieldDate.fromJson({});
+ assertValueDefault(dateField);
+ });
+ test('Null', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: null });
+ assertValueDefault(dateField);
+ });
+ test('Undefined', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: undefined });
+ assertValueDefault(dateField);
+ });
+ test.skip('Non-Parsable String', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: 'bad' });
+ assertValueDefault(dateField);
+ });
+ test('2020-02-20', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: '2020-02-20' });
+ assertValue(dateField, '2020-02-20');
+ });
+ test.skip('Invalid Date - Month(2020-13-20)', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: '2020-13-20' });
+ assertValueDefault(dateField);
+ });
+ test.skip('Invalid Date - Day(2020-02-32)', function() {
+ var dateField = Blockly.FieldDate.fromJson({ date: '2020-02-32' });
+ assertValueDefault(dateField);
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.dateField = new Blockly.FieldDate();
+ });
+ test.skip('Null', function() {
+ this.dateField.setValue(null);
+ assertValueDefault(this.dateField);
+ });
+ test.skip('Undefined', function() {
+ this.dateField.setValue(undefined);
+ assertValueDefault(this.dateField);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.dateField.setValue('bad');
+ assertValueDefault(this.dateField);
+ });
+ test.skip('Invalid Date - Month(2020-13-20)', function() {
+ this.dateField.setValue('2020-13-20');
+ assertValueDefault(this.dateField);
+ });
+ test.skip('Invalid Date - Day(2020-02-32)', function() {
+ this.dateField.setValue('2020-02-32');
+ assertValueDefault(this.dateField);
+ });
+ test('3030-03-30', function() {
+ this.dateField.setValue('3030-03-30');
+ assertValue(this.dateField, '3030-03-30');
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.dateField = new Blockly.FieldDate('2020-02-20');
+ });
+ test.skip('Null', function() {
+ this.dateField.setValue(null);
+ assertValue(this.dateField, '2020-02-20');
+ });
+ test.skip('Undefined', function() {
+ this.dateField.setValue(undefined);
+ assertValue(this.dateField, '2020-02-20');
+ });
+ test.skip('Non-Parsable String', function() {
+ this.dateField.setValue('bad');
+ assertValue(this.dateField, '2020-02-20');
+ });
+ test.skip('Invalid Date - Month(2020-13-20)', function() {
+ this.dateField.setValue('2020-13-20');
+ assertValue(this.dateField, '2020-02-20');
+ });
+ test.skip('Invalid Date - Day(2020-02-32)', function() {
+ this.dateField.setValue('2020-02-32');
+ assertValue(this.dateField, '2020-02-20');
+ });
+ test('3030-03-30', function() {
+ this.dateField.setValue('3030-03-30');
+ assertValue(this.dateField, '3030-03-30');
+ });
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.dateField = new Blockly.FieldDate('2020-02-20');
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.dateField.setValidator(function() {
+ return null;
+ });
+ });
+ test('New Value', function() {
+ this.dateField.setValue('3030-03-30');
+ assertValue(this.dateField, '2020-02-20');
+ });
+ });
+ suite('Force Day 20s Validator', function() {
+ setup(function() {
+ this.dateField.setValidator(function(newValue) {
+ return newValue.substr(0, 8) + '2' + newValue.substr(9, 1);
+ });
+ });
+ test('New Value', function() {
+ this.dateField.setValue('3030-03-30');
+ assertValue(this.dateField, '3030-03-20');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_dropdown_test.js b/tests/mocha/field_dropdown_test.js
new file mode 100644
index 000000000..23c5f8016
--- /dev/null
+++ b/tests/mocha/field_dropdown_test.js
@@ -0,0 +1,193 @@
+/**
+ * @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 ('Dropdown Fields', function() {
+ function assertValue(dropdownField, expectedValue, expectedText) {
+ var actualValue = dropdownField.getValue();
+ var actualText = dropdownField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedText);
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown();
+ });
+ });
+ test('Null', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown(null);
+ });
+ });
+ test('Undefined', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown(undefined);
+ });
+ });
+ test('Array Items not Arrays', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown([1, 2, 3]);
+ });
+ });
+ test('Array Items with Invalid IDs', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown([['1', 1], ['2', 2], ['3', 3]]);
+ });
+ });
+ test('Array Items with Invalid Content', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ new Blockly.FieldDropdown([[1, '1'], [2, '2'], [3, '3']]);
+ });
+ });
+ test('Text Dropdown', function() {
+ var dropdownField = new Blockly.FieldDropdown(
+ [['a', 'A'], ['b', 'B'], ['c', 'C']]);
+ assertValue(dropdownField, 'A', 'a');
+ });
+ test('Image Dropdown', function() {
+ var dropdownField = new Blockly.FieldDropdown([
+ [{ src:'scrA', alt:'a' }, 'A'],
+ [{ src:'scrB', alt:'b' }, 'B'],
+ [{ src:'scrC', alt:'c' }, 'C']]);
+ assertValue(dropdownField, 'A', 'a');
+ });
+ test('Dynamic Dropdown Text', function() {
+ var dynamicDropdownFunc = function() {
+ return [['a', 'A'], ['b', 'B'], ['c', 'C']];
+ };
+ var dropdownField = new Blockly.FieldDropdown(dynamicDropdownFunc);
+ assertValue(dropdownField, 'A', 'a');
+ });
+ test('Dynamic Dropdown Image', function() {
+ var dynamicDropdownFunc = function() {
+ return [
+ [{ src:'scrA', alt:'a' }, 'A'],
+ [{ src:'scrB', alt:'b' }, 'B'],
+ [{ src:'scrC', alt:'c' }, 'C']
+ ];
+ };
+ var dropdownField = new Blockly.FieldDropdown(dynamicDropdownFunc);
+ assertValue(dropdownField, 'A', 'a');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson({});
+ });
+ });
+ test('Null', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson({ options: null });
+ });
+ });
+ test('Undefined', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson({ options: undefined });
+ });
+ });
+ test('Array Items not Arrays', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson({ options: [1, 2, 3] });
+ });
+ });
+ test('Array Items with Invalid IDs', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson(
+ { options:[['1', 1], ['2', 2], ['3', 3]] });
+ });
+ });
+ test('Array Items with Invalid Content', function() {
+ console.log('You should see three console warnings after this message.');
+ chai.assert.throws(function() {
+ Blockly.FieldDropdown.fromJson(
+ { options:[[1, '1'], [2, '2'], [3, '3']] });
+ });
+ });
+ test('Text Dropdown', function() {
+ var dropdownField = Blockly.FieldDropdown.fromJson(
+ { options:[['a', 'A'], ['b', 'B'], ['c', 'C']] });
+ assertValue(dropdownField, 'A', 'a');
+ });
+ test('Image Dropdown', function() {
+ var dropdownField = Blockly.FieldDropdown.fromJson({ options:[
+ [{ src:'scrA', alt:'a' }, 'A'],
+ [{ src:'scrB', alt:'b' }, 'B'],
+ [{ src:'scrC', alt:'c' }, 'C']] });
+ assertValue(dropdownField, 'A', 'a');
+ });
+ });
+ suite('setValue', function() {
+ setup(function() {
+ this.dropdownField = new Blockly.FieldDropdown(
+ [['a', 'A'], ['b', 'B'], ['c', 'C']]);
+ });
+ test('Null', function() {
+ this.dropdownField.setValue(null);
+ assertValue(this.dropdownField, 'A', 'a');
+ });
+ test.skip('Undefined', function() {
+ this.dropdownField.setValue(undefined);
+ assertValue(this.dropdownField, 'A', 'a');
+ });
+ test.skip('Invalid ID', function() {
+ this.dropdownField.setValue('bad');
+ assertValue(this.dropdownField, 'A', 'a');
+ });
+ test('Valid ID', function() {
+ this.dropdownField.setValue('B');
+ assertValue(this.dropdownField, 'B', 'b');
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.dropdownField = new Blockly.FieldDropdown([
+ ["1a","1A"], ["1b","1B"], ["1c","1C"],
+ ["2a","2A"], ["2b","2B"], ["2c","2C"]]);
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.dropdownField.setValidator(function() {
+ return null;
+ });
+ });
+ test('New Value', function() {
+ this.dropdownField.setValue('1B');
+ assertValue(this.dropdownField, '1A', '1a');
+ });
+ });
+ suite('Force 1s Validator', function() {
+ setup(function() {
+ this.dropdownField.setValidator(function(newValue) {
+ return '1' + newValue.charAt(1);
+ });
+ });
+ test('New Value', function() {
+ this.dropdownField.setValue('2B');
+ assertValue(this.dropdownField, '1B', '1b');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_image_test.js b/tests/mocha/field_image_test.js
new file mode 100644
index 000000000..eb968882c
--- /dev/null
+++ b/tests/mocha/field_image_test.js
@@ -0,0 +1,169 @@
+/**
+ * @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 ('Image Fields', function() {
+ function assertValue(imageField, expectedValue, expectedText) {
+ var actualValue = imageField.getValue();
+ var actualText = imageField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedText);
+ }
+ function assertValueDefault(imageField) {
+ assertValue(imageField, '', '');
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldImage();
+ });
+ });
+ test('Null Src', function() {
+ var imageField = new Blockly.FieldImage(null, 1, 1);
+ assertValueDefault(imageField);
+ });
+ test('Undefined Src', function() {
+ var imageField = new Blockly.FieldImage(undefined, 1, 1);
+ assertValueDefault(imageField);
+ });
+ test('Null Size', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldImage('src', null, null);
+ });
+ });
+ test('Undefined Size', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldImage('src', undefined, undefined);
+ });
+ });
+ test('Zero Size', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldImage('src', 0, 0);
+ });
+ });
+ test('Non-Parsable String for Size', function() {
+ chai.assert.throws(function() {
+ new Blockly.FieldImage('src', 'bad', 'bad');
+ });
+ });
+ // Note: passing invalid an src path doesn't need to throw errors
+ // because the developer can see they did it incorrectly when they view
+ // the block.
+ test('With Alt', function() {
+ var imageField = new Blockly.FieldImage('src', 1, 1, 'alt');
+ assertValue(imageField, 'src', 'alt');
+ });
+ test('Without Alt', function() {
+ var imageField = new Blockly.FieldImage('src', 1, 1);
+ assertValue(imageField, 'src', '');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldImage.fromJson({});
+ });
+ });
+ test('Null Src', function() {
+ var imageField = Blockly.FieldImage.fromJson({
+ src: null,
+ width: 1,
+ height: 1
+ });
+ assertValueDefault(imageField);
+ });
+ test('Undefined Src', function() {
+ var imageField = Blockly.FieldImage.fromJson({
+ src: undefined,
+ width: 1,
+ height: 1
+ });
+ assertValueDefault(imageField);
+ });
+ test('Null Size', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldImage.fromJson({
+ src: 'src',
+ width: null,
+ height: null
+ });
+ });
+ });
+ test('Undefined Size', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldImage.fromJson({
+ src: 'src',
+ width: undefined,
+ height: undefined
+ });
+ });
+ });
+ test('Non-Parsable String for Size', function() {
+ chai.assert.throws(function() {
+ Blockly.FieldImage.fromJson({
+ src: 'src',
+ width: 'bad',
+ height: 'bad'
+ });
+ });
+ });
+ test('With Alt', function() {
+ var imageField = Blockly.FieldImage.fromJson({
+ src: 'src',
+ width: 1,
+ height: 1,
+ alt: 'alt'
+ });
+ assertValue(imageField, 'src', 'alt');
+ });
+ test('Without Alt', function() {
+ var imageField = Blockly.FieldImage.fromJson({
+ src: 'src',
+ width: 1,
+ height: 1
+ });
+ assertValue(imageField, 'src', '');
+ });
+ });
+ suite('setValue', function() {
+ setup(function() {
+ this.imageField = new Blockly.FieldImage('src', 1, 1, 'alt');
+ });
+ test('Null', function() {
+ this.imageField.setValue(null);
+ assertValue(this.imageField, 'src', 'alt');
+ });
+ test.skip('Undefined', function() {
+ this.imageField.setValue(undefined);
+ assertValue(this.imageField, 'src', 'alt');
+ });
+ test('New Src, New Alt', function() {
+ this.imageField.setValue('newSrc');
+ assertValue(this.imageField, 'newSrc', 'alt');
+ this.imageField.setText('newAlt');
+ assertValue(this.imageField, 'newSrc', 'newAlt');
+ });
+ test('New Alt, New Src', function() {
+ this.imageField.setText('newAlt');
+ assertValue(this.imageField, 'src', 'newAlt');
+ this.imageField.setValue('newSrc');
+ assertValue(this.imageField, 'newSrc', 'newAlt');
+ });
+ });
+});
diff --git a/tests/mocha/field_label_serializable_test.js b/tests/mocha/field_label_serializable_test.js
new file mode 100644
index 000000000..e59fd329c
--- /dev/null
+++ b/tests/mocha/field_label_serializable_test.js
@@ -0,0 +1,170 @@
+/**
+ * @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 ('Label Serializable Fields', function() {
+ function assertValue(labelField, expectedValue) {
+ var actualValue = labelField.getValue();
+ var actualText = labelField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedValue);
+ }
+ function assertValueDefault(labelField) {
+ assertValue(labelField, '');
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var labelField = new Blockly.FieldLabelSerializable();
+ assertValueDefault(labelField);
+ });
+ test('Null', function() {
+ var labelField = new Blockly.FieldLabelSerializable(null);
+ assertValueDefault(labelField);
+ });
+ test('Undefined', function() {
+ var labelField = new Blockly.FieldLabelSerializable(undefined);
+ assertValueDefault(labelField);
+ });
+ test('String', function() {
+ var labelField = new Blockly.FieldLabelSerializable('value');
+ assertValue(labelField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var labelField = new Blockly.FieldLabelSerializable(1);
+ assertValue(labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var labelField = new Blockly.FieldLabelSerializable(0);
+ assertValue(labelField, '0');
+ });
+ test('Boolean True', function() {
+ var labelField = new Blockly.FieldLabelSerializable(true);
+ assertValue(labelField, 'true');
+ });
+ test('Boolean False', function() {
+ var labelField = new Blockly.FieldLabelSerializable(false);
+ assertValue(labelField, 'false');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var labelField = new Blockly.FieldLabelSerializable.fromJson({});
+ assertValueDefault(labelField);
+ });
+ test('Null', function() {
+ var labelField = new Blockly.FieldLabelSerializable
+ .fromJson({ text:null });
+ assertValueDefault(labelField);
+ });
+ test('Undefined', function() {
+ var labelField = new Blockly.FieldLabelSerializable
+ .fromJson({ text:undefined });
+ assertValueDefault(labelField);
+ });
+ test('String', function() {
+ var labelField = Blockly.FieldLabelSerializable
+ .fromJson({ text:'value' });
+ assertValue(labelField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var labelField = Blockly.FieldLabelSerializable.fromJson({ text:1 });
+ assertValue(labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var labelField = Blockly.FieldLabelSerializable.fromJson({ text:0 });
+ assertValue(labelField, '0');
+ });
+ test('Boolean True', function() {
+ var labelField = Blockly.FieldLabelSerializable.fromJson({ text:true });
+ assertValue(labelField, 'true');
+ });
+ test('Boolean False', function() {
+ var labelField = Blockly.FieldLabelSerializable.fromJson({ text:false });
+ assertValue(labelField, 'false');
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.labelField = new Blockly.FieldLabelSerializable();
+ });
+ test('Null', function() {
+ this.labelField.setValue(null);
+ assertValueDefault(this.labelField);
+ });
+ test.skip('Undefined', function() {
+ this.labelField.setValue(undefined);
+ assertValueDefault(this.labelField);
+ });
+ test('New String', function() {
+ this.labelField.setValue('newValue');
+ assertValue(this.labelField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.labelField.setValue(1);
+ assertValue(this.labelField, '1');
+ });
+ test.skip('Number (Falsy)', function() {
+ this.labelField.setValue(0);
+ assertValue(this.labelField, '0');
+ });
+ test('Boolean True', function() {
+ this.labelField.setValue(true);
+ assertValue(this.labelField, 'true');
+ });
+ test.skip('Boolean False', function() {
+ this.labelField.setValue(false);
+ assertValue(this.labelField, 'false');
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.labelField = new Blockly.FieldLabelSerializable('value');
+ });
+ test('Null', function() {
+ this.labelField.setValue(null);
+ assertValue(this.labelField, 'value');
+ });
+ test.skip('Undefined', function() {
+ this.labelField.setValue(undefined);
+ assertValue(this.labelField, 'value');
+ });
+ test('New String', function() {
+ this.labelField.setValue('newValue');
+ assertValue(this.labelField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.labelField.setValue(1);
+ assertValue(this.labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ this.labelField.setValue(0);
+ assertValue(this.labelField, '0');
+ });
+ test('Boolean True', function() {
+ this.labelField.setValue(true);
+ assertValue(this.labelField, 'true');
+ });
+ test('Boolean False', function() {
+ this.labelField.setValue(false);
+ assertValue(this.labelField, 'false');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_label_test.js b/tests/mocha/field_label_test.js
new file mode 100644
index 000000000..1974da55e
--- /dev/null
+++ b/tests/mocha/field_label_test.js
@@ -0,0 +1,167 @@
+/**
+ * @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 ('Label Fields', function() {
+ function assertValue(labelField, expectedValue) {
+ var actualValue = labelField.getValue();
+ var actualText = labelField.getText();
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, expectedValue);
+ }
+ function assertValueDefault(labelField) {
+ assertValue(labelField, '');
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var labelField = new Blockly.FieldLabel();
+ assertValueDefault(labelField);
+ });
+ test('Null', function() {
+ var labelField = new Blockly.FieldLabel(null);
+ assertValueDefault(labelField);
+ });
+ test('Undefined', function() {
+ var labelField = new Blockly.FieldLabel(undefined);
+ assertValueDefault(labelField);
+ });
+ test('String', function() {
+ var labelField = new Blockly.FieldLabel('value');
+ assertValue(labelField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var labelField = new Blockly.FieldLabel(1);
+ assertValue(labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var labelField = new Blockly.FieldLabel(0);
+ assertValue(labelField, '0');
+ });
+ test('Boolean True', function() {
+ var labelField = new Blockly.FieldLabel(true);
+ assertValue(labelField, 'true');
+ });
+ test('Boolean False', function() {
+ var labelField = new Blockly.FieldLabel(false);
+ assertValue(labelField, 'false');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var labelField = new Blockly.FieldLabel.fromJson({});
+ assertValueDefault(labelField);
+ });
+ test('Null', function() {
+ var labelField = new Blockly.FieldLabel.fromJson({ text:null });
+ assertValueDefault(labelField);
+ });
+ test('Undefined', function() {
+ var labelField = new Blockly.FieldLabel.fromJson({ text:undefined });
+ assertValueDefault(labelField);
+ });
+ test('String', function() {
+ var labelField = Blockly.FieldLabel.fromJson({ text:'value' });
+ assertValue(labelField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var labelField = Blockly.FieldLabel.fromJson({ text:1 });
+ assertValue(labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var labelField = Blockly.FieldLabel.fromJson({ text:0 });
+ assertValue(labelField, '0');
+ });
+ test('Boolean True', function() {
+ var labelField = Blockly.FieldLabel.fromJson({ text:true });
+ assertValue(labelField, 'true');
+ });
+ test('Boolean False', function() {
+ var labelField = Blockly.FieldLabel.fromJson({ text:false });
+ assertValue(labelField, 'false');
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.labelField = new Blockly.FieldLabel();
+ });
+ test('Null', function() {
+ this.labelField.setValue(null);
+ assertValueDefault(this.labelField);
+ });
+ test.skip('Undefined', function() {
+ this.labelField.setValue(undefined);
+ assertValueDefault(this.labelField);
+ });
+ test('New String', function() {
+ this.labelField.setValue('newValue');
+ assertValue(this.labelField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.labelField.setValue(1);
+ assertValue(this.labelField, '1');
+ });
+ test.skip('Number (Falsy)', function() {
+ this.labelField.setValue(0);
+ assertValue(this.labelField, '0');
+ });
+ test('Boolean True', function() {
+ this.labelField.setValue(true);
+ assertValue(this.labelField, 'true');
+ });
+ test.skip('Boolean False', function() {
+ this.labelField.setValue(false);
+ assertValue(this.labelField, 'false');
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.labelField = new Blockly.FieldLabel('value');
+ });
+ test('Null', function() {
+ this.labelField.setValue(null);
+ assertValue(this.labelField, 'value');
+ });
+ test.skip('Undefined', function() {
+ this.labelField.setValue(undefined);
+ assertValue(this.labelField, 'value');
+ });
+ test('New String', function() {
+ this.labelField.setValue('newValue');
+ assertValue(this.labelField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.labelField.setValue(1);
+ assertValue(this.labelField, '1');
+ });
+ test('Number (Falsy)', function() {
+ this.labelField.setValue(0);
+ assertValue(this.labelField, '0');
+ });
+ test('Boolean True', function() {
+ this.labelField.setValue(true);
+ assertValue(this.labelField, 'true');
+ });
+ test('Boolean False', function() {
+ this.labelField.setValue(false);
+ assertValue(this.labelField, 'false');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_number_test.js b/tests/mocha/field_number_test.js
new file mode 100644
index 000000000..f0c1d1db0
--- /dev/null
+++ b/tests/mocha/field_number_test.js
@@ -0,0 +1,342 @@
+/**
+ * @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 ('Number Fields', function() {
+ function assertValue(numberField, expectedValue, opt_expectedText) {
+ var actualValue = numberField.getValue();
+ var actualText = numberField.getText();
+ opt_expectedText = opt_expectedText || String(expectedValue);
+ assertEquals(String(actualValue), String(expectedValue));
+ assertEquals(parseFloat(actualValue), expectedValue);
+ assertEquals(actualText, opt_expectedText);
+ }
+ function assertValueDefault(numberFieldField) {
+ assertValue(numberFieldField, 0);
+ }
+ function assertNumberField(numberField, expectedMin, expectedMax,
+ expectedPrecision, expectedValue) {
+ assertValue(numberField, expectedValue);
+ assertEquals(numberField.min_, expectedMin);
+ assertEquals(numberField.max_, expectedMax);
+ assertEquals(numberField.precision_, expectedPrecision);
+ }
+ function assertNumberFieldDefault(numberField) {
+ assertNumberField(numberField, -Infinity, Infinity, 0, 0);
+ }
+ function createNumberFieldSameValuesConstructor(value) {
+ return new Blockly.FieldNumber(value, value, value, value);
+ }
+ function createNumberFieldSameValuesJson(value) {
+ return Blockly.FieldNumber.fromJson(
+ { 'value': value, min: value, max: value, precision: value });
+ }
+ function assertNumberFieldSameValues(numberField, value) {
+ assertNumberField(numberField, value, value, value, value);
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var numberField = new Blockly.FieldNumber();
+ assertNumberFieldDefault(numberField);
+ });
+ test('Null', function() {
+ var numberField = createNumberFieldSameValuesConstructor(null);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Undefined', function() {
+ var numberField = createNumberFieldSameValuesConstructor(undefined);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Non-Parsable String', function() {
+ var numberField = createNumberFieldSameValuesConstructor('bad');
+ assertNumberFieldDefault(numberField);
+ });
+ test('NaN', function() {
+ var numberField = createNumberFieldSameValuesConstructor(NaN);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Integer', function() {
+ var numberField = createNumberFieldSameValuesConstructor(1);
+ assertNumberFieldSameValues(numberField, 1);
+ });
+ test('Float', function() {
+ var numberField = createNumberFieldSameValuesConstructor(1.5);
+ assertNumberFieldSameValues(numberField, 1.5);
+ });
+ test('Integer String', function() {
+ var numberField = createNumberFieldSameValuesConstructor('1');
+ assertNumberFieldSameValues(numberField, 1);
+ });
+ test('Float String', function() {
+ var numberField = createNumberFieldSameValuesConstructor('1.5');
+ assertNumberFieldSameValues(numberField, 1.5);
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var numberField = Blockly.FieldNumber.fromJson({});
+ assertNumberFieldDefault(numberField);
+ });
+ test('Null', function() {
+ var numberField = createNumberFieldSameValuesJson(null);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Undefined', function() {
+ var numberField = createNumberFieldSameValuesJson(undefined);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Non-Parsable String', function() {
+ var numberField = createNumberFieldSameValuesJson('bad');
+ assertNumberFieldDefault(numberField);
+ });
+ test('NaN', function() {
+ var numberField = createNumberFieldSameValuesJson(NaN);
+ assertNumberFieldDefault(numberField);
+ });
+ test('Integer', function() {
+ var numberField = createNumberFieldSameValuesJson(1);
+ assertNumberFieldSameValues(numberField, 1);
+ });
+ test('Float', function() {
+ var numberField = createNumberFieldSameValuesJson(1.5);
+ assertNumberFieldSameValues(numberField, 1.5);
+ });
+ test('Integer String', function() {
+ var numberField = createNumberFieldSameValuesJson('1');
+ assertNumberFieldSameValues(numberField, 1);
+ });
+ test('Float String', function() {
+ var numberField = createNumberFieldSameValuesJson('1.5');
+ assertNumberFieldSameValues(numberField, 1.5);
+ });
+ });
+ suite('setValue', function() {
+ suite('Value Types', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.numberField = new Blockly.FieldNumber();
+ });
+ test('Null', function() {
+ this.numberField.setValue(null);
+ assertValueDefault(this.numberField);
+ });
+ test.skip('Undefined', function() {
+ this.numberField.setValue(undefined);
+ assertValueDefault(this.numberField);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.numberField.setValue('bad');
+ assertValueDefault(this.numberField);
+ });
+ test.skip('NaN', function() {
+ this.numberField.setValue(NaN);
+ assertValueDefault(this.numberField);
+ });
+ test('Integer', function() {
+ this.numberField.setValue(2);
+ assertValue(this.numberField, 2);
+ });
+ test('Float', function() {
+ this.numberField.setValue(2.5);
+ assertValue(this.numberField, 2.5);
+ });
+ test('Integer String', function() {
+ this.numberField.setValue('2');
+ assertValue(this.numberField, 2);
+ });
+ test('Float String', function() {
+ this.numberField.setValue('2.5');
+ assertValue(this.numberField, 2.5);
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.numberField = new Blockly.FieldNumber(1);
+ });
+ test('Null', function() {
+ this.numberField.setValue(null);
+ assertValue(this.numberField, 1);
+ });
+ test.skip('Undefined', function() {
+ this.numberField.setValue(undefined);
+ assertValue(this.numberField, 1);
+ });
+ test.skip('Non-Parsable String', function() {
+ this.numberField.setValue('bad');
+ assertValue(this.numberField, 1);
+ });
+ test.skip('NaN', function() {
+ this.numberField.setValue(NaN);
+ assertValue(this.numberField, 1);
+ });
+ test('Integer', function() {
+ this.numberField.setValue(2);
+ assertValue(this.numberField, 2);
+ });
+ test('Float', function() {
+ this.numberField.setValue(2.5);
+ assertValue(this.numberField, 2.5);
+ });
+ test('Integer String', function() {
+ this.numberField.setValue('2');
+ assertValue(this.numberField, 2);
+ });
+ test('Float String', function() {
+ this.numberField.setValue('2.5');
+ assertValue(this.numberField, 2.5);
+ });
+ });
+ });
+ suite('Constraints', function() {
+ suite('Precision', function() {
+ test('Float', function() {
+ var numberField = new Blockly.FieldNumber();
+ numberField.setValue(123.456);
+ assertValue(numberField, 123.456);
+ });
+ test('0.01', function() {
+ var numberField = new Blockly.FieldNumber
+ .fromJson({ precision: .01 });
+ numberField.setValue(123.456);
+ assertValue(numberField, 123.46);
+ });
+ test('0.5', function() {
+ var numberField = new Blockly.FieldNumber
+ .fromJson({ precision: .5 });
+ numberField.setValue(123.456);
+ assertValue(numberField, 123.5);
+ });
+ test('1', function() {
+ var numberField = new Blockly.FieldNumber
+ .fromJson({ precision: 1 });
+ numberField.setValue(123.456);
+ assertValue(numberField, 123);
+ });
+ test.skip('1.5', function() {
+ var numberField = new Blockly.FieldNumber
+ .fromJson({ precision: 1.5 });
+ numberField.setValue(123.456);
+ assertValue(numberField, 123);
+ });
+ });
+ suite('Min', function() {
+ test('-10', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ min: -10 });
+ numberField.setValue(-20);
+ assertValue(numberField, -10);
+ numberField.setValue(0);
+ assertValue(numberField, 0);
+ numberField.setValue(20);
+ assertValue(numberField, 20);
+ });
+ test('0', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ min: 0 });
+ numberField.setValue(-20);
+ assertValue(numberField, 0);
+ numberField.setValue(0);
+ assertValue(numberField, 0);
+ numberField.setValue(20);
+ assertValue(numberField, 20);
+ });
+ test('+10', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ min: 10 });
+ numberField.setValue(-20);
+ assertValue(numberField, 10);
+ numberField.setValue(0);
+ assertValue(numberField, 10);
+ numberField.setValue(20);
+ assertValue(numberField, 20);
+ });
+ });
+ suite('Max', function() {
+ test('-10', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ max: -10 });
+ numberField.setValue(-20);
+ assertValue(numberField, -20);
+ numberField.setValue(0);
+ assertValue(numberField, -10);
+ numberField.setValue(20);
+ assertValue(numberField, -10);
+ });
+ test('0', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ max: 0 });
+ numberField.setValue(-20);
+ assertValue(numberField, -20);
+ numberField.setValue(0);
+ assertValue(numberField, 0);
+ numberField.setValue(20);
+ assertValue(numberField, 0);
+ });
+ test('+10', function() {
+ var numberField = new Blockly.FieldNumber.fromJson({ max: 10 });
+ numberField.setValue(-20);
+ assertValue(numberField, -20);
+ numberField.setValue(0);
+ assertValue(numberField, 0);
+ numberField.setValue(20);
+ assertValue(numberField, 10);
+ });
+ });
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.numberFieldField = new Blockly.FieldNumber(1);
+ Blockly.FieldTextInput.htmlInput_ = Object.create(null);
+ Blockly.FieldTextInput.htmlInput_.oldValue_ = '1';
+ Blockly.FieldTextInput.htmlInput_.untypedDefaultValue_ = 1;
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.numberFieldField.setValidator(function() {
+ return null;
+ });
+ });
+ test('When Editing', function() {
+ this.numberFieldField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = '2';
+ this.numberFieldField.onHtmlInputChange_(null);
+ assertValue(this.numberFieldField, 1, '2');
+ this.numberFieldField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.numberFieldField.setValue(2);
+ assertValue(this.numberFieldField, 1);
+ });
+ });
+ suite('Force End with 6 Validator', function() {
+ setup(function() {
+ this.numberFieldField.setValidator(function(newValue) {
+ return String(newValue).replace(/.$/, "6");
+ });
+ });
+ test('When Editing', function() {
+ this.numberFieldField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = '25';
+ this.numberFieldField.onHtmlInputChange_(null);
+ assertValue(this.numberFieldField, 26, '25');
+ this.numberFieldField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.numberFieldField.setValue(25);
+ assertValue(this.numberFieldField, 26);
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_textinput_test.js b/tests/mocha/field_textinput_test.js
new file mode 100644
index 000000000..2a1b72b06
--- /dev/null
+++ b/tests/mocha/field_textinput_test.js
@@ -0,0 +1,213 @@
+/**
+ * @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 ('Text Input Fields', function() {
+ function assertValue(textInputField, expectedValue, opt_expectedText) {
+ var actualValue = textInputField.getValue();
+ var actualText = textInputField.getText();
+ opt_expectedText = opt_expectedText || expectedValue;
+ assertEquals(actualValue, expectedValue);
+ assertEquals(actualText, opt_expectedText);
+ }
+ function assertValueDefault(textInputField) {
+ assertValue(textInputField, '');
+ }
+ suite('Constructor', function() {
+ test('Empty', function() {
+ var textInputField = new Blockly.FieldTextInput();
+ assertValueDefault(textInputField);
+ });
+ test('Null', function() {
+ var textInputField = new Blockly.FieldTextInput(null);
+ assertValueDefault(textInputField);
+ });
+ test('Undefined', function() {
+ var textInputField = new Blockly.FieldTextInput(undefined);
+ assertValueDefault(textInputField);
+ });
+ test('String', function() {
+ var textInputField = new Blockly.FieldTextInput('value');
+ assertValue(textInputField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var textInputField = new Blockly.FieldTextInput(1);
+ assertValue(textInputField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var textInputField = new Blockly.FieldTextInput(0);
+ assertValue(textInputField, '0');
+ });
+ test('Boolean True', function() {
+ var textInputField = new Blockly.FieldTextInput(true);
+ assertValue(textInputField, 'true');
+ });
+ test('Boolean False', function() {
+ var textInputField = new Blockly.FieldTextInput(false);
+ assertValue(textInputField, 'false');
+ });
+ });
+ suite('fromJson', function() {
+ test('Empty', function() {
+ var textInputField = new Blockly.FieldTextInput.fromJson({});
+ assertValueDefault(textInputField);
+ });
+ test('Null', function() {
+ var textInputField = new Blockly.FieldTextInput.fromJson({ text: null});
+ assertValueDefault(textInputField);
+ });
+ test('Undefined', function() {
+ var textInputField = new Blockly.FieldTextInput
+ .fromJson({ text: undefined});
+ assertValueDefault(textInputField);
+ });
+ test('String', function() {
+ var textInputField = Blockly.FieldTextInput.fromJson({ text:'value' });
+ assertValue(textInputField, 'value');
+ });
+ test('Number (Truthy)', function() {
+ var textInputField = Blockly.FieldTextInput.fromJson({ text:1 });
+ assertValue(textInputField, '1');
+ });
+ test('Number (Falsy)', function() {
+ var textInputField = Blockly.FieldTextInput.fromJson({ text:0 });
+ assertValue(textInputField, '0');
+ });
+ test('Boolean True', function() {
+ var textInputField = Blockly.FieldTextInput.fromJson({ text:true });
+ assertValue(textInputField, 'true');
+ });
+ test('Boolean False', function() {
+ var textInputField = Blockly.FieldTextInput.fromJson({ text:false });
+ assertValue(textInputField, 'false');
+ });
+ });
+ suite('setValue', function() {
+ suite('Empty -> New Value', function() {
+ setup(function() {
+ this.textInputField = new Blockly.FieldTextInput();
+ });
+ test('Null', function() {
+ this.textInputField.setValue(null);
+ assertValueDefault(this.textInputField);
+ });
+ test.skip('Undefined', function() {
+ this.textInputField.setValue(undefined);
+ assertValueDefault(this.textInputField);
+ });
+ test('New String', function() {
+ this.textInputField.setValue('newValue');
+ assertValue(this.textInputField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.textInputField.setValue(1);
+ assertValue(this.textInputField, '1');
+ });
+ test.skip('Number (Falsy)', function() {
+ this.textInputField.setValue(0);
+ assertValue(this.textInputField, '0');
+ });
+ test('Boolean True', function() {
+ this.textInputField.setValue(true);
+ assertValue(this.textInputField, 'true');
+ });
+ test.skip('Boolean False', function() {
+ this.textInputField.setValue(false);
+ assertValue(this.textInputField, 'false');
+ });
+ });
+ suite('Value -> New Value', function() {
+ setup(function() {
+ this.textInputField = new Blockly.FieldTextInput('value');
+ });
+ test('Null', function() {
+ this.textInputField.setValue(null);
+ assertValue(this.textInputField, 'value');
+ });
+ test.skip('Undefined', function() {
+ this.textInputField.setValue(undefined);
+ assertValue(this.textInputField, 'value');
+ });
+ test('New String', function() {
+ this.textInputField.setValue('newValue');
+ assertValue(this.textInputField, 'newValue');
+ });
+ test('Number (Truthy)', function() {
+ this.textInputField.setValue(1);
+ assertValue(this.textInputField, '1');
+ });
+ test('Number (Falsy)', function() {
+ this.textInputField.setValue(0);
+ assertValue(this.textInputField, '0');
+ });
+ test('Boolean True', function() {
+ this.textInputField.setValue(true);
+ assertValue(this.textInputField, 'true');
+ });
+ test('Boolean False', function() {
+ this.textInputField.setValue(false);
+ assertValue(this.textInputField, 'false');
+ });
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.textInputField = new Blockly.FieldTextInput('value');
+ Blockly.FieldTextInput.htmlInput_ = Object.create(null);
+ Blockly.FieldTextInput.htmlInput_.oldValue_ = 'value';
+ Blockly.FieldTextInput.htmlInput_.untypedDefaultValue_ = 'value';
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.textInputField.setValidator(function() {
+ return null;
+ });
+ });
+ test('When Editing', function() {
+ this.textInputField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = 'newValue';
+ this.textInputField.onHtmlInputChange_(null);
+ assertValue(this.textInputField, 'value', 'newValue');
+ this.textInputField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.textInputField.setValue('newValue');
+ assertValue(this.textInputField, 'value');
+ });
+ });
+ suite('Remove \'a\' Validator', function() {
+ setup(function() {
+ this.textInputField.setValidator(function(newValue) {
+ return newValue.replace(/a/g, '');
+ });
+ });
+ test('When Editing', function() {
+ this.textInputField.isBeingEdited_ = true;
+ Blockly.FieldTextInput.htmlInput_.value = 'bbbaaa';
+ this.textInputField.onHtmlInputChange_(null);
+ assertValue(this.textInputField, 'bbb', 'bbbaaa');
+ this.textInputField.isBeingEdited_ = false;
+ });
+ test('When Not Editing', function() {
+ this.textInputField.setValue('bbbaaa');
+ assertValue(this.textInputField, 'bbb');
+ });
+ });
+ });
+});
diff --git a/tests/mocha/field_variable_test.js b/tests/mocha/field_variable_test.js
index da8338c38..8e7026eb6 100644
--- a/tests/mocha/field_variable_test.js
+++ b/tests/mocha/field_variable_test.js
@@ -1,5 +1,27 @@
+/**
+ * @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('Variable Fields', function() {
+ var FAKE_VARIABLE_NAME = 'yertle';
+ var FAKE_ID = 'turtle';
+
function getMockBlock(workspace) {
return {
'workspace': workspace,
@@ -8,54 +30,49 @@ suite('Variable Fields', function() {
}
};
}
- function fieldVariable_createAndInitField(workspace) {
- var fieldVariable = new Blockly.FieldVariable('name1');
+ function initField(fieldVariable, workspace) {
var mockBlock = getMockBlock(workspace);
fieldVariable.setSourceBlock(mockBlock);
// No view to initialize, but still need to init the model.
fieldVariable.initModel();
return fieldVariable;
}
+ function createAndInitFieldConstructor(workspace, variableName) {
+ return initField(new Blockly.FieldVariable(variableName), workspace);
+ }
+ function createAndInitFieldJson(workspace, variableName) {
+ return initField(Blockly.FieldVariable.fromJson(
+ { variable:variableName }), workspace);
+ }
+ function assertValue(variableField, expectedName, opt_expectedId) {
+ var actualName = variableField.getText();
+ var actualId = variableField.getValue();
+ opt_expectedId = opt_expectedId || FAKE_ID;
+ assertEquals(actualName, expectedName);
+ assertEquals(actualId, opt_expectedId);
+ }
+ function assertValueDefault(variableField) {
+ assertValue(variableField, FAKE_VARIABLE_NAME, FAKE_ID);
+ }
setup(function() {
this.workspace = new Blockly.Workspace();
+
+ sinon.stub(Blockly.utils, 'genUid')
+ .returns(FAKE_ID);
+ sinon.stub(Blockly.Variables, 'generateUniqueName')
+ .returns(FAKE_VARIABLE_NAME);
});
teardown(function() {
this.workspace.dispose();
- });
-
- test('Constructor', function() {
- var fieldVariable = new Blockly.FieldVariable('name1');
- // The field does not have a variable until after init() is called.
- assertEquals('', fieldVariable.getText());
- assertNull(fieldVariable.getValue());
- });
-
- test('Set value by ID', function() {
- this.workspace.createVariable('name2', null, 'id2');
-
- var fieldVariable = fieldVariable_createAndInitField(this.workspace);
- var oldId = fieldVariable.getValue();
-
- fieldVariable.setValue('id2');
- // Setting value by ID gives us the right text as well.
- assertEquals('name2', fieldVariable.getText());
- assertEquals('id2', fieldVariable.getValue());
- chai.assert.notEqual(oldId, fieldVariable.getValue());
- });
-
- test('Set value: variable does not exist', function() {
- var fieldVariable = fieldVariable_createAndInitField(this.workspace);
- chai.assert.throws(function() {
- fieldVariable.setValue('id1');
- });
+ sinon.restore();
});
test('Dropdown contains variables', function() {
// Expect that the dropdown options will contain the variables that exist
this.workspace.createVariable('name1', '', 'id1');
this.workspace.createVariable('name2', '', 'id2');
- var fieldVariable = fieldVariable_createAndInitField(this.workspace);
+ var fieldVariable = createAndInitFieldConstructor(this.workspace, 'name1');
// Expect that variables created after field creation will show up too.
this.workspace.createVariable('name3', '', 'id3');
@@ -68,9 +85,112 @@ suite('Variable Fields', function() {
isEqualArrays(result_options[0], ['name1', 'id1']);
isEqualArrays(result_options[1], ['name2', 'id2']);
isEqualArrays(result_options[2], ['name3', 'id3']);
-
});
+ suite('Constructor', function() {
+ test('Null', function() {
+ var variableField = createAndInitFieldConstructor(this.workspace, null);
+ assertValueDefault(variableField);
+ });
+ test('Undefined', function() {
+ var variableField = createAndInitFieldConstructor(
+ this.workspace, undefined);
+ assertValueDefault(variableField);
+ });
+ test('No Value Before InitModel', function() {
+ var fieldVariable = new Blockly.FieldVariable('name1');
+ assertEquals('', fieldVariable.getText());
+ assertNull(fieldVariable.getValue());
+ });
+ test('Given Variable Name', function() {
+ var fieldVariable = createAndInitFieldConstructor(
+ this.workspace, 'name1');
+ assertValue(fieldVariable, 'name1');
+ });
+ });
+ suite('fromJson', function() {
+ test('Null', function() {
+ var variableField = createAndInitFieldJson(this.workspace, null);
+ assertValueDefault(variableField);
+ });
+ test('Undefined', function() {
+ var variableField = createAndInitFieldJson(this.workspace, undefined);
+ assertValueDefault(variableField);
+ });
+ test('No Value Before InitModel', function() {
+ var variableField = new Blockly.FieldVariable('name1');
+ assertEquals('', variableField.getText());
+ assertNull(variableField.getValue());
+ });
+ test('Given Variable Name', function() {
+ var variableField = createAndInitFieldJson(this.workspace, 'name1');
+ assertValue(variableField, 'name1');
+ });
+ });
+ suite('setValue', function() {
+ test.skip('Null', function() {
+ var variableField = createAndInitFieldConstructor(
+ this.workspace, 'name1');
+ variableField.setValue(null);
+ assertValue(variableField, 'name1');
+ });
+ test.skip('Undefined', function() {
+ var variableField = createAndInitFieldConstructor(
+ this.workspace, 'name1');
+ variableField.setValue(undefined);
+ assertValue(variableField, 'name1');
+ });
+ test('New Variable ID', function() {
+ this.workspace.createVariable('name2', null, 'id2');
+ var variableField = createAndInitFieldConstructor(
+ this.workspace, 'name1');
+ var oldId = variableField.getValue();
+
+ variableField.setValue('id2');
+ // Setting value by ID gives us the right text as well.
+ assertEquals('name2', variableField.getText());
+ assertEquals('id2', variableField.getValue());
+ chai.assert.notEqual(oldId, variableField.getValue());
+ });
+ test.skip('Variable Does not Exist', function() {
+ var variableField = createAndInitFieldConstructor(
+ this.workspace, 'name1');
+ variableField.setValue('id1');
+ assertValue(variableField, 'name1');
+ });
+ });
+ suite.skip('Validators', function() {
+ setup(function() {
+ this.workspace.createVariable('name1', null, 'id1');
+ this.workspace.createVariable('name2', null, 'id2');
+ this.workspace.createVariable('name3', null, 'id3');
+ this.variableField = createAndInitFieldConstructor(this.workspace, 'name1');
+ });
+ suite('Null Validator', function() {
+ setup(function() {
+ this.variableField.setValidator(function() {
+ return null;
+ });
+ });
+ test('New Value', function() {
+ this.variableField.setValue('id2');
+ assertValue(this.variableField, 'name1', 'id1');
+ });
+ });
+ suite('Force \'id\' ID Validator', function() {
+ setup(function() {
+ this.variableField.setValidator(function(newValue) {
+ return 'id' + newValue.charAt(newValue.length - 1);
+ });
+ });
+ test('New Value', function() {
+ // Must create the var so that the field doesn't throw an error.
+ this.workspace.createVariable('thing2', null, 'other2');
+ this.variableField.setValue('other2');
+ assertValue(this.variableField, 'name2', 'id2');
+ });
+ });
+ });
suite('Get variable types', function() {
setup(function() {
this.workspace.createVariable('name1', 'type1');
@@ -121,7 +241,6 @@ suite('Variable Fields', function() {
});
});
});
-
suite('Default types', function() {
test('Default type exists', function() {
var fieldVariable = new Blockly.FieldVariable(null, null, ['b'], 'b');
diff --git a/tests/mocha/index.html b/tests/mocha/index.html
index f65647865..48c0c2e51 100644
--- a/tests/mocha/index.html
+++ b/tests/mocha/index.html
@@ -19,11 +19,23 @@
ui: 'tdd'
});
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/mocha/test_helpers.js b/tests/mocha/test_helpers.js
index deea338e8..fc6b1e795 100644
--- a/tests/mocha/test_helpers.js
+++ b/tests/mocha/test_helpers.js
@@ -104,5 +104,5 @@ function assertNotUndefined() {
_validateArguments(1, arguments);
var commentArg = _commentArg(1, arguments);
var val = _nonCommentArg(1, 1, arguments);
- chai.assert.isNotUndefined(val, commentArg);
+ chai.assert.isDefined(val, commentArg);
}