/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/spec/KeyBindingManager-test.js
1 012 строк
45 KB
ficristo
Move most inline jslint directives to config files
12 авг 2016, 10:22
12 авг 2016, 10:22
074382a
Код
Авторство
О чём код?
/* * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ /*global describe, beforeEach, afterEach, it, expect, spyOn, runs, waits */ /*unittests: KeyBindingManager */ define(function (require, exports, module) { 'use strict'; require("utils/Global"); // Load dependent modules var CommandManager = require("command/CommandManager"), Dialogs = require("widgets/Dialogs"), KeyBindingManager = require("command/KeyBindingManager"), KeyEvent = require("utils/KeyEvent"), SpecRunnerUtils = require("spec/SpecRunnerUtils"), Strings = require("strings"), _ = require("thirdparty/lodash"); var platform = brackets.platform; var testPath = SpecRunnerUtils.getTestPath("/spec/KeyBindingManager-test-files"); var executed, testCommandFn = function () { executed = true; }; var defaultKeyBindings = { "Ctrl-L": "edit.selectLine", "Ctrl-Alt-L": "edit.splitSelIntoLines", "Alt-Shift-Down": "edit.addCursorToNextLine", "Alt-Shift-Up": "edit.addCursorToPrevLine", "F8": "navigate.gotoFirstProblem", "Ctrl-Alt-O": "file.openFolder", "Ctrl-Alt-H": "view.toggleSidebar", "Ctrl-Shift-O": "navigate.quickOpen", "Ctrl-T": "navigate.gotoDefinition" }, macDefaultKeyBindings = { "Ctrl-L": "edit.selectLine", "Alt-Cmd-L": "edit.splitSelIntoLines", "Alt-Shift-Down": "edit.addCursorToNextLine", "Alt-Shift-Up": "edit.addCursorToPrevLine", "Cmd-'": "navigate.gotoFirstProblem", "Alt-Cmd-O": "file.openFolder", "Shift-Cmd-H": "view.toggleSidebar", "Shift-Cmd-O": "navigate.quickOpen", "Cmd-T": "navigate.gotoDefinition" }; function key(k, displayKey, explicitPlatform) { return { key : k, displayKey : displayKey || k, explicitPlatform : explicitPlatform }; } function keyBinding(k, commandID, displayKey, explicitPlatform) { var obj = key(k, displayKey, explicitPlatform); obj.commandID = commandID; return obj; } function keyMap(keyBindings) { var map = {}; keyBindings.forEach(function (k) { map[k.key] = k; }); return map; } function populateDefaultKeyMap() { var defaults = (platform === "mac") ? macDefaultKeyBindings : defaultKeyBindings, index = 0; _.forEach(defaults, function (commandID, key) { ++index; CommandManager.register("test command" + index.toString(), commandID, testCommandFn); }); } function getDefaultKeyMap() { var bindings = [], defaults = (platform === "mac") ? macDefaultKeyBindings : defaultKeyBindings, displayKey = "", explicitPlatform; _.forEach(defaults, function (commandID, key) { displayKey = KeyBindingManager._getDisplayKey(key); if (platform === "mac") { explicitPlatform = undefined; if (commandID === "edit.selectLine" || commandID === "view.toggleSidebar" || commandID === "navigate.gotoFirstProblem") { explicitPlatform = "mac"; } } bindings.push(keyBinding(key, commandID, displayKey, explicitPlatform)); }); return keyMap(bindings); } describe("KeyBindingManager", function () { beforeEach(function () { CommandManager._testReset(); KeyBindingManager._reset(); brackets.platform = "test"; }); afterEach(function () { CommandManager._testRestore(); brackets.platform = platform; }); describe("addBinding", function () { it("should require command and key binding arguments", function () { KeyBindingManager.addBinding(); expect(KeyBindingManager.getKeymap()).toEqual({}); KeyBindingManager.addBinding("test.foo"); expect(KeyBindingManager.getKeymap()).toEqual({}); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([]); }); it("should ignore invalid bindings", function () { expect(KeyBindingManager.addBinding("test.foo", "Ktrl-Shift-A")).toBeNull(); expect(KeyBindingManager.addBinding("test.foo", "Ctrl+R")).toBeNull(); expect(KeyBindingManager.getKeymap()).toEqual({}); }); it("should add single bindings to the keymap", function () { var result = KeyBindingManager.addBinding("test.foo", "Ctrl-A"), keyTest = key("Ctrl-A"); expect(result).toEqual(keyTest); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([keyTest]); result = KeyBindingManager.addBinding("test.bar", "Ctrl-B"); keyTest = key("Ctrl-B"); expect(result).toEqual(keyTest); expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([keyTest]); result = KeyBindingManager.addBinding("test.cat", "Ctrl-C", "bark"); expect(result).toBeNull(); result = KeyBindingManager.addBinding("test.dog", "Ctrl-D", "test"); keyTest = key("Ctrl-D", null, "test"); expect(result).toEqual(keyTest); expect(KeyBindingManager.getKeyBindings("test.dog")).toEqual([keyTest]); // only "test" platform bindings var expected = keyMap([ keyBinding("Ctrl-A", "test.foo"), keyBinding("Ctrl-B", "test.bar"), keyBinding("Ctrl-D", "test.dog", null, "test") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should use displayKey to override display of the shortcut", function () { KeyBindingManager.addBinding("test.foo", key("Ctrl-=", "Ctrl-+")); // only "test" platform bindings var expected = keyMap([ keyBinding("Ctrl-=", "test.foo", "Ctrl-+") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should add multiple bindings to the keymap", function () { // use a fake platform brackets.platform = "test1"; var results = KeyBindingManager.addBinding( "test.foo", [ {key: "Ctrl-A", platform: "test1"}, {key: "Ctrl-1", platform: "all"} ] ); expect(results).toEqual([ key("Ctrl-A", null, "test1"), key("Ctrl-1", null, "all") ]); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([ key("Ctrl-A", null, "test1"), key("Ctrl-1", null, "all") ]); results = KeyBindingManager.addBinding("test.bar", [{key: "Ctrl-B"}, {key: "Ctrl-2", platform: "test2"}]); expect(results).toEqual([ key("Ctrl-B") ]); expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([ key("Ctrl-B") ]); // only "test1" platform and cross-platform bindings var expected = keyMap([ keyBinding("Ctrl-A", "test.foo", null, "test1"), keyBinding("Ctrl-1", "test.foo", null, "all"), keyBinding("Ctrl-B", "test.bar") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should allow the command argument to be a string or an object", function () { var result = KeyBindingManager.addBinding("test.foo", "Ctrl-A"), keyTest = key("Ctrl-A"); expect(result).toEqual(keyTest); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([keyTest]); var commandObj = CommandManager.register("Bar", "test.bar", function () { return; }); result = KeyBindingManager.addBinding(commandObj, "Ctrl-B"); keyTest = key("Ctrl-B"); expect(result).toEqual(keyTest); expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([keyTest]); // only "test" platform bindings var expected = keyMap([ keyBinding("Ctrl-A", "test.foo"), keyBinding("Ctrl-B", "test.bar") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should not allow a generic key binding to be replaced with another generic binding", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A"); KeyBindingManager.addBinding("test.bar", "Ctrl-A"); var expected = keyMap([ keyBinding("Ctrl-A", "test.foo") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should allow a platform-specific key binding to override a generic binding", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A"); KeyBindingManager.addBinding("test.bar", "Ctrl-A", "test"); var expected = keyMap([ keyBinding("Ctrl-A", "test.bar", null, "test") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should keep a platform-specific key binding if a generic binding is added later", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A", "test"); KeyBindingManager.addBinding("test.bar", "Ctrl-A"); var expected = keyMap([ keyBinding("Ctrl-A", "test.foo", null, "test") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should allow a command to map to multiple key bindings", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A"); KeyBindingManager.addBinding("test.foo", "Ctrl-B"); // only "test1" platform and cross-platform bindings var expected = keyMap([ keyBinding("Ctrl-A", "test.foo"), keyBinding("Ctrl-B", "test.foo") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should support the Ctrl key on mac", function () { brackets.platform = "mac"; KeyBindingManager.addBinding("test.cmd", "Cmd-A", "mac"); KeyBindingManager.addBinding("test.ctrl", "Ctrl-A", "mac"); KeyBindingManager.addBinding("test.ctrlAlt", "Ctrl-Alt-A", "mac"); KeyBindingManager.addBinding("test.cmdCtrlAlt", "Cmd-Ctrl-A", "mac"); var expected = keyMap([ keyBinding("Cmd-A", "test.cmd", null, "mac"), keyBinding("Ctrl-A", "test.ctrl", null, "mac"), keyBinding("Ctrl-Alt-A", "test.ctrlAlt", null, "mac"), keyBinding("Ctrl-Cmd-A", "test.cmdCtrlAlt", null, "mac") // KeyBindingManager changes the order ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should use windows key bindings on linux", function () { var original = KeyBindingManager.useWindowsCompatibleBindings; this.after(function () { KeyBindingManager.useWindowsCompatibleBindings = original; }); KeyBindingManager.useWindowsCompatibleBindings = true; brackets.platform = "linux"; // create a windows-specific binding KeyBindingManager.addBinding("test.cmd", "Ctrl-A", "win"); var expected = keyMap([ keyBinding("Ctrl-A", "test.cmd", null, "win") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); // create a generic binding to replace the windows binding KeyBindingManager.addBinding("test.cmd", "Ctrl-B"); expected = keyMap([ keyBinding("Ctrl-B", "test.cmd", null) ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should support windows compatible bindings", function () { var original = KeyBindingManager.useWindowsCompatibleBindings; this.after(function () { KeyBindingManager.useWindowsCompatibleBindings = original; }); KeyBindingManager.useWindowsCompatibleBindings = true; brackets.platform = "linux"; // create a generic binding KeyBindingManager.addBinding("test.cmd", "Ctrl-A"); var expected = keyMap([ keyBinding("Ctrl-A", "test.cmd") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); // create a linux-only binding to replace the windows binding KeyBindingManager.addBinding("test.cmd", "Ctrl-B", "linux"); expected = keyMap([ keyBinding("Ctrl-B", "test.cmd", null, "linux") ]); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); }); describe("removeBinding", function () { it("should handle an empty keymap gracefully", function () { KeyBindingManager.removeBinding("Ctrl-A"); expect(KeyBindingManager.getKeymap()).toEqual({}); }); it("should require a key to remove", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A"); KeyBindingManager.addBinding("test.bar", "Ctrl-B"); // keymap should be unchanged var expected = keyMap([ keyBinding("Ctrl-A", "test.foo"), keyBinding("Ctrl-B", "test.bar") ]); KeyBindingManager.removeBinding(); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); it("should remove a key from the key map", function () { KeyBindingManager.addBinding("test.foo", "Ctrl-A"); KeyBindingManager.addBinding("test.foo", "Ctrl-B"); // Ctrl-A should be removed var expected = keyMap([ keyBinding("Ctrl-B", "test.foo") ]); KeyBindingManager.removeBinding("Ctrl-A"); expect(KeyBindingManager.getKeymap()).toEqual(expected); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([key("Ctrl-B")]); KeyBindingManager.removeBinding("Ctrl-B"); expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([]); }); it("should remove a key from the key map for the specified platform", function () { brackets.platform = "test1"; KeyBindingManager.addBinding("test.foo", "Ctrl-A", "test1"); // remove Ctrl-A, only for platform "test1" KeyBindingManager.removeBinding("Ctrl-A", "test1"); expect(KeyBindingManager.getKeymap()).toEqual({}); }); it("should exclude a specified platform key binding for a cross-platform command", function () { brackets.platform = "test1"; // all platforms KeyBindingManager.addBinding("test.foo", "Ctrl-B"); var expected = keyMap([ keyBinding("Ctrl-B", "test.foo") ]); // remove Ctrl-B, only for platform "test2" KeyBindingManager.removeBinding("Ctrl-B", "test2"); expect(KeyBindingManager.getKeymap()).toEqual(expected); }); }); describe("Load User Key Map", function () { it("should show an error when loading an image file as a user key map file", function () { runs(function () { var imageTestFilesPath = SpecRunnerUtils.getTestPath("/spec/test-image-files"); KeyBindingManager._setUserKeyMapFilePath(imageTestFilesPath + "/eye.jpg"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { expect(message).toEqual(Strings.ERROR_LOADING_KEYMAP); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when loading a corrupted key map file", function () { runs(function () { KeyBindingManager._setUserKeyMapFilePath(testPath + "/invalid.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { expect(message).toEqual(Strings.ERROR_KEYMAP_CORRUPT); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when loading a key map file with only whitespaces", function () { runs(function () { KeyBindingManager._setUserKeyMapFilePath(testPath + "/whitespace.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { expect(message).toEqual(Strings.ERROR_KEYMAP_CORRUPT); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should NOT show any error when loading a user key map file with an empty object", function () { runs(function () { KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/empty.json"); KeyBindingManager._loadUserKeyMap(); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).not.toHaveBeenCalled(); }); }); it("should NOT show any error when loading a zero-byte user key map file", function () { runs(function () { KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/blank.json"); KeyBindingManager._loadUserKeyMap(); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).not.toHaveBeenCalled(); }); }); it("should show an error when attempting to reassign a special command", function () { runs(function () { CommandManager.register("test copy command", "edit.copy", testCommandFn); KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/reassignCopy.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_RESTRICTED_COMMANDS.replace("{0}", ""); expect(message).toMatch(msgPrefix); expect(message).toMatch("edit.copy"); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when attempting to reassign a restricted shortcut (either bind to a special command or a mac system shortcut)", function () { var testFilePath = (platform === "mac") ? (testPath + "/macRestrictedShortcut.json") : (testPath + "/restrictedShortcut.json"); runs(function () { brackets.platform = platform; populateDefaultKeyMap(); KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testFilePath); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_RESTRICTED_SHORTCUTS.replace("{0}", ""); expect(message).toMatch(msgPrefix); if (platform === "mac") { expect(message).toMatch("cmd-z"); expect(message).toMatch("Cmd-m"); expect(message).toMatch("cmd-h"); } else { expect(message).toMatch("ctrl-z"); } return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when attempting to assign multiple shortcuts to the same command", function () { runs(function () { brackets.platform = platform; populateDefaultKeyMap(); KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/multipleShortcuts.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_MULTIPLE_SHORTCUTS.replace("{0}", ""); expect(message).toMatch(msgPrefix); expect(message).toMatch("file.openFolder"); expect(message).toMatch("view.toggleSidebar"); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when attempting to set duplicate shortcuts", function () { runs(function () { brackets.platform = platform; populateDefaultKeyMap(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/duplicateShortcuts.json"); KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_DUPLICATE_SHORTCUTS.replace("{0}", ""); expect(message).toMatch(msgPrefix); expect(message).toMatch("Ctrl-2"); expect(message).toMatch("Alt-Ctrl-4"); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when parsing invalid shortcuts", function () { runs(function () { brackets.platform = platform; populateDefaultKeyMap(); KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/invalidKeys.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_INVALID_SHORTCUTS.replace("{0}", ""); expect(message).toMatch(msgPrefix); expect(message).toMatch("command-2"); expect(message).toMatch("Option-Cmd-Backspace"); expect(message).toMatch("ctrl-kk"); expect(message).toMatch("cmd-Del"); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should show an error when attempting to set shortcuts to non-existent commands", function () { runs(function () { // The command map has to be empty for this test case. So we are intentionally NOT calling // populateDefaultKeyMap() before loading the user key map. KeyBindingManager._initCommandAndKeyMaps(); KeyBindingManager._setUserKeyMapFilePath(testPath + "/keymap.json"); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { var msgPrefix = Strings.ERROR_NONEXISTENT_COMMANDS.replace("{0}", ""); expect(message).toMatch(msgPrefix); expect(message).toMatch("file.openFolder"); expect(message).toMatch("view.toggleSidebar"); return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { expect(Dialogs.showModalDialog).toHaveBeenCalled(); }); }); it("should update key map with the user specified key bindings", function () { var testFilePath = (platform === "mac") ? (testPath + "/macKeymap.json") : (testPath + "/keymap.json"); runs(function () { brackets.platform = platform; var defKeyMap = getDefaultKeyMap(); populateDefaultKeyMap(); KeyBindingManager._initCommandAndKeyMaps(); expect(KeyBindingManager.getKeymap()).toEqual(defKeyMap); KeyBindingManager._setUserKeyMapFilePath(testFilePath); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { var keymap = KeyBindingManager.getKeymap(), reassignedKey1 = (platform === "mac") ? "Alt-Cmd-Backspace" : "Ctrl-Alt-Backspace", reassignedKey2 = (platform === "mac") ? "Cmd-T" : "Ctrl-T"; expect(Dialogs.showModalDialog).not.toHaveBeenCalled(); expect(keymap["Ctrl-2"].commandID).toEqual("file.openFolder"); expect(keymap["Alt-Cmd-O"]).toBeFalsy(); expect(keymap["Alt-Ctrl-O"]).toBeFalsy(); expect(keymap[reassignedKey1].commandID).toEqual("view.toggleSidebar"); expect(keymap["Shift-Cmd-H"]).toBeFalsy(); expect(keymap["Alt-Ctrl-H"]).toBeFalsy(); expect(keymap["Ctrl-L"].commandID).toEqual("navigate.gotoDefinition"); expect(keymap[reassignedKey2]).toBeFalsy(); expect(keymap["Alt-Cmd-L"]).toBeFalsy(); expect(keymap["Alt-Ctrl-L"]).toBeFalsy(); }); }); it("should restore original key bindings when the user key map is updated", function () { var testFilePath1 = (platform === "mac") ? (testPath + "/macKeymap.json") : (testPath + "/keymap.json"), testFilePath2 = (platform === "mac") ? (testPath + "/macKeymap1.json") : (testPath + "/keymap1.json"); runs(function () { brackets.platform = platform; var defKeyMap = getDefaultKeyMap(); populateDefaultKeyMap(); KeyBindingManager._initCommandAndKeyMaps(); expect(KeyBindingManager.getKeymap()).toEqual(defKeyMap); KeyBindingManager._setUserKeyMapFilePath(testFilePath1); KeyBindingManager._loadUserKeyMap(); waits(300); // Loading a different key map file to simulate the user updating an existing key map. KeyBindingManager._setUserKeyMapFilePath(testFilePath2); KeyBindingManager._loadUserKeyMap(); waits(300); spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) { return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } }; }); }); runs(function () { var keymap = KeyBindingManager.getKeymap(), reassignedKey1 = (platform === "mac") ? "Alt-Cmd-Backspace" : "Ctrl-Alt-Backspace", reassignedKey2 = (platform === "mac") ? "Alt-Cmd-O" : "Ctrl-Alt-O", reassignedKey3 = (platform === "mac") ? "Cmd-T" : "Ctrl-T"; expect(Dialogs.showModalDialog).not.toHaveBeenCalled(); expect(keymap["Ctrl-2"].commandID).toEqual("view.toggleSidebar"); // Previous user key binding to "view.toggleSidebar" is gone. expect(keymap[reassignedKey1]).toBeFalsy(); // Default key binding for "file.openFolder" is restored. expect(keymap[reassignedKey2].commandID).toEqual("file.openFolder"); expect(keymap["Ctrl-L"].commandID).toEqual("navigate.gotoDefinition"); expect(keymap[reassignedKey3]).toBeFalsy(); }); }); }); describe("handleKey", function () { it("should execute a command", function () { var fooCalled = false; CommandManager.register("Foo", "test.foo", function () { fooCalled = true; }); KeyBindingManager.addBinding("test.foo", "Ctrl-A"); expect(fooCalled).toBe(false); KeyBindingManager._handleKey("Ctrl-A"); expect(fooCalled).toBe(true); }); }); describe("handle AltGr key", function () { var commandCalled, ctrlAlt1Event, ctrlAltEvents; var ctrlEvent = { ctrlKey: true, keyIdentifier: "Control", keyCode: KeyEvent.DOM_VK_CONTROL, immediatePropagationStopped: false, propagationStopped: false, defaultPrevented: false, stopImmediatePropagation: function () { this.immediatePropagationStopped = true; }, stopPropagation: function () { this.propagationStopped = true; }, preventDefault: function () { this.defaultPrevented = true; } }; function makeCtrlAltKeyEvents() { var altGrEvents = [], altEvent = _.cloneDeep(ctrlEvent); altEvent.keyIdentifier = "Alt"; altEvent.altKey = true; altEvent.keyCode = KeyEvent.DOM_VK_ALT; altGrEvents.push(_.cloneDeep(ctrlEvent)); altGrEvents.push(altEvent); return altGrEvents; } function makeCtrlAlt1KeyEvent() { return { ctrlKey: true, altKey: true, keyCode: "1".charCodeAt(0), immediatePropagationStopped: false, propagationStopped: false, defaultPrevented: false, stopImmediatePropagation: function () { this.immediatePropagationStopped = true; }, stopPropagation: function () { this.propagationStopped = true; }, preventDefault: function () { this.defaultPrevented = true; } }; } beforeEach(function () { commandCalled = false; ctrlAlt1Event = makeCtrlAlt1KeyEvent(); ctrlAltEvents = makeCtrlAltKeyEvents(); CommandManager.register("FakeUnitTestCommand", "unittest.fakeCommand", function () { commandCalled = true; }); KeyBindingManager.addBinding("unittest.fakeCommand", "Ctrl-Alt-1"); // Modify platform to "win" since right Alt key detection is done only on Windows. brackets.platform = "win"; }); afterEach(function () { // Restore the platform. brackets.platform = "test"; }); it("should block command execution if right Alt key is pressed", function () { // Simulate a right Alt key down with the specific sequence of keydown events. ctrlAltEvents.forEach(function (e) { e.timeStamp = new Date(); KeyBindingManager._handleKeyEvent(e); }); // Simulate the command shortcut KeyBindingManager._handleKeyEvent(ctrlAlt1Event); expect(commandCalled).toBe(false); // In this case, the event should not have been stopped, because KBM didn't handle it. expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false); expect(ctrlAlt1Event.propagationStopped).toBe(false); expect(ctrlAlt1Event.defaultPrevented).toBe(false); // Now explicitly remove the keyup event listener created by _detectAltGrKeyDown function. KeyBindingManager._onCtrlUp(ctrlEvent); }); it("should block command execution when right Alt key is pressed following a Ctrl shortcut execution", function () { var ctrlEvent1 = _.cloneDeep(ctrlEvent); // Simulate holding down Ctrl key and execute a Ctrl shortcut in native shell code // No need to call the actual Ctrl shortcut since it is not handled in KBM anyway. KeyBindingManager._handleKeyEvent(ctrlEvent1); ctrlEvent1.repeat = true; KeyBindingManager._handleKeyEvent(ctrlEvent1); KeyBindingManager._handleKeyEvent(ctrlEvent1); // Simulate a right Alt key down with the specific sequence of keydown events. ctrlAltEvents.forEach(function (e) { e.timeStamp = new Date(); e.repeat = false; KeyBindingManager._handleKeyEvent(e); }); // Simulate the command shortcut KeyBindingManager._handleKeyEvent(ctrlAlt1Event); expect(commandCalled).toBe(false); // In this case, the event should not have been stopped, because KBM didn't handle it. expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false); expect(ctrlAlt1Event.propagationStopped).toBe(false); expect(ctrlAlt1Event.defaultPrevented).toBe(false); // Now explicitly remove the keyup event listener created by _detectAltGrKeyDown function. KeyBindingManager._onCtrlUp(ctrlEvent); }); it("should not block command execution if interval between Ctrl & Alt events are more than 30 ms.", function () { var lastTS; // Simulate a Ctrl-Alt keys down with the specific sequence of keydown events. ctrlAltEvents.forEach(function (e) { if (!lastTS) { e.timeStamp = new Date(); lastTS = e.timeStamp; } else { e.timeStamp = lastTS + 50; } KeyBindingManager._handleKeyEvent(e); }); // Simulate the command shortcut KeyBindingManager._handleKeyEvent(ctrlAlt1Event); expect(commandCalled).toBe(true); // In this case, the event should have been stopped (but not immediately) because // KBM handled it. expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false); expect(ctrlAlt1Event.propagationStopped).toBe(true); expect(ctrlAlt1Event.defaultPrevented).toBe(true); }); it("should not block command execution when the right Alt key is not used", function () { // Simulate the command shortcut KeyBindingManager._handleKeyEvent(ctrlAlt1Event); expect(commandCalled).toBe(true); // In this case, the event should have been stopped (but not immediately) because // KBM handled it. expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false); expect(ctrlAlt1Event.propagationStopped).toBe(true); expect(ctrlAlt1Event.defaultPrevented).toBe(true); }); }); describe("global hooks", function () { var commandCalled, hook1Called, hook2Called, ctrlAEvent; function keydownHook1(event) { hook1Called = true; return true; } function keydownHook2(event) { hook2Called = true; return true; } function makeKeyEvent() { // We don't create a real native event object here--just a fake // object with enough info for the key translation to work // properly--since our mock hooks don't actually look at it // anyway. return { ctrlKey: true, keyCode: "A".charCodeAt(0), immediatePropagationStopped: false, propagationStopped: false, defaultPrevented: false, stopImmediatePropagation: function () { this.immediatePropagationStopped = true; }, stopPropagation: function () { this.propagationStopped = true; }, preventDefault: function () { this.defaultPrevented = true; } }; } beforeEach(function () { commandCalled = false; hook1Called = false; hook2Called = false; ctrlAEvent = makeKeyEvent(); CommandManager.register("FakeUnitTestCommand", "unittest.fakeCommand", function () { commandCalled = true; }); KeyBindingManager.addBinding("unittest.fakeCommand", "Ctrl-A"); }); it("should block command execution if a global hook is added that prevents it", function () { KeyBindingManager.addGlobalKeydownHook(keydownHook1); KeyBindingManager._handleKeyEvent(ctrlAEvent); expect(hook1Called).toBe(true); expect(commandCalled).toBe(false); // In this case, the event should not have been stopped, because our hook didn't stop it // and KBM didn't handle it. expect(ctrlAEvent.immediatePropagationStopped).toBe(false); expect(ctrlAEvent.propagationStopped).toBe(false); expect(ctrlAEvent.defaultPrevented).toBe(false); }); it("should not block command execution if a global hook is added then removed", function () { KeyBindingManager.addGlobalKeydownHook(keydownHook1); KeyBindingManager.removeGlobalKeydownHook(keydownHook1); KeyBindingManager._handleKeyEvent(ctrlAEvent); expect(hook1Called).toBe(false); expect(commandCalled).toBe(true); // In this case, the event should have been stopped (but not immediately) because // KBM handled it. expect(ctrlAEvent.immediatePropagationStopped).toBe(false); expect(ctrlAEvent.propagationStopped).toBe(true); expect(ctrlAEvent.defaultPrevented).toBe(true); }); it("should call the most recently added hook first", function () { KeyBindingManager.addGlobalKeydownHook(keydownHook1); KeyBindingManager.addGlobalKeydownHook(keydownHook2); KeyBindingManager._handleKeyEvent(ctrlAEvent); expect(hook2Called).toBe(true); expect(hook1Called).toBe(false); expect(commandCalled).toBe(false); // In this case, the event should not have been stopped, because our hook didn't stop it // and KBM didn't handle it. expect(ctrlAEvent.immediatePropagationStopped).toBe(false); expect(ctrlAEvent.propagationStopped).toBe(false); expect(ctrlAEvent.defaultPrevented).toBe(false); }); }); }); });