Files
blockly/tests/mocha/field_test.js
Neil Fraser b46a4fe286 Bring our license format up to date (#3127)
* Google changed from an Inc to an LLC.

This happened back in 2017 but we didn’t notice.  Officially we should update files from Inc to LLC when they are changed as part of regular edits, but this is a nightmare to remember for the next decade.

* Remove project description/titles from licenses

This is no longer part of Google’s header requirements.  Our existing descriptions were useless (“Visual Blocks Editor”) or grossly obselete (“Visual Blocks Language”).

* License no longer requires URL.

* Fix license regexps.
2019-10-02 14:46:56 -07:00

538 lines
20 KiB
JavaScript

/**
* @license
* Copyright 2019 Google LLC
*
* 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('Abstract Fields', function() {
suite('Is Serializable', function() {
// Both EDITABLE and SERIALIZABLE are default.
function FieldDefault() {
this.name = 'NAME';
}
FieldDefault.prototype = Object.create(Blockly.Field.prototype);
// EDITABLE is false and SERIALIZABLE is default.
function FieldFalseDefault() {
this.name = 'NAME';
}
FieldFalseDefault.prototype = Object.create(Blockly.Field.prototype);
FieldFalseDefault.prototype.EDITABLE = false;
// EDITABLE is default and SERIALIZABLE is true.
function FieldDefaultTrue() {
this.name = 'NAME';
}
FieldDefaultTrue.prototype = Object.create(Blockly.Field.prototype);
FieldDefaultTrue.prototype.SERIALIZABLE = true;
// EDITABLE is false and SERIALIZABLE is true.
function FieldFalseTrue() {
this.name = 'NAME';
}
FieldFalseTrue.prototype = Object.create(Blockly.Field.prototype);
FieldFalseTrue.prototype.EDITABLE = false;
FieldFalseTrue.prototype.SERIALIZABLE = true;
/* Test Backwards Compatibility */
test('Editable Default(true), Serializable Default(false)', function() {
// An old default field should be serialized.
var field = new FieldDefault();
var stub = sinon.stub(console, 'warn');
assertEquals(true, field.isSerializable());
chai.assert(stub.calledOnce);
stub.restore();
});
test('Editable False, Serializable Default(false)', function() {
// An old non-editable field should not be serialized.
var field = new FieldFalseDefault();
assertEquals(false, field.isSerializable());
});
/* Test Other Cases */
test('Editable Default(true), Serializable True', function() {
// A field that is both editable and serializable should be serialized.
var field = new FieldDefaultTrue();
assertEquals(true, field.isSerializable());
});
test('Editable False, Serializable True', function() {
// A field that is not editable, but overrides serializable to true
// should be serialized (e.g. field_label_serializable)
var field = new FieldFalseTrue();
assertEquals(true, field.isSerializable());
});
});
suite('setValue', function() {
function addSpies(field) {
if (!this.isSpying) {
sinon.spy(field, 'doValueInvalid_');
sinon.spy(field, 'doValueUpdate_');
sinon.spy(field, 'forceRerender');
this.isSpying = true;
}
}
function removeSpies(field) {
if (this.isSpying) {
field.doValueInvalid_.restore();
field.doValueUpdate_.restore();
field.forceRerender.restore();
this.isSpying = false;
}
}
setup(function() {
this.field = new Blockly.Field();
this.field.isDirty_ = false;
this.cachedDoClassValidation = this.field.doClassValidation_;
this.cachedDoValueUpdate = this.field.doValueUpdate_;
this.cachedDoValueInvalid = this.field.doValueInvalid_;
});
teardown(function() {
removeSpies(this.field);
this.field.doClassValidation_ = this.cachedDoClassValidation;
this.field.doValueUpdate_ = this.cachedDoValueUpdate;
this.field.doValueInvalid_ = this.cachedDoValueInvalid;
this.field.setValidator(null);
});
test('Null', function() {
addSpies(this.field);
this.field.setValue(null);
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('No Validators, Dirty (Default)', function() {
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.calledOnce);
});
test('No Validators, Not Dirty', function() {
this.field.doValueUpdate_ = function(newValue) {
this.value_ = newValue;
this.isDirty_ = false;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.notCalled);
});
test('Class Validator Returns Invalid, Not Dirty (Default)', function() {
this.field.doClassValidation_ = function() {
return null;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('Class Validator Returns Invalid, Dirty', function() {
this.field.doClassValidation_ = function() {
return null;
};
this.field.doValueInvalid_ = function() {
this.isDirty_ = true;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.calledOnce);
});
test('Class Validator Returns Valid, Not Dirty', function() {
this.field.doClassValidation_ = function(newValue) {
return newValue;
};
this.field.doValueUpdate_ = function() {
this.isDirty_ = false;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.notCalled);
});
test('Class Validator Returns Valid, Dirty (Default)', function() {
this.field.doClassValidation_ = function(newValue) {
return newValue;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.calledOnce);
});
test('Local Validator Returns Invalid, Not Dirty (Default)', function() {
this.field.setValidator(function() {
return null;
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('Local Validator Returns Invalid, Dirty', function() {
this.field.setValidator(function() {
return null;
});
this.field.doValueInvalid_ = function() {
this.isDirty_ = true;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.calledOnce);
});
test('Local Validator Returns Valid, Not Dirty', function() {
this.field.setValidator(function(newValue) {
return newValue;
});
this.field.doValueUpdate_ = function() {
this.isDirty_ = false;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.notCalled);
});
test('Local Validator Returns Valid, Dirty (Default)', function() {
this.field.setValidator(function(newValue) {
return newValue;
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
chai.assert(this.field.forceRerender.calledOnce);
});
test('New Value Matches Old Value', function() {
this.field.setValue('value');
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('New Value (Class)Validates to Old Value', function() {
this.field.setValue('value');
this.field.doClassValidation_ = function() {
return 'value';
};
addSpies(this.field);
this.field.setValue('notValue');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('New Value (Local)Validates to Old Value', function() {
this.field.setValue('value');
this.field.setValidator(function() {
return 'value';
});
addSpies(this.field);
this.field.setValue('notValue');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.notCalled);
chai.assert(this.field.forceRerender.notCalled);
});
test('New Value (Class)Validates to not Old Value', function() {
this.field.setValue('value');
this.field.doClassValidation_ = function() {
return 'notValue';
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('New Value (Local)Validates to not Old Value', function() {
this.field.setValue('value');
this.field.setValidator(function() {
return 'notValue';
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Class Validator Returns Null', function() {
this.field.doClassValidation_ = function() {
return null;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
});
test('Class Validator Returns Same', function() {
this.field.doClassValidation_ = function(newValue) {
return newValue;
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Class Validator Returns Different', function() {
this.field.doClassValidation_ = function() {
return 'differentValue';
};
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Class Validator Returns Undefined', function() {
this.field.doClassValidation_ = function() {};
addSpies(this.field);
this.field.setValue('value');
chai.assert.equal(this.field.getValue(), 'value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Local Validator Returns Null', function() {
this.field.setValidator(function() {
return null;
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.calledOnce);
chai.assert(this.field.doValueUpdate_.notCalled);
});
test('Local Validator Returns Same', function() {
this.field.setValidator(function(newValue) {
return newValue;
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Local Validator Returns Different', function() {
this.field.setValidator(function() {
return 'differentValue';
});
addSpies(this.field);
this.field.setValue('value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
test('Local Validator Returns Undefined', function() {
this.field.setValidator(function() {});
addSpies(this.field);
this.field.setValue('value');
chai.assert.equal(this.field.getValue(), 'value');
chai.assert(this.field.doValueInvalid_.notCalled);
chai.assert(this.field.doValueUpdate_.calledOnce);
});
});
suite('Customization', function() {
// All this field does is wrap the abstract field.
function CustomField(opt_config) {
CustomField.superClass_.constructor.call(
this, 'value', null, opt_config);
}
Blockly.utils.object.inherits(CustomField, Blockly.Field);
CustomField.fromJson = function(options) {
return new CustomField(options);
};
suite('Tooltip', function() {
test('JS Constructor', function() {
var field = new Blockly.Field('value', null, {
tooltip: 'test tooltip',
});
chai.assert.equal(field.tooltip_, 'test tooltip');
});
test('JS Constructor - Dynamic', function() {
var returnTooltip = function() {
return 'dynamic tooltip text';
};
var field = new Blockly.Field('value', null, {
tooltip: returnTooltip
});
chai.assert.equal(field.tooltip_, returnTooltip);
});
test('JSON Definition', function() {
var field = CustomField.fromJson({
tooltip: "test tooltip"
});
chai.assert.equal(field.tooltip_, 'test tooltip');
});
suite('W/ Msg References', function() {
setup(function() {
Blockly.Msg['TOOLTIP'] = 'test tooltip';
});
teardown(function() {
delete Blockly.Msg['TOOLTIP'];
});
test('JS Constructor', function() {
var field = new Blockly.Field('value', null, {
tooltip: '%{BKY_TOOLTIP}',
});
chai.assert.equal(field.tooltip_, 'test tooltip');
});
test('JSON Definition', function() {
var field = CustomField.fromJson({
tooltip: "%{BKY_TOOLTIP}"
});
chai.assert.equal(field.tooltip_, 'test tooltip');
});
});
suite('setTooltip', function() {
setup(function() {
this.workspace = new Blockly.WorkspaceSvg({});
this.workspace.createDom();
});
teardown(function() {
this.workspace = null;
});
test('Before Append', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
field.setTooltip('tooltip');
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, 'tooltip');
delete Blockly.Blocks['tooltip'];
});
test('After Append', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
field.setTooltip('tooltip');
},
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, 'tooltip');
delete Blockly.Blocks['tooltip'];
});
test('After Block Creation', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
field.setTooltip('tooltip');
chai.assert.equal(field.getClickTarget_().tooltip, 'tooltip');
delete Blockly.Blocks['tooltip'];
});
test('Dynamic Function', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
field.setTooltip(this.tooltipFunc);
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
tooltipFunc: function() {
return this.getFieldValue('TOOLTIP');
}
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, block.tooltipFunc);
delete Blockly.Blocks['tooltip'];
});
test('Element', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
field.setTooltip(this.element);
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
element: {
tooltip: 'tooltip'
}
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, block.element);
delete Blockly.Blocks['tooltip'];
});
test('Null', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
field.setTooltip(null);
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, block);
delete Blockly.Blocks['tooltip'];
});
test('Undefined', function() {
Blockly.Blocks['tooltip'] = {
init: function() {
var field = new Blockly.FieldTextInput('default');
this.appendDummyInput()
.appendField(field, 'TOOLTIP');
},
};
var block = Blockly.Xml.domToBlock(Blockly.Xml.textToDom(
'<xml xmlns="https://developers.google.com/blockly/xml">' +
' <block type="tooltip"></block>' +
'</xml>'
).children[0], this.workspace);
var field = block.getField('TOOLTIP');
chai.assert.equal(field.getClickTarget_().tooltip, block);
delete Blockly.Blocks['tooltip'];
});
});
});
});
});