1 | import {simulate} from 'combokeys/test/lib/key-event';
|
2 |
|
3 | import sniffr from '../global/sniffer';
|
4 |
|
5 | import shortcuts from './core';
|
6 |
|
7 | describe('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 | });
|