UNPKG

7.86 kBJavaScriptView Raw
1import {simulate} from 'combokeys/test/lib/key-event';
2
3import sniffr from '../global/sniffer';
4
5import shortcuts from './core';
6
7describe('Shortcuts', () => {
8 const key = 'a';
9 const keyCode = '65';
10 const key2 = 'b';
11 const scope = 'scope scope scope';
12 let noop;
13 let noop2;
14
15 function trigger() {
16 simulate(key.charCodeAt(0), keyCode);
17 }
18
19 const wrapScope = shortcuts.wrapScope.bind(shortcuts);
20
21 beforeEach(() => {
22 shortcuts.reset();
23 shortcuts.setScope();
24 shortcuts.setFilter();
25
26 noop = sandbox.stub();
27 noop2 = sandbox.stub();
28 });
29
30 describe('bind', () => {
31 it('should throw without a handler', () => {
32 (() => {
33 shortcuts.bind();
34 }).should.throw(Error, 'Shortcut handler should exist');
35 });
36
37 it('should throw without a key', () => {
38 (() => {
39 shortcuts.bind({handler: sandbox.stub()});
40 }).should.throw(Error, 'Shortcut key should exist');
41 });
42
43 it('should bind to root scope', () => {
44 shortcuts.bind({key, handler: noop});
45
46 shortcuts._scopes[shortcuts.ROOT_SCOPE.scopeId][key].should.equal(noop);
47 });
48
49 it('should bind to custom scope', () => {
50 shortcuts.bind({key, scope, handler: noop});
51
52 shortcuts._scopes[scope][key].should.equal(noop);
53 });
54
55 it('should bind array of keys', () => {
56 const keys = [key, key2];
57 shortcuts.bind({key: keys, handler: noop});
58
59 shortcuts._scopes[shortcuts.ROOT_SCOPE.scopeId][key].should.equal(noop);
60 shortcuts._scopes[shortcuts.ROOT_SCOPE.scopeId][key2].should.equal(noop);
61 });
62 });
63
64 describe('bindMap', () => {
65 it('should throw without a map', () => {
66 (() => {
67 shortcuts.bindMap();
68 }).should.throw(Error, 'Shortcuts map shouldn\'t be empty');
69 });
70
71 it('should throw with wrong handler', () => {
72 (() => {
73 shortcuts.bindMap({a: {}});
74 }).should.throw(Error, 'Shortcut handler should exist');
75 });
76
77 it('should bind map of keys to root scope', () => {
78 const keys = {};
79 keys[key] = noop;
80 keys[key2] = noop2;
81 shortcuts.bindMap(keys);
82
83 shortcuts._scopes[shortcuts.ROOT_SCOPE.scopeId][key].should.equal(noop);
84 shortcuts._scopes[shortcuts.ROOT_SCOPE.scopeId][key2].should.equal(noop2);
85 });
86
87 it('should bind map of keys to custom scope', () => {
88 const keys = {};
89 keys[key] = noop;
90 keys[key2] = noop2;
91 shortcuts.bindMap(keys, {scope});
92
93 shortcuts._scopes[scope][key].should.equal(noop);
94 shortcuts._scopes[scope][key2].should.equal(noop2);
95 });
96 });
97
98 describe('unbindScope', () => {
99 it('should clear scope', () => {
100 shortcuts.bind({key, scope, handler: noop});
101 shortcuts.unbindScope(scope);
102
103 should.not.exist(shortcuts._scopes[scope]);
104 });
105 });
106
107 describe('hasKey', () => {
108 it('should clear scope', () => {
109 shortcuts.bind({key, scope, handler: noop});
110
111 shortcuts.hasKey(key, scope).should.be.true;
112 shortcuts.hasKey(key, shortcuts.ROOT_SCOPE.scopeId).should.be.false;
113 });
114 });
115
116 describe('filter', () => {
117 it('should setFilter', () => {
118 shortcuts.setFilter(noop2);
119 shortcuts.bind({key, handler: noop});
120
121 trigger();
122
123 noop.should.have.been.called;
124 noop2.should.have.been.called;
125 });
126
127 it('should prevent handler run', () => {
128 const stop = sandbox.stub().returns(true);
129
130 shortcuts.setFilter(stop);
131 shortcuts.bind({key, handler: noop});
132
133 trigger();
134
135 stop.should.have.been.called;
136 noop.should.not.have.been.called;
137 });
138 });
139
140 describe('key press', () => {
141 it('should handle keys in root scope', () => {
142 shortcuts.bind({key, handler: noop});
143
144 trigger();
145
146 noop.should.have.been.called;
147 });
148
149 it('should handle keys in root scope with other scope defined', () => {
150 shortcuts.bind({key, handler: noop});
151 shortcuts.bind({key, scope, handler: noop2});
152
153 trigger();
154
155 noop.should.have.been.called;
156 noop2.should.not.have.been.called;
157 });
158
159 it('should handle keys in top scope', () => {
160 shortcuts.bind({key, handler: noop});
161 shortcuts.bind({key, scope, handler: noop2});
162
163 shortcuts.pushScope(scope);
164 trigger();
165
166 noop.should.not.have.been.called;
167 noop2.should.have.been.called;
168 });
169
170 it('should fall trough scopes when returning true', () => {
171 const fallthrough = sandbox.stub().returns(true);
172
173 shortcuts.bind({key, handler: noop});
174 shortcuts.bind({key, scope, handler: fallthrough});
175
176 shortcuts.pushScope(scope);
177 trigger();
178
179 noop.should.have.been.called;
180 fallthrough.should.have.been.called;
181 });
182
183 it('should not fall trough modal scope', () => {
184 const fallthrough = sandbox.stub().returns(true);
185
186 shortcuts.bind({key, handler: noop});
187 shortcuts.bind({key, scope, handler: fallthrough});
188
189 shortcuts.pushScope(scope, {modal: true});
190 trigger();
191
192 fallthrough.should.have.been.called;
193 noop.should.not.have.been.called;
194 });
195
196 it('should not fall trough modal scope even if it has no handler for key', () => {
197 const fallthrough = sandbox.stub().returns(true);
198
199 shortcuts.bind({key, handler: noop});
200 shortcuts.bind({key: key2, scope, handler: fallthrough});
201
202 shortcuts.pushScope(scope, {modal: true});
203 trigger();
204
205 noop.should.not.have.been.called;
206 });
207 });
208
209 describe('scope chain operations', () => {
210 const scope1 = 'a';
211 const scope2 = 'bb';
212 const scope3 = 'ccc';
213
214 it('empty scope chain should be equal to default', () => {
215 shortcuts.getScope().should.deep.equal([]);
216 });
217
218 it('setScope should set full scope chain by string name', () => {
219 const myscope = 'aaaa';
220 shortcuts.setScope(myscope);
221
222 shortcuts.getScope().should.deep.equal([wrapScope(myscope)]);
223 });
224
225 it('setScope should set full scope chain by array of names', () => {
226 shortcuts.setScope([scope1, scope2]);
227
228 shortcuts.getScope().should.deep.equal([wrapScope(scope1), wrapScope(scope2)]);
229 });
230
231 it('pushScope should add scope to scope chain end', () => {
232 shortcuts.setScope(scope1);
233 shortcuts.pushScope(scope2);
234
235 shortcuts.getScope().should.deep.equal([wrapScope(scope1), wrapScope(scope2)]);
236 });
237
238 it('popScope should remove by name scope and next scopes from chain', () => {
239 shortcuts.setScope([scope1, scope2, scope3]);
240 shortcuts.popScope(scope2);
241
242 shortcuts.getScope().should.deep.equal([wrapScope(scope1)]);
243 });
244
245 it('spliceScope should remove by name scope from chain', () => {
246 shortcuts.setScope([scope1, scope2, scope3]);
247 shortcuts.spliceScope(scope2);
248
249 shortcuts.getScope().should.deep.equal([wrapScope(scope1), wrapScope(scope3)]);
250 });
251
252 it('should store options passed with scope', () => {
253 shortcuts.pushScope(scope1, {foo: 'bar'});
254
255 shortcuts.getScope().should.deep.equal([wrapScope(scope1, {foo: 'bar'})]);
256 });
257
258 it('should workaround system windows shortcuts', () => {
259 let eventType;
260 sandbox.stub(shortcuts.combokeys, 'bind').callsFake((param1, param2, param3) => {
261 eventType = param3;
262 });
263 sandbox.stub(sniffr, 'os').value({name: 'windows'});
264
265 shortcuts.bind({key: 'shift+ctrl+0', handler: noop});
266
267 eventType.should.equal('keyup');
268 });
269
270 it('should not apply workaround for system windows shortcuts on other operating systems', () => {
271 let eventType;
272 sandbox.stub(shortcuts.combokeys, 'bind').callsFake((param1, param2, param3) => {
273 eventType = param3;
274 });
275 sandbox.stub(sniffr, 'os').value({name: 'macos'});
276
277 shortcuts.bind({key: 'shift+ctrl+0', handler: noop});
278
279 ('keyup').should.not.equal(eventType);
280 });
281 });
282});