diff --git a/src/shortcut_formatting.ts b/src/shortcut_formatting.ts index eb18292c..1c21f40e 100644 --- a/src/shortcut_formatting.ts +++ b/src/shortcut_formatting.ts @@ -22,7 +22,7 @@ export function getMenuItem(labelText: string, action: string): HTMLElement { label.textContent = labelText; const shortcut = document.createElement('span'); shortcut.className = 'blocklyShortcut'; - shortcut.textContent = getShortActionShortcut(action); + shortcut.textContent = ` ${getShortActionShortcut(action)}`; container.appendChild(label); container.appendChild(shortcut); return container; diff --git a/test/webdriverio/index.ts b/test/webdriverio/index.ts index 63ef40bc..4560ef2a 100644 --- a/test/webdriverio/index.ts +++ b/test/webdriverio/index.ts @@ -82,8 +82,8 @@ function createWorkspace(): Blockly.WorkspaceSvg { KeyboardNavigation.registerKeyboardNavigationStyles(); const workspace = Blockly.inject(blocklyDiv, injectOptions); - new KeyboardNavigation(workspace); Blockly.ContextMenuItems.registerCommentOptions(); + new KeyboardNavigation(workspace); // Disable blocks that aren't inside the setup or draw loops. workspace.addChangeListener(Blockly.Events.disableOrphans); diff --git a/test/webdriverio/test/actions_test.ts b/test/webdriverio/test/actions_test.ts index be5a3ca7..cf33e322 100644 --- a/test/webdriverio/test/actions_test.ts +++ b/test/webdriverio/test/actions_test.ts @@ -15,6 +15,7 @@ import { testFileLocations, testSetup, keyRight, + contextMenuItems, } from './test_setup.js'; suite('Menus test', function () { @@ -27,20 +28,47 @@ suite('Menus test', function () { await this.browser.pause(PAUSE_TIME); }); - test('Menu action opens menu', async function () { + test('Menu on block', async function () { // Navigate to draw_circle_1. await tabNavigateToWorkspace(this.browser); await focusOnBlock(this.browser, 'draw_circle_1'); await this.browser.pause(PAUSE_TIME); await this.browser.keys([Key.Ctrl, Key.Return]); await this.browser.pause(PAUSE_TIME); - chai.assert.isTrue( - await contextMenuExists(this.browser, 'Collapse Block'), - 'The menu should be openable on a block', + + chai.assert.deepEqual( + process.platform === 'darwin' + ? [ + {'text': 'Duplicate D'}, + {'text': 'Add Comment'}, + {'text': 'External Inputs'}, + {'text': 'Collapse Block'}, + {'text': 'Disable Block'}, + {'text': 'Delete 2 Blocks Delete'}, + {'text': 'Move Block M'}, + {'text': 'Edit Block contents Right'}, + {'text': 'Cut ⌘ X'}, + {'text': 'Copy ⌘ C'}, + {'disabled': true, 'text': 'Paste ⌘ V'}, + ] + : [ + {'text': 'Duplicate D'}, + {'text': 'Add Comment'}, + {'text': 'External Inputs'}, + {'text': 'Collapse Block'}, + {'text': 'Disable Block'}, + {'text': 'Delete 2 Blocks Delete'}, + {'text': 'Move Block M'}, + {'text': 'Edit Block contents Right'}, + {'text': 'Cut Ctrl + X'}, + {'text': 'Copy Ctrl + C'}, + {'disabled': true, 'text': 'Paste Ctrl + V'}, + ], + await contextMenuItems(this.browser), ); }); - test('Menu action returns true in the toolbox', async function () { + test('Menu on block in the toolbox', async function () { // Navigate to draw_circle_1. await tabNavigateToWorkspace(this.browser); await focusOnBlock(this.browser, 'draw_circle_1'); @@ -51,13 +79,60 @@ suite('Menus test', function () { await this.browser.keys([Key.Ctrl, Key.Return]); await this.browser.pause(PAUSE_TIME); - chai.assert.isTrue( - await contextMenuExists(this.browser, 'Help'), - 'The menu should be openable on a block in the toolbox', + chai.assert.deepEqual( + process.platform === 'darwin' + ? [ + {'text': 'Help'}, + {'disabled': true, 'text': 'Move Block M'}, + {'disabled': true, 'text': 'Cut ⌘ X'}, + {'text': 'Copy ⌘ C'}, + {'disabled': true, 'text': 'Paste ⌘ V'}, + ] + : [ + {'text': 'Help'}, + {'disabled': true, 'text': 'Move Block M'}, + {'disabled': true, 'text': 'Cut Ctrl + X'}, + {'text': 'Copy Ctrl + C'}, + {'disabled': true, 'text': 'Paste Ctrl + V'}, + ], + await contextMenuItems(this.browser), + ); + }); + + test('Menu on workspace', async function () { + // Navigate to draw_circle_1. + await tabNavigateToWorkspace(this.browser); + await this.browser.keys('w'); + await this.browser.keys([Key.Ctrl, Key.Return]); + await this.browser.pause(PAUSE_TIME); + + chai.assert.deepEqual( + process.platform === 'darwin' + ? [ + {'disabled': true, 'text': 'Undo'}, + {'disabled': true, 'text': 'Redo'}, + {'text': 'Clean up Blocks'}, + {'text': 'Collapse Blocks'}, + {'disabled': true, 'text': 'Expand Blocks'}, + {'text': 'Delete 4 Blocks'}, + {'text': 'Add Comment'}, + {'disabled': true, 'text': 'Paste ⌘ V'}, + ] + : [ + {'disabled': true, 'text': 'Undo'}, + {'disabled': true, 'text': 'Redo'}, + {'text': 'Clean up Blocks'}, + {'text': 'Collapse Blocks'}, + {'disabled': true, 'text': 'Expand Blocks'}, + {'text': 'Delete 4 Blocks'}, + {'text': 'Add Comment'}, + {'disabled': true, 'text': 'Paste Ctrl + V'}, + ], + await contextMenuItems(this.browser), ); }); - test('Menu action returns false during drag', async function () { + test('Menu on block during drag is not shown', async function () { // Navigate to draw_circle_1. await tabNavigateToWorkspace(this.browser); await focusOnBlock(this.browser, 'draw_circle_1'); diff --git a/test/webdriverio/test/test_setup.ts b/test/webdriverio/test/test_setup.ts index 213f0bf5..4f733273 100644 --- a/test/webdriverio/test/test_setup.ts +++ b/test/webdriverio/test/test_setup.ts @@ -589,8 +589,10 @@ export async function checkActionPrecondition( /** * Wait for the specified context menu item to exist. * + * Does not check the shortcut. + * * @param browser The active WebdriverIO Browser object. - * @param itemText The display text of the context menu item to click. + * @param itemText The display text of the context menu item without shortcut. * @param reverse Whether to check for non-existence instead. * @return A Promise that resolves when the actions are completed. */ @@ -599,10 +601,38 @@ export async function contextMenuExists( itemText: string, reverse = false, ): Promise { - const item = await browser.$(`div=${itemText}`); + // XPath so as not to care if there's a shortcut which adds DOM structure. + const item = await browser.$( + `//div[contains(@class, "blocklyMenuItem")]//*[text()="${itemText}"]`, + ); return await item.waitForExist({timeout: 200, reverse: reverse}); } +/** + * Wait for the context menu and return a representation of its contents. + * + * The text field includes the shortcut if present. + * + * @param browser The active WebdriverIO Browser object. + * @returns The context menu items. + */ +export async function contextMenuItems(browser: WebdriverIO.Browser): Promise< + Array<{ + text: string; + disabled?: true; + }> +> { + await browser.$('.blocklyContextMenu').waitForExist(); + const items = await browser + .$$('.blocklyContextMenu .blocklyMenuItem') + .map(async (item) => { + const text = await item.getComputedLabel(); + const disabled = (await item.getAttribute('aria-disabled')) === 'true'; + return disabled ? {text, disabled} : {text}; + }); + return items; +} + /** * Find a clickable element on the block and click it. * We can't always use the block's SVG root because clicking will always happen diff --git a/test/webdriverio/test/workspace_comment_test.ts b/test/webdriverio/test/workspace_comment_test.ts index 827aac31..82d6fe12 100644 --- a/test/webdriverio/test/workspace_comment_test.ts +++ b/test/webdriverio/test/workspace_comment_test.ts @@ -19,6 +19,7 @@ import { keyRight, keyDown, keyUp, + contextMenuItems, } from './test_setup.js'; import {Key} from 'webdriverio'; @@ -175,17 +176,25 @@ suite('Workspace comment navigation', function () { test('Action menu can be displayed for a workspace comment', async function () { await focusOnWorkspaceComment(this.browser, this.commentId1); await sendKeyAndWait(this.browser, [Key.Ctrl, Key.Return]); - chai.assert.isTrue( - await contextMenuExists(this.browser, 'Duplicate Comment'), - 'The menu should be openable on a workspace comment', - ); - chai.assert.isTrue( - await contextMenuExists(this.browser, 'Remove Comment'), - 'The menu should be openable on a workspace comment', - ); - chai.assert.isTrue( - await contextMenuExists(this.browser, 'Move CommentM'), - 'The menu should be openable on a workspace comment', + chai.assert.deepEqual( + process.platform === 'darwin' + ? [ + {'text': 'Duplicate Comment D'}, + {'text': 'Remove Comment'}, + {'text': 'Move Comment M'}, + {'text': 'Cut ⌘ X'}, + {'text': 'Copy ⌘ C'}, + {'disabled': true, 'text': 'Paste ⌘ V'}, + ] + : [ + {'text': 'Duplicate Comment D'}, + {'text': 'Remove Comment'}, + {'text': 'Move Comment M'}, + {'text': 'Cut Ctrl + X'}, + {'text': 'Copy Ctrl + C'}, + {'disabled': true, 'text': 'Paste Ctrl + V'}, + ], + await contextMenuItems(this.browser), ); });