You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/test/client/components/commands.js

217 lines
7.4 KiB

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'), {});
});
});
});