mirror of
https://github.com/google/blockly.git
synced 2026-01-09 10:00:09 +01:00
Keyboard shortcuts (#4421)
* Adds shortcut registry and removes action and key map (#4398) * Adds Shortcut tests and refactored navigation tests (#4412) * Adds shortcut items (#4408) * Add shortcuts for navigation (#4409) * Add final keyboard shortcut cleanup (#4413)
This commit is contained in:
@@ -81,7 +81,7 @@
|
||||
<script src="input_test.js"></script>
|
||||
<script src="insertion_marker_test.js"></script>
|
||||
<script src="json_test.js"></script>
|
||||
<script src="key_map_test.js"></script>
|
||||
<script src="shortcut_registry_test.js"></script>
|
||||
<script src="keydown_test.js"></script>
|
||||
<script src="logic_ternary_test.js"></script>
|
||||
<script src="metrics_test.js"></script>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Key Map Tests', function() {
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
test('Test adding a new action to key map', function() {
|
||||
var newAction = new Blockly.Action('test_action', 'test', function(){
|
||||
return "test";
|
||||
});
|
||||
Blockly.user.keyMap.setActionForKey('65', newAction);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_['65'].name, 'test_action');
|
||||
});
|
||||
|
||||
test('Test giving an old action a new key', function() {
|
||||
Blockly.user.keyMap.setActionForKey(Blockly.utils.KeyCodes.F,
|
||||
Blockly.navigation.ACTION_PREVIOUS);
|
||||
chai.assert.isUndefined(Blockly.user.keyMap.map_[Blockly.utils.KeyCodes.W]);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_[Blockly.utils.KeyCodes.F],
|
||||
Blockly.navigation.ACTION_PREVIOUS);
|
||||
});
|
||||
|
||||
test('Test get key by action defined', function() {
|
||||
var key = Blockly.user.keyMap.getKeyByAction(Blockly.navigation.ACTION_PREVIOUS);
|
||||
chai.assert.equal(key, Blockly.utils.KeyCodes.W);
|
||||
});
|
||||
|
||||
test('Test get key by action not defined', function() {
|
||||
var key = Blockly.user.keyMap.getKeyByAction(new Blockly.Action('something'));
|
||||
chai.assert.notExists(key);
|
||||
});
|
||||
|
||||
test('Test set key map', function() {
|
||||
var testKeyMap = Blockly.user.keyMap.createDefaultKeyMap();
|
||||
testKeyMap['randomKey'] = new Blockly.Action('test','',null);
|
||||
Blockly.user.keyMap.setKeyMap(testKeyMap);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_['randomKey'].name, 'test');
|
||||
});
|
||||
|
||||
test('Test get key map returns a clone', function() {
|
||||
var keyMap = Blockly.user.keyMap.getKeyMap();
|
||||
keyMap['randomKey'] = new Blockly.Action('test', '', null);
|
||||
chai.assert.isUndefined(Blockly.user.keyMap.map_['randomKey']);
|
||||
});
|
||||
|
||||
test('Test serialize key code with modifiers', function() {
|
||||
var mockEvent = {
|
||||
getModifierState: function(){
|
||||
return true;
|
||||
},
|
||||
keyCode: 65
|
||||
};
|
||||
var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'ShiftControlAltMeta65');
|
||||
});
|
||||
|
||||
test('Test serialize key code without modifiers', function() {
|
||||
var mockEvent = {
|
||||
getModifierState: function(){
|
||||
return false;
|
||||
},
|
||||
keyCode: 65
|
||||
};
|
||||
var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
|
||||
test('Test modifiers in reverse order', function() {
|
||||
var testKey = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.K, [Blockly.user.keyMap.modifierKeys.CONTROL,
|
||||
Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
Blockly.user.keyMap.setActionForKey(testKey, new Blockly.Action('test', '', null));
|
||||
var action = Blockly.user.keyMap.getActionByKeyCode('ShiftControl75');
|
||||
chai.assert.isNotNull(action);
|
||||
chai.assert.equal(action.name, 'test');
|
||||
});
|
||||
|
||||
test('Test report invalid modifiers', function() {
|
||||
var shouldThrow = function() {
|
||||
Blockly.user.keyMap.createSerializedKey(Blockly.utils.KeyCodes.K, ['s',
|
||||
Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
};
|
||||
chai.assert.throws(shouldThrow, Error, 's is not a valid modifier key.');
|
||||
});
|
||||
|
||||
|
||||
teardown(function() {});
|
||||
});
|
||||
@@ -93,7 +93,7 @@ suite('Key Down', function() {
|
||||
suite('Copy', function() {
|
||||
setup(function() {
|
||||
setSelectedBlock(this.workspace);
|
||||
this.copySpy = sinon.spy(Blockly, 'copy_');
|
||||
this.copySpy = sinon.spy(Blockly, 'copy');
|
||||
this.hideChaffSpy = sinon.spy(Blockly, 'hideChaff');
|
||||
});
|
||||
var testCases = [
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
|
||||
suite('Navigation', function() {
|
||||
function createNavigationWorkspace(enableKeyboardNav) {
|
||||
function createNavigationWorkspace(enableKeyboardNav, readOnly) {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
var workspace =
|
||||
Blockly.inject('blocklyDiv', {toolbox: toolbox, readOnly: readOnly});
|
||||
if (enableKeyboardNav) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
@@ -45,64 +46,88 @@ suite('Navigation', function() {
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_();
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
function testToolboxSelectMethodCalled(ws, mockEvent, keyCode, selectMethodName) {
|
||||
mockEvent.keyCode = keyCode;
|
||||
var toolbox = ws.getToolbox();
|
||||
toolbox.selectedItem_ = toolbox.contents_[0];
|
||||
var selectNextStub = sinon.stub(toolbox, selectMethodName);
|
||||
Blockly.navigation.onKeyPress(mockEvent);
|
||||
sinon.assert.called(selectNextStub);
|
||||
}
|
||||
var testCases = [
|
||||
[
|
||||
'Calls toolbox selectNext_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField'), 'selectNext_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectPrevious_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField'),
|
||||
'selectPrevious_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectParent_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField'),
|
||||
'selectChild_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectChild_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField'),
|
||||
'selectParent_'
|
||||
]
|
||||
];
|
||||
|
||||
test('Calls toolbox selectNext_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.S, 'selectNext_');
|
||||
});
|
||||
test('Calls toolbox selectPrevious_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.W, 'selectPrevious_');
|
||||
});
|
||||
test('Calls toolbox selectParent_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.D, 'selectChild_');
|
||||
});
|
||||
test('Calls toolbox selectChild_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.A, 'selectParent_');
|
||||
testCases.forEach(function(testCase) {
|
||||
var testCaseName = testCase[0];
|
||||
var mockEvent = testCase[1];
|
||||
var stubName = testCase[2];
|
||||
test(testCaseName, function() {
|
||||
var toolbox = this.workspace.getToolbox();
|
||||
var selectStub = sinon.stub(toolbox, stubName);
|
||||
toolbox.selectedItem_ = toolbox.contents_[0];
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
sinon.assert.called(selectStub);
|
||||
});
|
||||
});
|
||||
|
||||
test('Go to flyout', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.D;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var flyoutCursor = Blockly.navigation.getFlyoutCursor_();
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
|
||||
var flyoutCursor = Blockly.navigation.getFlyoutCursor_();
|
||||
chai.assert.equal(flyoutCursor.getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Focuses workspace from toolbox (e)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.E;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.E, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
test('Focuses workspace from toolbox (escape)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.E;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
// More tests:
|
||||
// - nested categories
|
||||
@@ -124,14 +149,8 @@ suite('Navigation', function() {
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -140,10 +159,15 @@ suite('Navigation', function() {
|
||||
|
||||
// Should be a no-op
|
||||
test('Previous at beginning', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
chai.assert.equal(Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
@@ -155,45 +179,72 @@ suite('Navigation', function() {
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
|
||||
test('MARK', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
test('Mark', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
chai.assert.equal(this.workspace.getTopBlocks().length, 1);
|
||||
});
|
||||
|
||||
test('EXIT', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ESC;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
test('Exit', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -216,12 +267,6 @@ suite('Navigation', function() {
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
this.firstCategory_ = this.workspace.getToolbox().contents_[0];
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -229,77 +274,106 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
sinon.spy(this.workspace.getCursor(), 'prev');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(this.workspace.getCursor().prev);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
this.workspace.getCursor().prev.restore();
|
||||
var prevSpy = sinon.spy(this.workspace.getCursor(), 'prev');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var wEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, '');
|
||||
|
||||
Blockly.onKeyDown(wEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(prevSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'next');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.next);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.next.restore();
|
||||
var nextSpy = sinon.spy(this.workspace.getCursor(), 'next');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var sEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
|
||||
Blockly.onKeyDown(sEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(nextSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'out');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.out);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.out.restore();
|
||||
var outSpy = sinon.spy(this.workspace.getCursor(), 'out');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var aEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
|
||||
Blockly.onKeyDown(aEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(outSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('In', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'in');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.D;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.in);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.in.restore();
|
||||
var inSpy = sinon.spy(this.workspace.getCursor(), 'in');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var dEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, '');
|
||||
|
||||
Blockly.onKeyDown(dEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(inSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Insert', function() {
|
||||
// Stub modify as we are not testing its behavior, only if it was called.
|
||||
// Otherwise, there is a warning because there is no marked node.
|
||||
sinon.stub(Blockly.navigation, 'modify_');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.I;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(Blockly.navigation.modify_);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
Blockly.navigation.modify_.restore();
|
||||
var modifyStub = sinon.stub(Blockly.navigation, 'modify_').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var iEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
|
||||
Blockly.onKeyDown(iEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(modifyStub);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Mark', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection));
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var enterEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
|
||||
Blockly.onKeyDown(enterEvent);
|
||||
|
||||
var markedNode = this.workspace.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(markedNode.getLocation(), this.basicBlock.previousConnection);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Toolbox', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.T;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(this.workspace.getToolbox().getSelectedItem(), this.firstCategory_);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var tEvent = createKeyDownEvent(Blockly.utils.KeyCodes.T, '');
|
||||
|
||||
Blockly.onKeyDown(tEvent);
|
||||
|
||||
var firstCategory = this.workspace.getToolbox().contents_[0];
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
this.workspace.getToolbox().getSelectedItem(), firstCategory);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -325,83 +399,84 @@ suite('Navigation', function() {
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
|
||||
test('Action does not exist', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.N, '');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.N;
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isFalse(isHandled);
|
||||
sinon.assert.notCalled(field.onBlocklyAction);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isFalse(keyDownSpy.returned(true));
|
||||
sinon.assert.notCalled(fieldSpy);
|
||||
});
|
||||
|
||||
test('Action exists - field handles action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
|
||||
sinon.stub(field, 'onBlocklyAction').returns(true);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.stub(field, 'onBlocklyAction').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(field.onBlocklyAction);
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
});
|
||||
|
||||
test('Action exists - field does not handle action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(field.onBlocklyAction);
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
});
|
||||
|
||||
test('Toggle Action Off', function() {
|
||||
this.mockEvent.keyCode = 'ShiftControl75';
|
||||
sinon.spy(Blockly.navigation, 'onBlocklyAction');
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = true;
|
||||
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(Blockly.navigation.onBlocklyAction);
|
||||
chai.assert.isFalse(Blockly.getMainWorkspace().keyboardAccessibilityMode);
|
||||
Blockly.navigation.onBlocklyAction.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isFalse(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
test('Toggle Action On', function() {
|
||||
this.mockEvent.keyCode = 'ShiftControl75';
|
||||
sinon.stub(Blockly.navigation, 'focusWorkspace_');
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = false;
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = false;
|
||||
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(Blockly.navigation.focusWorkspace_);
|
||||
chai.assert.isTrue(Blockly.getMainWorkspace().keyboardAccessibilityMode);
|
||||
Blockly.navigation.focusWorkspace_.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isTrue(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
suite('Test key press in read only mode', function() {
|
||||
@@ -431,19 +506,12 @@ suite('Navigation', function() {
|
||||
"tooltip": "",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
this.workspace = Blockly.inject('blocklyDiv', {readOnly: true});
|
||||
this.workspace = createNavigationWorkspace(true, true);
|
||||
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.fieldBlock1 = this.workspace.newBlock('field_block');
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -452,24 +520,39 @@ suite('Navigation', function() {
|
||||
|
||||
test('Perform valid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
});
|
||||
|
||||
test('Perform invalid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.I;
|
||||
chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
|
||||
test('Try to perform action on a field', function() {
|
||||
var field = this.fieldBlock1.inputList[0].fieldRow[0];
|
||||
var astNode = Blockly.ASTNode.createFieldNode(field);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -508,9 +591,9 @@ suite('Navigation', function() {
|
||||
var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection);
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(prevNode);
|
||||
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
Blockly.navigation.insertFromFlyout();
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var insertedBlock = this.basicBlock.previousConnection.targetBlock();
|
||||
|
||||
@@ -520,9 +603,9 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Insert Block from flyout without marking a connection', function() {
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
Blockly.navigation.insertFromFlyout();
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var numBlocks = this.workspace.getTopBlocks().length;
|
||||
|
||||
|
||||
355
tests/mocha/shortcut_registry_test.js
Normal file
355
tests/mocha/shortcut_registry_test.js
Normal file
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Keyboard Shortcut Registry Test', function() {
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
this.registry = new Blockly.ShortcutRegistry();
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
suite('Registering', function() {
|
||||
test('Registering a shortcut', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.register(testShortcut, true);
|
||||
var shortcut = this.registry.registry_['test_shortcut'];
|
||||
chai.assert.equal(shortcut.name, 'test_shortcut');
|
||||
});
|
||||
test('Registers shortcut with same name', function() {
|
||||
var registry = this.registry;
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
|
||||
registry.registry_['test_shortcut'] = [testShortcut];
|
||||
|
||||
var shouldThrow = function() {
|
||||
registry.register(testShortcut);
|
||||
};
|
||||
chai.assert.throws(
|
||||
shouldThrow, Error,
|
||||
'Shortcut with name "test_shortcut" already exists.');
|
||||
});
|
||||
test(
|
||||
'Registers shortcut with same name opt_allowOverrides=true',
|
||||
function() {
|
||||
var registry = this.registry;
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
var otherShortcut = {
|
||||
'name': 'test_shortcut',
|
||||
'callback': function() {}
|
||||
};
|
||||
|
||||
registry.registry_['test_shortcut'] = [testShortcut];
|
||||
|
||||
var shouldNotThrow = function() {
|
||||
registry.register(otherShortcut, true);
|
||||
};
|
||||
chai.assert.doesNotThrow(shouldNotThrow);
|
||||
chai.assert.exists(registry.registry_['test_shortcut'].callback);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Unregistering', function() {
|
||||
test('Unregistering a shortcut', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.registry_['test'] = [testShortcut];
|
||||
chai.assert.isOk(this.registry.registry_['test']);
|
||||
this.registry.unregister('test', 'test_shortcut');
|
||||
chai.assert.isUndefined(this.registry.registry_['test']);
|
||||
});
|
||||
test('Unregistering a nonexistent shortcut', function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
chai.assert.isUndefined(this.registry.registry_['test']);
|
||||
|
||||
var registry = this.registry;
|
||||
chai.assert.isFalse(registry.unregister('test', 'test_shortcut'));
|
||||
sinon.assert.calledOnceWithExactly(consoleStub, 'Keyboard shortcut with name "test" not found.');
|
||||
});
|
||||
test('Unregistering a shortcut with key mappings', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut'];
|
||||
this.registry.registry_['test_shortcut'] = testShortcut;
|
||||
|
||||
this.registry.unregister('test_shortcut');
|
||||
|
||||
var shortcut = this.registry.registry_['test'];
|
||||
var keyMappings = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.isUndefined(shortcut);
|
||||
chai.assert.isUndefined(keyMappings);
|
||||
});
|
||||
test('Unregistering a shortcut with colliding key mappings', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut', 'other_shortcutt'];
|
||||
this.registry.registry_['test_shortcut'] = testShortcut;
|
||||
|
||||
this.registry.unregister('test_shortcut');
|
||||
|
||||
var shortcut = this.registry.registry_['test'];
|
||||
var keyMappings = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(keyMappings, 1);
|
||||
chai.assert.isUndefined(shortcut);
|
||||
});
|
||||
});
|
||||
|
||||
suite('addKeyMapping', function() {
|
||||
test('Adds a key mapping', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
|
||||
this.registry.addKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 1);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut');
|
||||
});
|
||||
test('Adds a colliding key mapping - opt_allowCollision=true', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
this.registry.addKeyMapping('keyCode', 'test_shortcut', true);
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 2);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut');
|
||||
chai.assert.equal(shortcutNames[1], 'test_shortcut_2');
|
||||
});
|
||||
test('Adds a colliding key mapping - opt_allowCollision=false', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
var registry = this.registry;
|
||||
var shouldThrow = function() {
|
||||
registry.addKeyMapping('keyCode', 'test_shortcut');
|
||||
};
|
||||
chai.assert.throws(
|
||||
shouldThrow, Error,
|
||||
'Shortcut with name "test_shortcut" collides with shortcuts test_shortcut_2');
|
||||
});
|
||||
});
|
||||
|
||||
suite('removeKeyMapping', function() {
|
||||
test('Removes a key mapping', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut', 'test_shortcut_2'];
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 1);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut_2');
|
||||
chai.assert.isTrue(isRemoved);
|
||||
});
|
||||
test('Removes last key mapping for a key', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut'];
|
||||
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.isUndefined(shortcutNames);
|
||||
});
|
||||
test('Removes a key map that does not exist opt_quiet=false', function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
chai.assert.isFalse(isRemoved);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
consoleStub,
|
||||
'No keyboard shortcut with name "test_shortcut" registered with key code "keyCode"');
|
||||
});
|
||||
test(
|
||||
'Removes a key map that does not exist from empty key mapping opt_quiet=false',
|
||||
function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
chai.assert.isFalse(isRemoved);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
consoleStub,
|
||||
'No keyboard shortcut with name "test_shortcut" registered with key code "keyCode"');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setters/Getters', function() {
|
||||
test('Sets the key map', function() {
|
||||
this.registry.setKeyMap({'keyCode': ['test_shortcut']});
|
||||
chai.assert.lengthOf(Object.keys(this.registry.keyMap_), 1);
|
||||
chai.assert.equal(this.registry.keyMap_['keyCode'][0], 'test_shortcut');
|
||||
});
|
||||
test('Gets a copy of the key map', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['a'];
|
||||
var keyMapCopy = this.registry.getKeyMap();
|
||||
keyMapCopy['keyCode'] = ['b'];
|
||||
chai.assert.equal(this.registry.keyMap_['keyCode'][0], 'a');
|
||||
});
|
||||
test('Gets a copy of the registry', function() {
|
||||
this.registry.registry_['shortcutName'] = {'name': 'shortcutName'};
|
||||
var registrycopy = this.registry.getRegistry();
|
||||
registrycopy['shortcutName']['name'] = 'shortcutName1';
|
||||
chai.assert.equal(
|
||||
this.registry.registry_['shortcutName']['name'], 'shortcutName');
|
||||
});
|
||||
test('Gets keyboard shortcuts from a key code', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['shortcutName'];
|
||||
var shortcutNames = this.registry.getKeyboardShortcuts('keyCode');
|
||||
chai.assert.equal(shortcutNames[0], 'shortcutName');
|
||||
});
|
||||
test('Gets keycodes by shortcut name', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['shortcutName'];
|
||||
this.registry.keyMap_['keyCode1'] = ['shortcutName'];
|
||||
var shortcutNames =
|
||||
this.registry.getKeyCodeByShortcutName('shortcutName');
|
||||
chai.assert.lengthOf(shortcutNames, 2);
|
||||
chai.assert.equal(shortcutNames[0], 'keyCode');
|
||||
chai.assert.equal(shortcutNames[1], 'keyCode1');
|
||||
});
|
||||
});
|
||||
|
||||
suite('onKeyDown', function() {
|
||||
function addShortcut(registry, shortcut, keyCode, returns) {
|
||||
registry.register(shortcut, true);
|
||||
registry.addKeyMapping(keyCode, shortcut.name, true);
|
||||
return sinon.stub(shortcut, 'callback').returns(returns);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
this.testShortcut = {
|
||||
'name': 'test_shortcut',
|
||||
'callback': function() {
|
||||
return true;
|
||||
},
|
||||
'precondition': function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
this.callBackStub =
|
||||
addShortcut(this.registry, this.testShortcut, 'keyCode', true);
|
||||
});
|
||||
test('Execute a shortcut from event', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('No shortcut executed from event', function() {
|
||||
var event = createKeyDownEvent('nonExistentKeyCode', '');
|
||||
chai.assert.isFalse(this.registry.onKeyDown(this.workspace, event));
|
||||
});
|
||||
test('No precondition available - execute callback', function() {
|
||||
delete this.testShortcut['precondition'];
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('Execute all shortcuts in list', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
var testShortcut2 = {
|
||||
'name': 'test_shortcut_2',
|
||||
'callback': function() {
|
||||
return false;
|
||||
},
|
||||
'precondition': function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var testShortcut2Stub =
|
||||
addShortcut(this.registry, testShortcut2, 'keyCode', false);
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(testShortcut2Stub);
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('Stop executing shortcut when event is handled', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
var testShortcut2 = {
|
||||
'name': 'test_shortcut_2',
|
||||
'callback': function() {
|
||||
return false;
|
||||
},
|
||||
'precondition': function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var testShortcut2Stub =
|
||||
addShortcut(this.registry, testShortcut2, 'keyCode', true);
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(testShortcut2Stub);
|
||||
sinon.assert.notCalled(this.callBackStub);
|
||||
});
|
||||
});
|
||||
|
||||
suite('createSerializedKey', function() {
|
||||
test('Serialize key', function() {
|
||||
var serializedKey =
|
||||
this.registry.createSerializedKey(Blockly.utils.KeyCodes.A);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
|
||||
test('Serialize key code and modifier', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.A, [Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKey, 'Control+65');
|
||||
});
|
||||
test('Serialize only a modifier', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKey, 'Control');
|
||||
});
|
||||
test('Serialize multiple modifiers', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
});
|
||||
test('Order of modifiers should result in same serialized key', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
var serializedKeyNewOrder = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKeyNewOrder, 'Shift+Control');
|
||||
});
|
||||
});
|
||||
|
||||
suite('serializeKeyEvent', function() {
|
||||
test('Serialize key', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
test('Serialize key code and modifier', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.A, '', [Blockly.utils.KeyCodes.CTRL]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Control+65');
|
||||
});
|
||||
test('Serialize only a modifier', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(null, '', [Blockly.utils.KeyCodes.CTRL]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Control');
|
||||
});
|
||||
test('Serialize multiple modifiers', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
null, '',
|
||||
[Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
});
|
||||
test('Throw error when incorrect modifier', function() {
|
||||
var registry = this.registry;
|
||||
var shouldThrow = function() {
|
||||
registry.createSerializedKey(Blockly.utils.KeyCodes.K, ['s']);
|
||||
};
|
||||
chai.assert.throws(shouldThrow, Error, 's is not a valid modifier key.');
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {});
|
||||
});
|
||||
@@ -576,10 +576,17 @@ function dispatchPointerEvent(target, type, properties) {
|
||||
function createKeyDownEvent(keyCode, type, modifiers) {
|
||||
var event = {
|
||||
keyCode: keyCode,
|
||||
target: {
|
||||
type: type
|
||||
},
|
||||
getModifierState: function() {
|
||||
target: {type: type},
|
||||
getModifierState: function(name) {
|
||||
if (name == 'Shift' && this.shiftKey) {
|
||||
return true;
|
||||
} else if (name == 'Control' && this.ctrlKey) {
|
||||
return true;
|
||||
} else if (name == 'Meta' && this.metaKey) {
|
||||
return true;
|
||||
} else if (name == 'Alt' && this.altKey) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
preventDefault: function() {}
|
||||
|
||||
Reference in New Issue
Block a user