Files
blockly/tests/mocha/toolbox_test.js
dependabot[bot] bfb5b1dd49 chore(deps): Bump chai from 4.3.10 to 5.1.1 (#8092)
* chore(deps): Bump chai from 4.3.10 to 5.1.1

  Bumps [chai](https://github.com/chaijs/chai) from 4.3.10 to 5.1.1.
  - [Release notes](https://github.com/chaijs/chai/releases)
  - [Changelog](https://github.com/chaijs/chai/blob/main/History.md)
  - [Commits](https://github.com/chaijs/chai/compare/v4.3.10...v5.1.1)

  ---
  updated-dependencies:
  - dependency-name: chai
    dependency-type: direct:development
    update-type: version-update:semver-major
  ...

  Signed-off-by: dependabot[bot] <support@github.com>

* fix(tests): Migrate all usage of chai to ESM (#8216)

* fix(tests): Migrate node tests from CJS to ESM

  This allows us to import (rather than require) chai, fixing failures
  caused by that package dropping suppport for CJS in chai v5.0.0.

* fix(tests): Have mocha tests directly import chai

  Previously they relied on obtaining it from the global scope, but it's
  better if imports are explicit.

* fix(tests): Remove broken load of chai as script

  Chai v5.0.0 no longer supports being loaded as a script, so this did
  nothing but emit an syntax error message on the console.

* fix(tests): Migrate browser tests from CJS to ESM

  This allows us to import (rather than require) chai, fixing failures
  caused by chai no longer supporting CJS.

* chore(tests): format

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Christopher Allen <cpcallen+git@google.com>
2024-06-17 16:48:21 +01:00

763 lines
27 KiB
JavaScript

/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {assert} from '../../node_modules/chai/chai.js';
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 () {
assert.isDefined(this.toolbox.HtmlDiv);
});
test('Init called -> HtmlDiv is inserted before parent node', function () {
const toolboxDiv = Blockly.common.getMainWorkspace().getInjectionDiv()
.childNodes[0];
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();
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': []},
],
});
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',
},
];
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];
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);
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>',
},
],
},
],
};
assert.doesNotThrow(() => {
this.toolbox.render(jsonDef);
});
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_();
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_();
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_();
assert.isTrue(handled);
assert.isTrue(collapsibleItem.isExpanded());
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_();
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_();
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_();
assert.isTrue(handled);
assert.isFalse(collapsibleItem.isExpanded());
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_();
assert.isTrue(handled);
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_();
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_();
assert.isTrue(handled);
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_();
assert.isFalse(handled);
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_();
assert.isTrue(handled);
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_();
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_();
assert.isFalse(handled);
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_();
assert.isTrue(handled);
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_();
assert.isTrue(handled);
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) {
assert.equal(toolbox.HtmlDiv.style.left, '0px', 'Check left position');
assert.equal(toolbox.HtmlDiv.style.height, 'auto', 'Check height');
assert.equal(toolbox.HtmlDiv.style.width, '100%', 'Check width');
assert.equal(
toolbox.height_,
toolbox.HtmlDiv.offsetHeight,
'Check height',
);
}
function checkVerticalToolbox(toolbox) {
assert.equal(toolbox.HtmlDiv.style.height, '100%', 'Check height');
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();
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);
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);
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();
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();
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];
assert.equal(
actualVal.toUpperCase(),
expectedVal.toUpperCase(),
'Checking value for: ' + value,
);
}
function checkContents(actualContents, expectedContents) {
assert.equal(actualContents.length, expectedContents.length);
for (let i = 0; i < actualContents.length; i++) {
// TODO: Check the values as well as all the keys.
assert.containsAllKeys(
actualContents[i],
Object.keys(expectedContents[i]),
);
}
}
function checkCategory(actual, expected) {
checkValue(actual, expected, 'kind');
checkValue(actual, expected, 'name');
assert.deepEqual(actual['cssconfig'], expected['cssconfig']);
checkContents(actual.contents, expected.contents);
}
function checkCategoryToolbox(actual, expected) {
const actualContents = actual['contents'];
const expectedContents = expected['contents'];
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,
);
assert.isNotNull(toolboxDef);
checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON);
});
test('Simple Toolbox: JSON', function () {
const toolboxDef = Blockly.utils.toolbox.convertToolboxDefToJson(
this.simpleToolboxJSON,
);
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);
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);
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);
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);
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();
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();
assert.isFalse(
innerCategory.isVisible(),
'Not all ancestors are expanded, so category should not be visible',
);
});
});
});