var _ = require('underscore'); var sinon = require('sinon'); var assert = require('chai').assert; var ko = require('knockout'); var Mousetrap = require('app/client/lib/Mousetrap'); var commands = require('app/client/components/commands'); var clientUtil = require('../clientUtil'); describe('commands', function() { clientUtil.setTmpMochaGlobals(); before(function() { sinon.stub(Mousetrap, "bind"); sinon.stub(Mousetrap, "unbind"); }); after(function() { Mousetrap.bind.restore(); Mousetrap.unbind.restore(); }); beforeEach(function() { commands.init([{ group: "Foo", commands: [{ name: "cmd1", keys: ["Ctrl+a", "Ctrl+b"], desc: "Command 1" }, { name: "cmd2", keys: ["Ctrl+c"], desc: "Command 2" }, { name: "cmd3", keys: ["Ctrl+a"], desc: "Command 1B" }] }]); }); describe("activate", function() { it("should invoke Mousetrap.bind/unbind", function() { var obj = {}; var spy = sinon.spy(); var cmdGroup = commands.createGroup({ cmd1: spy }, obj, true); sinon.assert.callCount(Mousetrap.bind, 2); sinon.assert.calledWith(Mousetrap.bind, "ctrl+a"); sinon.assert.calledWith(Mousetrap.bind, "ctrl+b"); Mousetrap.bind.reset(); Mousetrap.unbind.reset(); commands.allCommands.cmd1.run(); sinon.assert.callCount(spy, 1); sinon.assert.calledOn(spy, obj); cmdGroup.activate(false); sinon.assert.callCount(Mousetrap.bind, 0); sinon.assert.callCount(Mousetrap.unbind, 2); sinon.assert.calledWith(Mousetrap.unbind, "ctrl+a"); sinon.assert.calledWith(Mousetrap.unbind, "ctrl+b"); Mousetrap.bind.reset(); Mousetrap.unbind.reset(); commands.allCommands.cmd1.run(); sinon.assert.callCount(spy, 1); cmdGroup.activate(true); sinon.assert.callCount(Mousetrap.bind, 2); sinon.assert.calledWith(Mousetrap.bind, "ctrl+a"); sinon.assert.calledWith(Mousetrap.bind, "ctrl+b"); sinon.assert.callCount(Mousetrap.unbind, 0); commands.allCommands.cmd1.run(); sinon.assert.callCount(spy, 2); cmdGroup.dispose(); sinon.assert.callCount(Mousetrap.unbind, 2); sinon.assert.calledWith(Mousetrap.unbind, "ctrl+a"); sinon.assert.calledWith(Mousetrap.unbind, "ctrl+b"); commands.allCommands.cmd1.run(); sinon.assert.callCount(spy, 2); }); /** * For an object of the form { group1: { cmd1: sinon.spy() } }, goes through all spys, and * returns a mapping of call counts: {'group1:cmd1': spyCallCount}. */ function getCallCounts(groups) { var counts = {}; _.each(groups, function(group, grpName) { _.each(group, function(cmdSpy, cmdName) { counts[grpName + ":" + cmdName] = cmdSpy.callCount; }); }); return counts; } /** * Diffs two sets of call counts as produced by getCallCounts and returns the difference. */ function diffCallCounts(callCounts1, callCounts2) { return _.chain(callCounts2).mapObject(function(count, name) { return count - callCounts1[name]; }) .pick(function(count, name) { return count > 0; }) .value(); } /** * Invokes the given command, and makes sure the difference of call counts before and after is * as expected. */ function assertCallCounts(groups, cmdOrFunc, expectedCounts) { var before = getCallCounts(groups); if (typeof cmdOrFunc === 'string') { commands.allCommands[cmdOrFunc].run(); } else if (cmdOrFunc === null) { // nothing } else { cmdOrFunc(); } var after = getCallCounts(groups); assert.deepEqual(diffCallCounts(before, after), expectedCounts); } it("should respect order of CommandGroups", function() { var groups = { group1: { cmd1: sinon.spy(), cmd3: sinon.spy() }, group2: { cmd1: sinon.spy(), cmd2: sinon.spy() }, group3: { cmd3: sinon.spy() }, }; var cmdGroup1 = commands.createGroup(groups.group1, null, true); var cmdGroup2 = commands.createGroup(groups.group2, null, true); var cmdGroup3 = commands.createGroup(groups.group3, null, false); assertCallCounts(groups, 'cmd1', {'group2:cmd1': 1}); assertCallCounts(groups, 'cmd2', {'group2:cmd2': 1}); assertCallCounts(groups, 'cmd3', {'group1:cmd3': 1}); cmdGroup2.activate(false); assertCallCounts(groups, 'cmd1', {'group1:cmd1': 1}); assertCallCounts(groups, 'cmd2', {}); assertCallCounts(groups, 'cmd3', {'group1:cmd3': 1}); cmdGroup3.activate(true); cmdGroup1.activate(false); assertCallCounts(groups, 'cmd1', {}); assertCallCounts(groups, 'cmd2', {}); assertCallCounts(groups, 'cmd3', {'group3:cmd3': 1}); cmdGroup2.activate(true); assertCallCounts(groups, 'cmd1', {'group2:cmd1': 1}); assertCallCounts(groups, 'cmd2', {'group2:cmd2': 1}); assertCallCounts(groups, 'cmd3', {'group3:cmd3': 1}); }); it("should allow use of observable for activation flag", function() { var groups = { groupFoo: { cmd1: sinon.spy() }, }; var isActive = ko.observable(false); commands.createGroup(groups.groupFoo, null, isActive); assertCallCounts(groups, 'cmd1', {}); isActive(true); assertCallCounts(groups, 'cmd1', {'groupFoo:cmd1': 1}); // Check that subsequent calls continue working. assertCallCounts(groups, 'cmd1', {'groupFoo:cmd1': 1}); isActive(false); assertCallCounts(groups, 'cmd1', {}); }); function getFuncForShortcut(shortcut) { function argsIncludeShortcut(args) { return Array.isArray(args[0]) ? _.contains(args[0], shortcut) : (args[0] === shortcut); } var b = _.findLastIndex(Mousetrap.bind.args, argsIncludeShortcut); var u = _.findLastIndex(Mousetrap.unbind.args, argsIncludeShortcut); if (b < 0) { return null; } else if (u < 0) { return Mousetrap.bind.args[b][1]; } else if (Mousetrap.bind.getCall(b).calledBefore(Mousetrap.unbind.getCall(u))) { return null; } else { return Mousetrap.bind.args[b][1]; } } it("should allow same keys used for different commands", function() { // Both cmd1 and cmd3 use "Ctrl+a" shortcut, so cmd3 should win when group3 is active. Mousetrap.bind.reset(); Mousetrap.unbind.reset(); var groups = { group1: { cmd1: sinon.spy() }, group3: { cmd3: sinon.spy() }, }; var cmdGroup1 = commands.createGroup(groups.group1, null, true); var cmdGroup3 = commands.createGroup(groups.group3, null, true); assertCallCounts(groups, getFuncForShortcut('ctrl+a'), {'group3:cmd3': 1}); assertCallCounts(groups, getFuncForShortcut('ctrl+b'), {'group1:cmd1': 1}); cmdGroup3.activate(false); assertCallCounts(groups, getFuncForShortcut('ctrl+a'), {'group1:cmd1': 1}); assertCallCounts(groups, getFuncForShortcut('ctrl+b'), {'group1:cmd1': 1}); cmdGroup1.activate(false); assertCallCounts(groups, getFuncForShortcut('ctrl+a'), {}); assertCallCounts(groups, getFuncForShortcut('ctrl+b'), {}); cmdGroup3.activate(true); assertCallCounts(groups, getFuncForShortcut('ctrl+a'), {'group3:cmd3': 1}); assertCallCounts(groups, getFuncForShortcut('ctrl+b'), {}); }); }); });