|
7 | 7 | import {callbackFactory} from '../../build/src/core/contextmenu.js'; |
8 | 8 | import * as xmlUtils from '../../build/src/core/utils/xml.js'; |
9 | 9 | import {assert} from '../../node_modules/chai/index.js'; |
| 10 | +import { |
| 11 | + defineRowBlock, |
| 12 | + defineStackBlock, |
| 13 | +} from './test_helpers/block_definitions.js'; |
10 | 14 | import { |
11 | 15 | sharedTestSetup, |
12 | 16 | sharedTestTeardown, |
@@ -91,4 +95,88 @@ suite('Context Menu', function () { |
91 | 95 | assert.isNull(Blockly.ContextMenu.getMenu()); |
92 | 96 | }); |
93 | 97 | }); |
| 98 | + |
| 99 | + suite('Block showContextMenu', function () { |
| 100 | + setup(function () { |
| 101 | + defineRowBlock(); |
| 102 | + defineStackBlock(); |
| 103 | + Blockly.ContextMenuRegistry.registry.reset(); |
| 104 | + Blockly.ContextMenuItems.registerDefaultOptions(); |
| 105 | + this.pointerEvent = new PointerEvent('pointerdown'); |
| 106 | + }); |
| 107 | + |
| 108 | + teardown(function () { |
| 109 | + if (Blockly.ContextMenu.getMenu()) { |
| 110 | + Blockly.ContextMenu.hide(); |
| 111 | + } |
| 112 | + }); |
| 113 | + |
| 114 | + /** |
| 115 | + * Initializes and renders the given blocks. |
| 116 | + * @param {...Blockly.BlockSvg} blocks The blocks to initialize. |
| 117 | + */ |
| 118 | + function initAndRender(...blocks) { |
| 119 | + for (const block of blocks) { |
| 120 | + block.initSvg(); |
| 121 | + block.render(); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Asserts that the given block's context menu is shown and it has focus. |
| 127 | + * @param {!Blockly.BlockSvg} block The block that should own the menu. |
| 128 | + */ |
| 129 | + function assertBlockContextMenu(block) { |
| 130 | + assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block); |
| 131 | + assert.instanceOf( |
| 132 | + Blockly.ContextMenu.getMenu(), |
| 133 | + Blockly.Menu, |
| 134 | + 'Context menu should be shown', |
| 135 | + ); |
| 136 | + assert.strictEqual(Blockly.ContextMenu.getCurrentBlock(), block); |
| 137 | + } |
| 138 | + |
| 139 | + test('value shadow forwards context menu to parent and focuses parent', function () { |
| 140 | + const parent = this.workspace.newBlock('row_block'); |
| 141 | + parent.getInput('INPUT').connection.setShadowState({type: 'row_block'}); |
| 142 | + const shadow = parent.getInput('INPUT').connection.targetBlock(); |
| 143 | + assert.isTrue(shadow.isShadow()); |
| 144 | + initAndRender(parent, shadow); |
| 145 | + |
| 146 | + Blockly.getFocusManager().focusNode(shadow); |
| 147 | + shadow.showContextMenu(this.pointerEvent); |
| 148 | + |
| 149 | + assertBlockContextMenu(parent); |
| 150 | + }); |
| 151 | + |
| 152 | + test('nested shadows forward to the first non-shadow ancestor', function () { |
| 153 | + const parent = this.workspace.newBlock('row_block'); |
| 154 | + parent.getInput('INPUT').connection.setShadowState({type: 'row_block'}); |
| 155 | + const middleShadow = parent.getInput('INPUT').connection.targetBlock(); |
| 156 | + middleShadow |
| 157 | + .getInput('INPUT') |
| 158 | + .connection.setShadowState({type: 'row_block'}); |
| 159 | + const childShadow = middleShadow |
| 160 | + .getInput('INPUT') |
| 161 | + .connection.targetBlock(); |
| 162 | + assert.isTrue(middleShadow.isShadow()); |
| 163 | + assert.isTrue(childShadow.isShadow()); |
| 164 | + initAndRender(parent, middleShadow, childShadow); |
| 165 | + |
| 166 | + Blockly.getFocusManager().focusNode(childShadow); |
| 167 | + childShadow.showContextMenu(this.pointerEvent); |
| 168 | + |
| 169 | + assertBlockContextMenu(parent); |
| 170 | + }); |
| 171 | + |
| 172 | + test('non-shadow block shows its own context menu', function () { |
| 173 | + const block = this.workspace.newBlock('stack_block'); |
| 174 | + initAndRender(block); |
| 175 | + Blockly.getFocusManager().focusNode(block); |
| 176 | + |
| 177 | + block.showContextMenu(this.pointerEvent); |
| 178 | + |
| 179 | + assertBlockContextMenu(block); |
| 180 | + }); |
| 181 | + }); |
94 | 182 | }); |
0 commit comments