mirror of
https://github.com/google/blockly.git
synced 2026-01-10 18:37:09 +01:00
* chore: add and configure prettier * chore: remove clang-format * chore: remove clang-format config * chore: lint additional ts files * chore: fix lint errors in blocks * chore: add prettier-ignore where needed * chore: ignore js blocks when formatting * chore: fix playground html syntax * chore: fix yaml spacing from merge * chore: convert text blocks to use arrow functions * chore: format everything with prettier * chore: fix lint unused imports in blocks
668 lines
20 KiB
JavaScript
668 lines
20 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.declareModuleId('Blockly.test.flyout');
|
|
|
|
import {
|
|
sharedTestSetup,
|
|
sharedTestTeardown,
|
|
workspaceTeardown,
|
|
} from './test_helpers/setup_teardown.js';
|
|
import {defineStackBlock} from './test_helpers/block_definitions.js';
|
|
import {
|
|
getBasicToolbox,
|
|
getChildItem,
|
|
getCollapsibleItem,
|
|
getDeeplyNestedJSON,
|
|
getInjectedToolbox,
|
|
getNonCollapsibleItem,
|
|
getProperSimpleJson,
|
|
getSeparator,
|
|
getSimpleJson,
|
|
getXmlArray,
|
|
} from './test_helpers/toolbox_definitions.js';
|
|
|
|
suite('Flyout', function () {
|
|
setup(function () {
|
|
sharedTestSetup.call(this);
|
|
Blockly.defineBlocksWithJsonArray([
|
|
{
|
|
'type': 'basic_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'field_input',
|
|
'name': 'TEXT',
|
|
'text': 'default',
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
this.toolboxXml = document.getElementById('toolbox-simple');
|
|
this.workspace = Blockly.inject('blocklyDiv', {
|
|
toolbox: this.toolboxXml,
|
|
});
|
|
});
|
|
|
|
teardown(function () {
|
|
sharedTestTeardown.call(this);
|
|
});
|
|
|
|
suite('position', function () {
|
|
suite('vertical flyout', function () {
|
|
suite('simple flyout', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
test('y is always 0', function () {
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
0,
|
|
'y coordinate in vertical flyout should be 0'
|
|
);
|
|
});
|
|
test('x is right of workspace if flyout at right', function () {
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
width: 100,
|
|
});
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.RIGHT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.RIGHT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
100,
|
|
'x should be right of workspace'
|
|
);
|
|
});
|
|
test('x is 0 if flyout at left', function () {
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.LEFT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.LEFT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
0,
|
|
'x should be 0 if the flyout is on the left'
|
|
);
|
|
});
|
|
});
|
|
suite('toolbox flyout', function () {
|
|
setup(function () {
|
|
const toolbox = document.getElementById('toolbox-categories');
|
|
this.workspace = Blockly.inject('blocklyDiv', {
|
|
toolbox: toolbox,
|
|
});
|
|
this.flyout = this.workspace.getToolbox().getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
teardown(function () {
|
|
workspaceTeardown.call(this, this.workspace);
|
|
});
|
|
test('x is aligned with toolbox at left', function () {
|
|
sinon.stub(this.targetMetricsManager, 'getToolboxMetrics').returns({
|
|
width: 20,
|
|
});
|
|
this.flyout.setVisible(true);
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.LEFT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.LEFT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
20,
|
|
'x should be aligned with toolbox'
|
|
);
|
|
});
|
|
test('x is aligned with toolbox at right', function () {
|
|
sinon.stub(this.targetMetricsManager, 'getToolboxMetrics').returns({
|
|
width: 20,
|
|
});
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
width: 100,
|
|
});
|
|
this.flyout.width_ = 10;
|
|
this.flyout.setVisible(true);
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.RIGHT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.RIGHT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
90,
|
|
'x + width should be aligned with toolbox'
|
|
);
|
|
});
|
|
});
|
|
// These tests simulate a trashcan flyout, i.e. the flyout under test is on the
|
|
// opposite side of the workspace toolbox setting.
|
|
suite('trashcan flyout', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
test('x is 0 if trashcan on left', function () {
|
|
sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
|
|
viewWidth: 100,
|
|
});
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.RIGHT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.LEFT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
0,
|
|
'x should be aligned with left edge'
|
|
);
|
|
});
|
|
test('trashcan on right covers right edge of workspace', function () {
|
|
this.flyout.width_ = 20;
|
|
sinon.stub(this.targetMetricsManager, 'getAbsoluteMetrics').returns({
|
|
left: 10,
|
|
});
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
width: 100,
|
|
});
|
|
|
|
this.flyout.setVisible(true);
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.LEFT;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.RIGHT;
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
90,
|
|
'x + width should be aligned with right edge'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('horizontal flyout', function () {
|
|
setup(function () {
|
|
this.workspace = Blockly.inject('blocklyDiv', {
|
|
toolbox: this.toolboxXml,
|
|
horizontalLayout: true,
|
|
});
|
|
});
|
|
teardown(function () {
|
|
workspaceTeardown.call(this, this.workspace);
|
|
});
|
|
suite('simple flyout', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
test('x is always 0', function () {
|
|
chai.assert.equal(
|
|
this.flyout.getX(),
|
|
0,
|
|
'x coordinate in horizontal flyout should be 0'
|
|
);
|
|
});
|
|
test('y is 0 if flyout at top', function () {
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.TOP;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.TOP;
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
0,
|
|
'y should be 0 if flyout is at the top'
|
|
);
|
|
});
|
|
test('y is below workspace if flyout at bottom', function () {
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.BOTTOM;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.BOTTOM;
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
height: 50,
|
|
});
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
50,
|
|
'y should be below the workspace'
|
|
);
|
|
});
|
|
});
|
|
suite('toolbox flyout', function () {
|
|
setup(function () {
|
|
const toolbox = document.getElementById('toolbox-categories');
|
|
this.workspace = Blockly.inject('blocklyDiv', {
|
|
toolbox: toolbox,
|
|
horizontalLayout: true,
|
|
});
|
|
this.flyout = this.workspace.getToolbox().getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
teardown(function () {
|
|
workspaceTeardown.call(this, this.workspace);
|
|
});
|
|
test('y is aligned with toolbox at top', function () {
|
|
sinon.stub(this.targetMetricsManager, 'getToolboxMetrics').returns({
|
|
height: 20,
|
|
});
|
|
this.flyout.setVisible(true);
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.TOP;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.TOP;
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
20,
|
|
'y should be aligned with toolbox'
|
|
);
|
|
});
|
|
test('y is aligned with toolbox at bottom', function () {
|
|
sinon.stub(this.targetMetricsManager, 'getToolboxMetrics').returns({
|
|
height: 20,
|
|
});
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
height: 100,
|
|
});
|
|
this.flyout.height_ = 30;
|
|
this.flyout.setVisible(true);
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.BOTTOM;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.BOTTOM;
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
70,
|
|
'y + height should be aligned with toolbox'
|
|
);
|
|
});
|
|
});
|
|
// These tests simulate a trashcan flyout, i.e. the flyout under test is on the
|
|
// opposite side of the workspace toolbox setting.
|
|
suite('trashcan flyout', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
this.targetMetricsManager =
|
|
this.flyout.targetWorkspace.getMetricsManager();
|
|
});
|
|
test('y is 0 if trashcan at top', function () {
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.BOTTOM;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.TOP;
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
0,
|
|
'y should be aligned with top'
|
|
);
|
|
});
|
|
test('trashcan on bottom covers bottom of workspace', function () {
|
|
this.flyout.targetWorkspace.toolboxPosition =
|
|
Blockly.utils.toolbox.Position.TOP;
|
|
this.flyout.toolboxPosition_ = Blockly.utils.toolbox.Position.BOTTOM;
|
|
sinon.stub(this.targetMetricsManager, 'getAbsoluteMetrics').returns({
|
|
top: 10,
|
|
});
|
|
sinon.stub(this.targetMetricsManager, 'getViewMetrics').returns({
|
|
height: 50,
|
|
});
|
|
this.flyout.setVisible(true);
|
|
this.flyout.height_ = 20;
|
|
chai.assert.equal(
|
|
this.flyout.getY(),
|
|
40,
|
|
'y + height should be aligned with bottom'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('createFlyoutInfo', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
this.createFlyoutSpy = sinon.spy(this.flyout, 'createFlyoutInfo');
|
|
});
|
|
|
|
function checkFlyoutInfo(flyoutSpy) {
|
|
const flyoutInfo = flyoutSpy.returnValues[0];
|
|
const contents = flyoutInfo.contents;
|
|
const gaps = flyoutInfo.gaps;
|
|
|
|
const expectedGaps = [20, 24, 24];
|
|
chai.assert.deepEqual(gaps, expectedGaps);
|
|
|
|
chai.assert.equal(contents.length, 3, 'Contents');
|
|
|
|
chai.assert.equal(contents[0].type, 'block', 'Contents');
|
|
const block = contents[0]['block'];
|
|
chai.assert.instanceOf(block, Blockly.BlockSvg);
|
|
chai.assert.equal(block.getFieldValue('OP'), 'NEQ');
|
|
const childA = block.getInputTargetBlock('A');
|
|
const childB = block.getInputTargetBlock('B');
|
|
chai.assert.isTrue(childA.isShadow());
|
|
chai.assert.isFalse(childB.isShadow());
|
|
chai.assert.equal(childA.getFieldValue('NUM'), 1);
|
|
chai.assert.equal(childB.getFieldValue('NUM'), 2);
|
|
|
|
chai.assert.equal(contents[1].type, 'button', 'Contents');
|
|
chai.assert.instanceOf(contents[1]['button'], Blockly.FlyoutButton);
|
|
|
|
chai.assert.equal(contents[2].type, 'button', 'Contents');
|
|
chai.assert.instanceOf(contents[2]['button'], Blockly.FlyoutButton);
|
|
}
|
|
|
|
suite('Direct show', function () {
|
|
test('Node', function () {
|
|
this.flyout.show(this.toolboxXml);
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
});
|
|
|
|
test('NodeList', function () {
|
|
const nodeList = document.getElementById('toolbox-simple').childNodes;
|
|
this.flyout.show(nodeList);
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
});
|
|
|
|
test('Array of JSON', function () {
|
|
this.flyout.show(getSimpleJson());
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
});
|
|
|
|
test('Array of Proper JSON', function () {
|
|
this.flyout.show(getProperSimpleJson());
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
});
|
|
|
|
test('Array of XML', function () {
|
|
this.flyout.show(getXmlArray());
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
});
|
|
});
|
|
|
|
suite('Dynamic category', function () {
|
|
setup(function () {
|
|
this.stubAndAssert = function (val) {
|
|
sinon
|
|
.stub(
|
|
this.flyout.workspace_.targetWorkspace,
|
|
'getToolboxCategoryCallback'
|
|
)
|
|
.returns(function () {
|
|
return val;
|
|
});
|
|
this.flyout.show('someString');
|
|
checkFlyoutInfo(this.createFlyoutSpy);
|
|
};
|
|
});
|
|
|
|
test('No category available', function () {
|
|
chai.assert.throws(
|
|
function () {
|
|
this.flyout.show('someString');
|
|
}.bind(this),
|
|
"Couldn't find a callback function when opening " +
|
|
'a toolbox category.'
|
|
);
|
|
});
|
|
|
|
test('Node', function () {
|
|
this.stubAndAssert(this.toolboxXml);
|
|
});
|
|
|
|
test('NodeList', function () {
|
|
this.stubAndAssert(
|
|
document.getElementById('toolbox-simple').childNodes
|
|
);
|
|
});
|
|
|
|
test('Array of JSON', function () {
|
|
this.stubAndAssert(getSimpleJson());
|
|
});
|
|
|
|
test('Array of Proper JSON', function () {
|
|
this.stubAndAssert(getProperSimpleJson());
|
|
});
|
|
|
|
test('Array of XML', function () {
|
|
this.stubAndAssert(getXmlArray());
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('Creating blocks', function () {
|
|
suite('Enabled/Disabled', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
|
|
this.assertDisabled = function (disabled) {
|
|
const block = this.flyout.getWorkspace().getTopBlocks(false)[0];
|
|
chai.assert.equal(!block.isEnabled(), disabled);
|
|
};
|
|
});
|
|
|
|
suite('XML', function () {
|
|
test('True string', function () {
|
|
const xml = Blockly.utils.xml.textToDom(
|
|
'<xml>' +
|
|
'<block type="text_print" disabled="true"></block>' +
|
|
'</xml>'
|
|
);
|
|
this.flyout.show(xml);
|
|
this.assertDisabled(true);
|
|
});
|
|
|
|
test('False string', function () {
|
|
const xml = Blockly.utils.xml.textToDom(
|
|
'<xml>' +
|
|
'<block type="text_print" disabled="false"></block>' +
|
|
'</xml>'
|
|
);
|
|
this.flyout.show(xml);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Disabled string', function () {
|
|
// The XML system supports this for some reason!?
|
|
const xml = Blockly.utils.xml.textToDom(
|
|
'<xml>' +
|
|
'<block type="text_print" disabled="disabled"></block>' +
|
|
'</xml>'
|
|
);
|
|
this.flyout.show(xml);
|
|
this.assertDisabled(true);
|
|
});
|
|
|
|
test('Different string', function () {
|
|
const xml = Blockly.utils.xml.textToDom(
|
|
'<xml>' +
|
|
'<block type="text_print" disabled="random"></block>' +
|
|
'</xml>'
|
|
);
|
|
this.flyout.show(xml);
|
|
this.assertDisabled(false);
|
|
});
|
|
});
|
|
|
|
suite('JSON', function () {
|
|
test('All undefined', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Enabled true', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'enabled': true,
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Enabled false', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'enabled': false,
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(true);
|
|
});
|
|
|
|
test('Disabled true string', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': 'true',
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(true);
|
|
});
|
|
|
|
test('Disabled false string', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': 'false',
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Disabled string', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': 'disabled', // This is not respected by the JSON!
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Disabled true value', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': true,
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(true);
|
|
});
|
|
|
|
test('Disabled false value', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': false,
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Disabled different string', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': 'random',
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
|
|
test('Disabled empty string', function () {
|
|
const json = [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'text_print',
|
|
'disabled': '',
|
|
},
|
|
];
|
|
this.flyout.show(json);
|
|
this.assertDisabled(false);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('Recycling', function () {
|
|
setup(function () {
|
|
this.flyout = this.workspace.getFlyout();
|
|
});
|
|
|
|
test('Recycling disabled', function () {
|
|
this.flyout.show({
|
|
'contents': [
|
|
{
|
|
'kind': 'BLOCK',
|
|
'type': 'math_number',
|
|
'fields': {
|
|
'NUM': 123,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
this.flyout.show({
|
|
'contents': [
|
|
{
|
|
'kind': 'BLOCK',
|
|
'type': 'math_number',
|
|
'fields': {
|
|
'NUM': 321,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
const block = this.flyout.workspace_.getAllBlocks()[0];
|
|
chai.assert.equal(block.getFieldValue('NUM'), 321);
|
|
});
|
|
|
|
test('Recycling enabled', function () {
|
|
this.flyout.blockIsRecyclable_ = function () {
|
|
return true;
|
|
};
|
|
this.flyout.show({
|
|
'contents': [
|
|
{
|
|
'kind': 'BLOCK',
|
|
'type': 'math_number',
|
|
'fields': {
|
|
'NUM': 123,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
this.flyout.show({
|
|
'contents': [
|
|
{
|
|
'kind': 'BLOCK',
|
|
'type': 'math_number',
|
|
'fields': {
|
|
'NUM': 321,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
const block = this.flyout.workspace_.getAllBlocks()[0];
|
|
chai.assert.equal(block.getFieldValue('NUM'), 123);
|
|
});
|
|
});
|
|
});
|