mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
* Reduce usage of obsolete .keyCode property.
* Rename private properties/methods which violate eslint rules.
* Use arrays of bound events rather than individual properties.
* Improve typing info.
* Also fix O(n^2) recursive performance issue in theme's getComponentStyle function.
* And replace String(...) with '${...}' (smaller, faster).
* .toString() is considered harmful.
681 lines
27 KiB
JavaScript
681 lines
27 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2020 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.declareModuleId('Blockly.test.toolbox');
|
|
|
|
import {defineStackBlock} from './test_helpers/block_definitions.js';
|
|
import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js';
|
|
import {getBasicToolbox, getCategoryJSON, getChildItem, getCollapsibleItem, getDeeplyNestedJSON, getInjectedToolbox, getNonCollapsibleItem, getSeparator, getSimpleJson, getXmlArray} from './test_helpers/toolbox_definitions.js';
|
|
|
|
|
|
suite('Toolbox', function() {
|
|
setup(function() {
|
|
sharedTestSetup.call(this);
|
|
defineStackBlock();
|
|
});
|
|
|
|
teardown(function() {
|
|
sharedTestTeardown.call(this);
|
|
});
|
|
|
|
suite('init', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
test('Init called -> HtmlDiv is created', function() {
|
|
chai.assert.isDefined(this.toolbox.HtmlDiv);
|
|
});
|
|
test('Init called -> HtmlDiv is inserted before parent node', function() {
|
|
const toolboxDiv = Blockly.common.getMainWorkspace().getInjectionDiv().childNodes[0];
|
|
chai.assert.equal(toolboxDiv.className,
|
|
'blocklyToolboxDiv blocklyNonSelectable');
|
|
});
|
|
test('Init called -> Toolbox is subscribed to background and foreground colour', function() {
|
|
const themeManager = this.toolbox.workspace_.getThemeManager();
|
|
const themeManagerSpy = sinon.spy(themeManager, 'subscribe');
|
|
const componentManager = this.toolbox.workspace_.getComponentManager();
|
|
sinon.stub(componentManager, 'addComponent');
|
|
this.toolbox.init();
|
|
sinon.assert.calledWith(themeManagerSpy, this.toolbox.HtmlDiv,
|
|
'toolboxBackgroundColour', 'background-color');
|
|
sinon.assert.calledWith(themeManagerSpy, this.toolbox.HtmlDiv,
|
|
'toolboxForegroundColour', 'color');
|
|
});
|
|
test('Init called -> Render is called', function() {
|
|
const renderSpy = sinon.spy(this.toolbox, 'render');
|
|
const componentManager = this.toolbox.workspace_.getComponentManager();
|
|
sinon.stub(componentManager, 'addComponent');
|
|
this.toolbox.init();
|
|
sinon.assert.calledOnce(renderSpy);
|
|
});
|
|
test('Init called -> Flyout is initialized', function() {
|
|
const componentManager = this.toolbox.workspace_.getComponentManager();
|
|
sinon.stub(componentManager, 'addComponent');
|
|
this.toolbox.init();
|
|
chai.assert.isDefined(this.toolbox.flyout_);
|
|
});
|
|
});
|
|
|
|
suite('render', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
test('Render called with valid toolboxDef -> Contents are created', function() {
|
|
const positionStub = sinon.stub(this.toolbox, 'position');
|
|
this.toolbox.render({'contents': [
|
|
{'kind': 'category', 'contents': []},
|
|
{'kind': 'category', 'contents': []},
|
|
]});
|
|
chai.assert.lengthOf(this.toolbox.contents_, 2);
|
|
sinon.assert.called(positionStub);
|
|
});
|
|
// TODO: Uncomment once implemented.
|
|
test.skip('Toolbox definition with both blocks and categories -> Should throw an error', function() {
|
|
const toolbox = this.toolbox;
|
|
const badToolboxDef = [
|
|
{
|
|
"kind": "block",
|
|
},
|
|
{
|
|
"kind": "category",
|
|
},
|
|
];
|
|
chai.assert.throws(function() {
|
|
toolbox.render({'contents': badToolboxDef});
|
|
}, 'Toolbox cannot have both blocks and categories in the root level.');
|
|
});
|
|
// TODO: Uncomment once implemented.
|
|
test.skip('Expanded set to true for a non collapsible toolbox item -> Should open flyout', function() {
|
|
this.toolbox.render(this.toolboxXml);
|
|
const selectedNode = this.toolbox.tree_.children_[0];
|
|
chai.assert.isTrue(selectedNode.selected_);
|
|
});
|
|
test('JSON toolbox definition -> Should create toolbox with contents', function() {
|
|
const jsonDef = {'contents': [
|
|
{
|
|
"kind": "category",
|
|
"contents": [
|
|
{
|
|
"kind": "block",
|
|
"blockxml": '<block xmlns="http://www.w3.org/1999/xhtml" type="basic_block"><field name="TEXT">FirstCategory-FirstBlock</field></block>',
|
|
},
|
|
{
|
|
"kind": "label",
|
|
"text": "Input/Output:",
|
|
"web-class": "ioLabel",
|
|
},
|
|
{
|
|
"kind": "button",
|
|
"text": "insert",
|
|
"callbackkey": "insertConnectionStacks",
|
|
"web-class": "ioLabel",
|
|
},
|
|
{
|
|
"kind": "sep",
|
|
"gap": "7",
|
|
},
|
|
],
|
|
},
|
|
]};
|
|
this.toolbox.render(jsonDef);
|
|
chai.assert.lengthOf(this.toolbox.contents_, 1);
|
|
});
|
|
test('multiple icon classes can be applied', function() {
|
|
const jsonDef = {'contents': [
|
|
{
|
|
"kind": "category",
|
|
"cssConfig": {
|
|
"icon": "customIcon customIconEvents",
|
|
},
|
|
"contents": [
|
|
{
|
|
"kind": "block",
|
|
"blockxml": '<block xmlns="http://www.w3.org/1999/xhtml" type="basic_block"><field name="TEXT">FirstCategory-FirstBlock</field></block>',
|
|
},
|
|
],
|
|
},
|
|
]};
|
|
chai.assert.doesNotThrow(() => {
|
|
this.toolbox.render(jsonDef);
|
|
});
|
|
chai.assert.lengthOf(this.toolbox.contents_, 1);
|
|
});
|
|
});
|
|
|
|
suite('onClick_', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
test('Toolbox clicked -> Should close flyout', function() {
|
|
const hideChaffStub = sinon.stub(
|
|
Blockly.WorkspaceSvg.prototype, "hideChaff");
|
|
const evt = new PointerEvent('pointerdown', {});
|
|
this.toolbox.HtmlDiv.dispatchEvent(evt);
|
|
sinon.assert.calledOnce(hideChaffStub);
|
|
});
|
|
test('Category clicked -> Should select category', function() {
|
|
const categoryXml = document.getElementsByClassName('blocklyTreeRow')[0];
|
|
const evt = {
|
|
'target': categoryXml,
|
|
};
|
|
const item = this.toolbox.contentMap_[categoryXml.getAttribute('id')];
|
|
const setSelectedSpy = sinon.spy(this.toolbox, 'setSelectedItem');
|
|
const onClickSpy = sinon.spy(item, 'onClick');
|
|
this.toolbox.onClick_(evt);
|
|
sinon.assert.calledOnce(setSelectedSpy);
|
|
sinon.assert.calledOnce(onClickSpy);
|
|
});
|
|
});
|
|
|
|
suite('onKeyDown_', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
function createKeyDownMock(key) {
|
|
return {
|
|
'key': key,
|
|
'preventDefault': function() {},
|
|
};
|
|
}
|
|
|
|
function testCorrectFunctionCalled(toolbox, key, funcName) {
|
|
const event = createKeyDownMock(key);
|
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
|
const selectMethodStub = sinon.stub(toolbox, funcName);
|
|
selectMethodStub.returns(true);
|
|
toolbox.onKeyDown_(event);
|
|
sinon.assert.called(selectMethodStub);
|
|
sinon.assert.called(preventDefaultEvent);
|
|
}
|
|
|
|
test('Down button is pushed -> Should call selectNext_', function() {
|
|
testCorrectFunctionCalled(this.toolbox, 'ArrowDown', 'selectNext_', true);
|
|
});
|
|
test('Up button is pushed -> Should call selectPrevious_', function() {
|
|
testCorrectFunctionCalled(this.toolbox, 'ArrowUp', 'selectPrevious_', true);
|
|
});
|
|
test('Left button is pushed -> Should call selectParent_', function() {
|
|
testCorrectFunctionCalled(this.toolbox, 'ArrowLeft', 'selectParent_', true);
|
|
});
|
|
test('Right button is pushed -> Should call selectChild_', function() {
|
|
testCorrectFunctionCalled(this.toolbox, 'ArrowRight', 'selectChild_', true);
|
|
});
|
|
test('Enter button is pushed -> Should toggle expanded', function() {
|
|
this.toolbox.selectedItem_ = getCollapsibleItem(this.toolbox);
|
|
const toggleExpandedStub = sinon.stub(this.toolbox.selectedItem_, 'toggleExpanded');
|
|
const event = createKeyDownMock('Enter');
|
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
|
this.toolbox.onKeyDown_(event);
|
|
sinon.assert.called(toggleExpandedStub);
|
|
sinon.assert.called(preventDefaultEvent);
|
|
});
|
|
test('Enter button is pushed when no item is selected -> Should not call prevent default', function() {
|
|
this.toolbox.selectedItem_ = null;
|
|
const event = createKeyDownMock('Enter');
|
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
|
this.toolbox.onKeyDown_(event);
|
|
sinon.assert.notCalled(preventDefaultEvent);
|
|
});
|
|
});
|
|
|
|
suite('Select Methods', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
suite('selectChild_', function() {
|
|
test('No item is selected -> Should not handle event', function() {
|
|
this.toolbox.selectedItem_ = null;
|
|
const handled = this.toolbox.selectChild_();
|
|
chai.assert.isFalse(handled);
|
|
});
|
|
test('Selected item is not collapsible -> Should not handle event', function() {
|
|
this.toolbox.selectedItem_ = getNonCollapsibleItem(this.toolbox);
|
|
const handled = this.toolbox.selectChild_();
|
|
chai.assert.isFalse(handled);
|
|
});
|
|
test('Selected item is collapsible -> Should expand', function() {
|
|
const collapsibleItem = getCollapsibleItem(this.toolbox);
|
|
this.toolbox.selectedItem_ = collapsibleItem;
|
|
const handled = this.toolbox.selectChild_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.isTrue(collapsibleItem.isExpanded());
|
|
chai.assert.equal(this.toolbox.selectedItem_, collapsibleItem);
|
|
});
|
|
|
|
test('Selected item is expanded -> Should select child', function() {
|
|
const collapsibleItem = getCollapsibleItem(this.toolbox);
|
|
collapsibleItem.expanded_ = true;
|
|
const selectNextStub = sinon.stub(this.toolbox, 'selectNext_');
|
|
this.toolbox.selectedItem_ = collapsibleItem;
|
|
const handled = this.toolbox.selectChild_();
|
|
chai.assert.isTrue(handled);
|
|
sinon.assert.called(selectNextStub);
|
|
});
|
|
});
|
|
|
|
suite('selectParent_', function() {
|
|
test('No item selected -> Should not handle event', function() {
|
|
this.toolbox.selectedItem_ = null;
|
|
const handled = this.toolbox.selectParent_();
|
|
chai.assert.isFalse(handled);
|
|
});
|
|
test('Selected item is expanded -> Should collapse', function() {
|
|
const collapsibleItem = getCollapsibleItem(this.toolbox);
|
|
collapsibleItem.expanded_ = true;
|
|
this.toolbox.selectedItem_ = collapsibleItem;
|
|
const handled = this.toolbox.selectParent_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.isFalse(collapsibleItem.isExpanded());
|
|
chai.assert.equal(this.toolbox.selectedItem_, collapsibleItem);
|
|
});
|
|
test('Selected item is not expanded -> Should get parent', function() {
|
|
const childItem = getChildItem(this.toolbox);
|
|
this.toolbox.selectedItem_ = childItem;
|
|
const handled = this.toolbox.selectParent_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.equal(this.toolbox.selectedItem_, childItem.getParent());
|
|
});
|
|
});
|
|
|
|
suite('selectNext_', function() {
|
|
test('No item is selected -> Should not handle event', function() {
|
|
this.toolbox.selectedItem_ = null;
|
|
const handled = this.toolbox.selectNext_();
|
|
chai.assert.isFalse(handled);
|
|
});
|
|
test('Next item is selectable -> Should select next item', function() {
|
|
const item = this.toolbox.contents_[0];
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectNext_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.equal(this.toolbox.selectedItem_, this.toolbox.contents_[1]);
|
|
});
|
|
test('Selected item is last item -> Should not handle event', function() {
|
|
const item = this.toolbox.contents_[this.toolbox.contents_.length - 1];
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectNext_();
|
|
chai.assert.isFalse(handled);
|
|
chai.assert.equal(this.toolbox.selectedItem_, item);
|
|
});
|
|
test('Selected item is collapsed -> Should skip over its children', function() {
|
|
const item = getCollapsibleItem(this.toolbox);
|
|
const childItem = item.flyoutItems_[0];
|
|
item.expanded_ = false;
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectNext_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.notEqual(this.toolbox.selectedItem_, childItem);
|
|
});
|
|
});
|
|
|
|
suite('selectPrevious', function() {
|
|
test('No item is selected -> Should not handle event', function() {
|
|
this.toolbox.selectedItem_ = null;
|
|
const handled = this.toolbox.selectPrevious_();
|
|
chai.assert.isFalse(handled);
|
|
});
|
|
test('Selected item is first item -> Should not handle event', function() {
|
|
const item = this.toolbox.contents_[0];
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectPrevious_();
|
|
chai.assert.isFalse(handled);
|
|
chai.assert.equal(this.toolbox.selectedItem_, item);
|
|
});
|
|
test('Previous item is selectable -> Should select previous item', function() {
|
|
const item = this.toolbox.contents_[1];
|
|
const prevItem = this.toolbox.contents_[0];
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectPrevious_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.equal(this.toolbox.selectedItem_, prevItem);
|
|
});
|
|
test('Previous item is collapsed -> Should skip over children of the previous item', function() {
|
|
const childItem = getChildItem(this.toolbox);
|
|
const parentItem = childItem.getParent();
|
|
const parentIdx = this.toolbox.contents_.indexOf(parentItem);
|
|
// Gets the item after the parent.
|
|
const item = this.toolbox.contents_[parentIdx + 1];
|
|
this.toolbox.selectedItem_ = item;
|
|
const handled = this.toolbox.selectPrevious_();
|
|
chai.assert.isTrue(handled);
|
|
chai.assert.notEqual(this.toolbox.selectedItem_, childItem);
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('setSelectedItem', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
function setupSetSelected(toolbox, oldItem, newItem) {
|
|
toolbox.selectedItem_ = oldItem;
|
|
const newItemStub = sinon.stub(newItem, 'setSelected');
|
|
toolbox.setSelectedItem(newItem);
|
|
return newItemStub;
|
|
}
|
|
|
|
test('Selected item and new item are null -> Should not update the flyout', function() {
|
|
this.selectedItem_ = null;
|
|
this.toolbox.setSelectedItem(null);
|
|
const updateFlyoutStub = sinon.stub(this.toolbox, 'updateFlyout_');
|
|
sinon.assert.notCalled(updateFlyoutStub);
|
|
});
|
|
test('New item is not selectable -> Should not update the flyout', function() {
|
|
const separator = getSeparator(this.toolbox);
|
|
this.toolbox.setSelectedItem(separator);
|
|
const updateFlyoutStub = sinon.stub(this.toolbox, 'updateFlyout_');
|
|
sinon.assert.notCalled(updateFlyoutStub);
|
|
});
|
|
test('Select an item with no children -> Should select item', function() {
|
|
const oldItem = getCollapsibleItem(this.toolbox);
|
|
const oldItemStub = sinon.stub(oldItem, 'setSelected');
|
|
const newItem = getNonCollapsibleItem(this.toolbox);
|
|
const newItemStub = setupSetSelected(this.toolbox, oldItem, newItem);
|
|
sinon.assert.calledWith(oldItemStub, false);
|
|
sinon.assert.calledWith(newItemStub, true);
|
|
});
|
|
test('Select previously selected item with no children -> Should deselect', function() {
|
|
const newItem = getNonCollapsibleItem(this.toolbox);
|
|
const newItemStub = setupSetSelected(this.toolbox, newItem, newItem);
|
|
sinon.assert.calledWith(newItemStub, false);
|
|
});
|
|
test('Select collapsible item -> Should select item', function() {
|
|
const newItem = getCollapsibleItem(this.toolbox);
|
|
const newItemStub = setupSetSelected(this.toolbox, null, newItem);
|
|
sinon.assert.calledWith(newItemStub, true);
|
|
});
|
|
test('Select previously selected collapsible item -> Should not deselect', function() {
|
|
const newItem = getCollapsibleItem(this.toolbox);
|
|
const newItemStub = setupSetSelected(this.toolbox, newItem, newItem);
|
|
sinon.assert.notCalled(newItemStub);
|
|
});
|
|
});
|
|
|
|
suite('updateFlyout_', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
|
|
function testHideFlyout(toolbox, oldItem, newItem) {
|
|
const updateFlyoutStub = sinon.stub(toolbox.flyout_, 'hide');
|
|
toolbox.updateFlyout_(oldItem, newItem);
|
|
sinon.assert.called(updateFlyoutStub);
|
|
}
|
|
|
|
test('Select previously selected item -> Should close flyout', function() {
|
|
const newItem = getNonCollapsibleItem(this.toolbox);
|
|
testHideFlyout(this.toolbox, newItem, newItem);
|
|
});
|
|
test('No new item -> Should close flyout', function() {
|
|
testHideFlyout(this.toolbox, null, null);
|
|
});
|
|
test('Old item but no new item -> Should close flyout', function() {
|
|
const oldItem = getNonCollapsibleItem(this.toolbox);
|
|
testHideFlyout(this.toolbox, oldItem, null);
|
|
});
|
|
test('Select collapsible item -> Should close flyout', function() {
|
|
const newItem = getCollapsibleItem(this.toolbox);
|
|
testHideFlyout(this.toolbox, null, newItem);
|
|
});
|
|
test('Select selectable item -> Should open flyout', function() {
|
|
const showFlyoutstub = sinon.stub(this.toolbox.flyout_, 'show');
|
|
const scrollToStartFlyout = sinon.stub(this.toolbox.flyout_, 'scrollToStart');
|
|
const newItem = getNonCollapsibleItem(this.toolbox);
|
|
this.toolbox.updateFlyout_(null, newItem);
|
|
sinon.assert.called(showFlyoutstub);
|
|
sinon.assert.called(scrollToStartFlyout);
|
|
});
|
|
});
|
|
|
|
suite('position', function() {
|
|
setup(function() {
|
|
this.toolbox = getBasicToolbox();
|
|
const metricsStub = sinon.stub(this.toolbox.workspace_, 'getMetrics');
|
|
metricsStub.returns({});
|
|
});
|
|
|
|
function checkHorizontalToolbox(toolbox) {
|
|
chai.assert.equal(toolbox.HtmlDiv.style.left, '0px', 'Check left position');
|
|
chai.assert.equal(toolbox.HtmlDiv.style.height, 'auto', 'Check height');
|
|
chai.assert.equal(toolbox.HtmlDiv.style.width, '100%', 'Check width');
|
|
chai.assert.equal(toolbox.height_, toolbox.HtmlDiv.offsetHeight, 'Check height');
|
|
}
|
|
function checkVerticalToolbox(toolbox) {
|
|
chai.assert.equal(toolbox.HtmlDiv.style.height, '100%', 'Check height');
|
|
chai.assert.equal(toolbox.width_, toolbox.HtmlDiv.offsetWidth, 'Check width');
|
|
}
|
|
test('HtmlDiv is not created -> Should not resize', function() {
|
|
const toolbox = this.toolbox;
|
|
toolbox.HtmlDiv = null;
|
|
toolbox.horizontalLayout_ = true;
|
|
toolbox.position();
|
|
chai.assert.equal(toolbox.height_, 0);
|
|
});
|
|
test('Horizontal toolbox at top -> Should anchor horizontal toolbox to top', function() {
|
|
const toolbox = this.toolbox;
|
|
toolbox.toolboxPosition = Blockly.utils.toolbox.Position.TOP;
|
|
toolbox.horizontalLayout_ = true;
|
|
toolbox.position();
|
|
checkHorizontalToolbox(toolbox);
|
|
chai.assert.equal(toolbox.HtmlDiv.style.top, '0px', 'Check top');
|
|
});
|
|
test('Horizontal toolbox at bottom -> Should anchor horizontal toolbox to bottom', function() {
|
|
const toolbox = this.toolbox;
|
|
toolbox.toolboxPosition = Blockly.utils.toolbox.Position.BOTTOM;
|
|
toolbox.horizontalLayout_ = true;
|
|
toolbox.position();
|
|
checkHorizontalToolbox(toolbox);
|
|
chai.assert.equal(toolbox.HtmlDiv.style.bottom, '0px', 'Check bottom');
|
|
});
|
|
test('Vertical toolbox at right -> Should anchor to right', function() {
|
|
const toolbox = this.toolbox;
|
|
toolbox.toolboxPosition = Blockly.utils.toolbox.Position.RIGHT;
|
|
toolbox.horizontalLayout_ = false;
|
|
toolbox.position();
|
|
chai.assert.equal(toolbox.HtmlDiv.style.right, '0px', 'Check right');
|
|
checkVerticalToolbox(toolbox);
|
|
});
|
|
test('Vertical toolbox at left -> Should anchor to left', function() {
|
|
const toolbox = this.toolbox;
|
|
toolbox.toolboxPosition = Blockly.utils.toolbox.Position.LEFT;
|
|
toolbox.horizontalLayout_ = false;
|
|
toolbox.position();
|
|
chai.assert.equal(toolbox.HtmlDiv.style.left, '0px', 'Check left');
|
|
checkVerticalToolbox(toolbox);
|
|
});
|
|
});
|
|
|
|
suite('parseMethods', function() {
|
|
setup(function() {
|
|
this.categoryToolboxJSON = getCategoryJSON();
|
|
this.simpleToolboxJSON = getSimpleJson();
|
|
});
|
|
|
|
function checkValue(actual, expected, value) {
|
|
const actualVal = actual[value];
|
|
const expectedVal = expected[value];
|
|
chai.assert.equal(actualVal.toUpperCase(), expectedVal.toUpperCase(), 'Checking value for: ' + value);
|
|
}
|
|
function checkContents(actualContents, expectedContents) {
|
|
chai.assert.equal(actualContents.length, expectedContents.length);
|
|
for (let i = 0; i < actualContents.length; i++) {
|
|
// TODO: Check the values as well as all the keys.
|
|
chai.assert.containsAllKeys(actualContents[i], Object.keys(expectedContents[i]));
|
|
}
|
|
}
|
|
function checkCategory(actual, expected) {
|
|
checkValue(actual, expected, 'kind');
|
|
checkValue(actual, expected, 'name');
|
|
chai.assert.deepEqual(actual['cssconfig'], expected['cssconfig']);
|
|
checkContents(actual.contents, expected.contents);
|
|
}
|
|
function checkCategoryToolbox(actual, expected) {
|
|
const actualContents = actual['contents'];
|
|
const expectedContents = expected['contents'];
|
|
chai.assert.equal(actualContents.length, expectedContents.length);
|
|
for (let i = 0; i < expected.length; i++) {
|
|
checkCategory(actualContents[i], expected[i]);
|
|
}
|
|
}
|
|
function checkSimpleToolbox(actual, expected) {
|
|
checkContents(actual['contents'], expected['contents']);
|
|
}
|
|
|
|
suite('parseToolbox', function() {
|
|
test('Category Toolbox: JSON', function() {
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(this.categoryToolboxJSON);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON);
|
|
});
|
|
test('Simple Toolbox: JSON', function() {
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(this.simpleToolboxJSON);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON);
|
|
});
|
|
test('Category Toolbox: xml', function() {
|
|
const toolboxXml = document.getElementById('toolbox-categories');
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(toolboxXml);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON);
|
|
});
|
|
test('Simple Toolbox: xml', function() {
|
|
const toolboxXml = document.getElementById('toolbox-simple');
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(toolboxXml);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON);
|
|
});
|
|
test('Simple Toolbox: string', function() {
|
|
let toolbox = '<xml>';
|
|
toolbox += ' <block type="controls_if"></block>';
|
|
toolbox += ' <block type="controls_whileUntil"></block>';
|
|
toolbox += '</xml>';
|
|
|
|
const toolboxJson = {
|
|
'contents': [
|
|
{
|
|
'kind': 'block',
|
|
'type': 'controls_if',
|
|
},
|
|
{
|
|
'kind': 'block',
|
|
'type': 'controls_if',
|
|
},
|
|
],
|
|
};
|
|
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(toolbox);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkSimpleToolbox(toolboxDef, toolboxJson);
|
|
});
|
|
test('Category Toolbox: string', function() {
|
|
let toolbox = '<xml>';
|
|
toolbox += ' <category name="a"></category>';
|
|
toolbox += ' <category name="b"></category>';
|
|
toolbox += '</xml>';
|
|
|
|
const toolboxJson = {
|
|
'contents': [
|
|
{
|
|
'kind': 'category',
|
|
'name': 'a',
|
|
},
|
|
{
|
|
'kind': 'category',
|
|
'name': 'b',
|
|
},
|
|
],
|
|
};
|
|
|
|
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(toolbox);
|
|
chai.assert.isNotNull(toolboxDef);
|
|
checkSimpleToolbox(toolboxDef, toolboxJson);
|
|
});
|
|
});
|
|
suite('parseFlyout', function() {
|
|
test('Array of Nodes', function() {
|
|
const xmlList = getXmlArray();
|
|
const flyoutDef = Blockly.utils.toolbox.convertFlyoutDefToJsonArray(xmlList);
|
|
checkContents(flyoutDef, this.simpleToolboxJSON['contents']);
|
|
});
|
|
test('NodeList', function() {
|
|
const nodeList = document.getElementById('toolbox-simple').childNodes;
|
|
const flyoutDef = Blockly.utils.toolbox.convertFlyoutDefToJsonArray(nodeList);
|
|
checkContents(flyoutDef, this.simpleToolboxJSON['contents']);
|
|
});
|
|
test('List of json', function() {
|
|
const jsonList = this.simpleToolboxJSON['contents'];
|
|
const flyoutDef = Blockly.utils.toolbox.convertFlyoutDefToJsonArray(jsonList);
|
|
checkContents(flyoutDef, this.simpleToolboxJSON['contents']);
|
|
});
|
|
test('Json', function() {
|
|
const flyoutDef = Blockly.utils.toolbox.convertFlyoutDefToJsonArray(this.simpleToolboxJSON);
|
|
checkContents(flyoutDef, this.simpleToolboxJSON['contents']);
|
|
});
|
|
});
|
|
});
|
|
suite('Nested Categories', function() {
|
|
setup(function() {
|
|
this.toolbox = getInjectedToolbox();
|
|
});
|
|
teardown(function() {
|
|
this.toolbox.dispose();
|
|
});
|
|
test('Child categories visible if all ancestors expanded', function() {
|
|
this.toolbox.render(getDeeplyNestedJSON());
|
|
const outerCategory = this.toolbox.contents_[0];
|
|
const middleCategory = this.toolbox.contents_[1];
|
|
const innerCategory = this.toolbox.contents_[2];
|
|
|
|
outerCategory.toggleExpanded();
|
|
middleCategory.toggleExpanded();
|
|
innerCategory.show();
|
|
|
|
chai.assert.isTrue(innerCategory.isVisible(),
|
|
'All ancestors are expanded, so category should be visible');
|
|
});
|
|
test('Child categories not visible if any ancestor not expanded', function() {
|
|
this.toolbox.render(getDeeplyNestedJSON());
|
|
const middleCategory = this.toolbox.contents_[1];
|
|
const innerCategory = this.toolbox.contents_[2];
|
|
|
|
// Don't expand the outermost category
|
|
// Even though the direct parent of inner is expanded, it shouldn't be visible
|
|
// because all ancestor categories need to be visible, not just parent
|
|
middleCategory.toggleExpanded();
|
|
innerCategory.show();
|
|
|
|
chai.assert.isFalse(innerCategory.isVisible(),
|
|
'Not all ancestors are expanded, so category should not be visible');
|
|
});
|
|
});
|
|
});
|