@peter.naydenov/shortcuts 3.5.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/API.md +939 -0
  2. package/CODE_OF_CONDUCT.md +84 -0
  3. package/CONTRIBUTING.md +476 -0
  4. package/Changelog.md +30 -1
  5. package/How.to.create.plugins.md +929 -0
  6. package/Migration.guide.md +48 -0
  7. package/README.md +396 -24
  8. package/dist/main.d.ts +54 -2
  9. package/dist/methods/_normalizeWithPlugins.d.ts +63 -1
  10. package/dist/methods/_readShortcutWithPlugins.d.ts +8 -1
  11. package/dist/methods/_setupPlugin.d.ts +9 -0
  12. package/dist/methods/_systemAction.d.ts +8 -1
  13. package/dist/methods/changeContext.d.ts +8 -1
  14. package/dist/methods/index.d.ts +2 -0
  15. package/dist/methods/listShortcuts.d.ts +1 -16
  16. package/dist/methods/load.d.ts +8 -1
  17. package/dist/methods/unload.d.ts +8 -1
  18. package/dist/plugins/click/_findTarget.d.ts +9 -1
  19. package/dist/plugins/click/_listenDOM.d.ts +76 -3
  20. package/dist/plugins/click/_normalizeShortcutName.d.ts +7 -1
  21. package/dist/plugins/click/_registerShortcutEvents.d.ts +26 -0
  22. package/dist/plugins/click/index.d.ts +6 -5
  23. package/dist/plugins/form/_defaults.d.ts +13 -1
  24. package/dist/plugins/form/_listenDOM.d.ts +66 -3
  25. package/dist/plugins/form/_registerShortcutEvents.d.ts +95 -1
  26. package/dist/plugins/form/index.d.ts +2 -3
  27. package/dist/plugins/hover/_findTarget.d.ts +10 -0
  28. package/dist/plugins/hover/_listenDOM.d.ts +68 -0
  29. package/dist/plugins/hover/_normalizeShortcutName.d.ts +2 -0
  30. package/dist/plugins/hover/_registerShortcutEvents.d.ts +28 -0
  31. package/dist/plugins/hover/index.d.ts +14 -0
  32. package/dist/plugins/key/_listenDOM.d.ts +61 -3
  33. package/dist/plugins/key/_registerShortcutEvents.d.ts +26 -0
  34. package/dist/plugins/key/_specialChars.d.ts +6 -31
  35. package/dist/plugins/key/index.d.ts +2 -3
  36. package/dist/plugins/scroll/_listenDOM.d.ts +58 -0
  37. package/dist/plugins/scroll/_normalizeShortcutName.d.ts +2 -0
  38. package/dist/plugins/scroll/_registerShortcutEvents.d.ts +28 -0
  39. package/dist/plugins/scroll/index.d.ts +16 -0
  40. package/dist/shortcuts.cjs +1 -1
  41. package/dist/shortcuts.esm.mjs +1 -1
  42. package/dist/shortcuts.umd.js +1 -1
  43. package/eslint.config.js +80 -0
  44. package/html/assets/index-COTh6lXR.css +1 -0
  45. package/html/assets/index-DOkKC3NI.js +53 -0
  46. package/html/bg.png +0 -0
  47. package/html/favicon.ico +0 -0
  48. package/html/favicon.svg +5 -0
  49. package/html/html.meta.json.gz +0 -0
  50. package/html/index.html +32 -0
  51. package/package.json +16 -12
  52. package/shortcuts.png +0 -0
  53. package/src/main.js +52 -22
  54. package/src/methods/_normalizeWithPlugins.js +26 -2
  55. package/src/methods/_readShortcutWithPlugins.js +9 -2
  56. package/src/methods/_setupPlugin.js +93 -0
  57. package/src/methods/_systemAction.js +12 -4
  58. package/src/methods/changeContext.js +11 -3
  59. package/src/methods/index.js +2 -0
  60. package/src/methods/listShortcuts.js +5 -12
  61. package/src/methods/load.js +11 -4
  62. package/src/methods/unload.js +8 -1
  63. package/src/plugins/click/_findTarget.js +11 -5
  64. package/src/plugins/click/_listenDOM.js +58 -20
  65. package/src/plugins/click/_normalizeShortcutName.js +11 -4
  66. package/src/plugins/click/_readClickEvent.js +1 -1
  67. package/src/plugins/click/_registerShortcutEvents.js +33 -5
  68. package/src/plugins/click/index.js +34 -51
  69. package/src/plugins/form/_defaults.js +13 -3
  70. package/src/plugins/form/_listenDOM.js +46 -9
  71. package/src/plugins/form/_normalizeShortcutName.js +2 -2
  72. package/src/plugins/form/_registerShortcutEvents.js +93 -17
  73. package/src/plugins/form/index.js +26 -47
  74. package/src/plugins/hover/_findTarget.js +26 -0
  75. package/src/plugins/hover/_listenDOM.js +154 -0
  76. package/src/plugins/hover/_normalizeShortcutName.js +21 -0
  77. package/src/plugins/hover/_registerShortcutEvents.js +51 -0
  78. package/src/plugins/hover/index.js +71 -0
  79. package/src/plugins/key/_listenDOM.js +67 -33
  80. package/src/plugins/key/_normalizeShortcutName.js +4 -3
  81. package/src/plugins/key/_readKeyEvent.js +1 -1
  82. package/src/plugins/key/_registerShortcutEvents.js +34 -5
  83. package/src/plugins/key/_specialChars.js +5 -0
  84. package/src/plugins/key/index.js +35 -50
  85. package/src/plugins/scroll/_listenDOM.js +141 -0
  86. package/src/plugins/scroll/_normalizeShortcutName.js +21 -0
  87. package/src/plugins/scroll/_registerShortcutEvents.js +50 -0
  88. package/src/plugins/scroll/index.js +61 -0
  89. package/test/01-general.test.js +92 -23
  90. package/test/02-key.test.js +241 -40
  91. package/test/03-click.test.js +291 -47
  92. package/test/04-form.test.js +241 -47
  93. package/test/05-hover.test.js +463 -0
  94. package/test/06-scroll.test.js +374 -0
  95. package/test-helpers/Block.jsx +3 -2
  96. package/test-helpers/style.css +6 -1
  97. package/vitest.config.js +13 -11
  98. package/How..to.make.plugins.md +0 -41
@@ -1,5 +1,5 @@
1
1
  import { beforeEach, afterEach, describe, it, expect } from 'vitest'
2
- import { userEvent } from '@vitest/browser/context'
2
+ import { userEvent } from 'vitest/browser'
3
3
  import {
4
4
  getByLabelText,
5
5
  getByText,
@@ -56,7 +56,7 @@ const contextDefinition = {
56
56
  , 'form : define' : () => 'input'
57
57
  , 'form : action' : () => [
58
58
  {
59
- fn : (e) => console.log ( e.target )
59
+ fn : (e) => e.target
60
60
  , type : 'input'
61
61
  , mode : 'in'
62
62
  }
@@ -66,7 +66,7 @@ const contextDefinition = {
66
66
 
67
67
 
68
68
 
69
- let short = shortcuts ();
69
+ const short = shortcuts ();
70
70
 
71
71
 
72
72
 
@@ -74,7 +74,7 @@ describe ( "Shortcuts", () => {
74
74
 
75
75
  beforeEach ( async () => {
76
76
  short.load ( contextDefinition )
77
- let container = document.createElement ( 'div' )
77
+ const container = document.createElement ( 'div' )
78
78
  container.id = 'app'
79
79
  document.body.appendChild ( container )
80
80
  await html.publish ( Block, {}, 'app' )
@@ -119,42 +119,105 @@ describe ( "Shortcuts", () => {
119
119
  it ( 'List enabled plugins. Disable and enable plugins', async () => {
120
120
  expect ( short.listPlugins () ).to.be.deep.equal ( [] )
121
121
  // Enable list of plugins
122
- let myPlugins = [ pluginKey, pluginClick ]
122
+ const myPlugins = [ pluginKey, pluginClick ]
123
123
  myPlugins.forEach ( plugin => short.enablePlugin ( plugin ) )
124
124
  expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
125
125
  // Method disablePlugin require plugin name (prefix)
126
126
  short.disablePlugin ( 'click' )
127
127
  expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key' ] )
128
- // Method enablePlugin require the plugin as a function
129
- short.enablePlugin ( pluginClick )
130
- expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
131
- }) // it list enabled plugins
128
+ // Method enablePlugin require the plugin as a function
129
+ short.enablePlugin ( pluginClick )
130
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
131
+
132
+ // Try to enable non-function plugins - should do nothing
133
+ short.enablePlugin ( 'not a function' )
134
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
135
+ short.enablePlugin ( 123 )
136
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
137
+ short.enablePlugin ( {} )
138
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
139
+
140
+ // Try to disable a plugin that is not enabled - should do nothing
141
+ short.disablePlugin ( 'scroll' )
142
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
143
+ }) // it list enabled plugins
132
144
 
133
145
 
134
146
 
135
147
  it ( 'Unload non existing context', () => {
136
148
  let change = false;
137
- let ls = short.listContexts ()
149
+ const ls = short.listContexts ()
138
150
  short.load ( {
139
151
  local : {
140
- 'click : leff-1' : () => console.log ( 'nothing' ),
152
+ 'click : leff-1' : () => 'nothing',
141
153
  '@shortcuts-error': () => change = true
142
154
  }
143
155
  })
144
156
  short.changeContext ( 'local' )
145
157
  short.unload ( 'unknown' )
146
158
  expect ( change ).to.be.true
147
- }) // it unload non existing context
148
-
149
-
150
-
151
- it ( 'Emit custom event', () => {
152
- // TODO: Check arguments for the custom event handlers
159
+ }) // it unload non existing context
160
+
161
+
162
+
163
+ it ( 'Change to same context', () => {
164
+ short.changeContext ( 'general' )
165
+ expect ( short.getContext() ).to.be.equal ( 'general' )
166
+ // Changing to the same context should do nothing
167
+ short.changeContext ( 'general' )
168
+ expect ( short.getContext() ).to.be.equal ( 'general' )
169
+ // No error should be emitted
170
+ expect ( c ).to.be.null
171
+ }) // it change to same context
172
+
173
+
174
+
175
+ it ( 'Switch off all shortcuts', () => {
176
+ short.enablePlugin ( pluginKey )
177
+ short.changeContext ( 'general' )
178
+ expect ( short.getContext() ).to.be.equal ( 'general' )
179
+
180
+ // Switch off all shortcuts
181
+ short.changeContext ( false )
182
+ expect ( short.getContext() ).to.be.null
183
+
184
+ // Try to trigger a shortcut - should not work
185
+ // But since it's key plugin, hard to test without DOM events
186
+ // Just check context is null
187
+ expect ( short.getContext() ).to.be.null
188
+ }) // it switch off all shortcuts
189
+
190
+
191
+
192
+ it ( 'Set note with invalid types', () => {
193
+ short.changeContext ( 'general' )
194
+ // Set valid note
195
+ short.setNote ( 'valid note' )
196
+ expect ( short.getNote() ).to.be.equal ( 'valid note' )
197
+
198
+ // Try invalid types - should not change
199
+ short.setNote ( 123 )
200
+ expect ( short.getNote() ).to.be.equal ( 'valid note' )
201
+
202
+ short.setNote ( {} )
203
+ expect ( short.getNote() ).to.be.equal ( 'valid note' )
204
+
205
+ short.setNote ( [] )
206
+ expect ( short.getNote() ).to.be.equal ( 'valid note' )
207
+
208
+ // Valid null should work
209
+ short.setNote ( null )
210
+ expect ( short.getNote() ).to.be.null
211
+ }) // it set note with invalid types
212
+
213
+
214
+
215
+ it ( 'Emit custom event', () => {
153
216
  let result = null;
154
217
  short.enablePlugin ( pluginClick )
155
218
  const myAllContext = {
156
219
  myAll: {
157
- 'click : leff-1' : () => console.log ( 'nothing' )
220
+ 'click : leff-1' : () => 'nothing'
158
221
  , 'yo' : ({msg}) => result = msg
159
222
  }}
160
223
  short.load ( myAllContext )
@@ -170,15 +233,21 @@ describe ( "Shortcuts", () => {
170
233
  it ( 'List shortcuts', () => {
171
234
  short.enablePlugin ( pluginKey )
172
235
 
173
- let general = short.listShortcuts ('general');
236
+ const general = short.listShortcuts ('general');
174
237
  expect ( general ).to.be.an ( 'array' )
175
238
  expect ( general ).to.have.lengthOf ( 2 )
176
239
  expect ( general ).to.include ( 'KEY:A+SHIFT' )
177
240
 
178
- let fail = short.listShortcuts ( 'somethingNotExisting' );
179
- expect ( fail ).to.be.null
180
-
181
- let all = short.listShortcuts ();
241
+ const fail = short.listShortcuts ( 'somethingNotExisting' );
242
+ expect ( fail ).to.be.null
243
+
244
+ // Edge cases for invalid context types
245
+ expect ( short.listShortcuts ( 123 ) ).to.be.null
246
+ expect ( short.listShortcuts ( {} ) ).to.be.null
247
+ expect ( short.listShortcuts ( [] ) ).to.be.null
248
+ expect ( short.listShortcuts ( undefined ) ).to.be.an ( 'array' ) // undefined == null, so lists all
249
+
250
+ const all = short.listShortcuts ();
182
251
  expect ( all ).to.be.an ( 'array' )
183
252
 
184
253
  expect ( all ).to.have.lengthOf ( 4 )
@@ -1,5 +1,5 @@
1
1
  import { beforeEach, afterEach, describe, it, expect } from 'vitest'
2
- import { userEvent } from '@vitest/browser/context'
2
+ import { userEvent } from 'vitest/browser'
3
3
  import {
4
4
  getByLabelText,
5
5
  getByText,
@@ -30,6 +30,7 @@ let
30
30
  a = false
31
31
  , b = false
32
32
  , c = null
33
+ , container
33
34
  ;
34
35
 
35
36
  const contextDefinition = {
@@ -55,7 +56,7 @@ const contextDefinition = {
55
56
  , 'form : define' : () => 'input'
56
57
  , 'form : action' : () => [
57
58
  {
58
- fn : (e) => console.log ( e.target )
59
+ fn : (e) => e.target
59
60
  , type : 'input'
60
61
  , mode : 'in'
61
62
  }
@@ -63,7 +64,7 @@ const contextDefinition = {
63
64
  }
64
65
  }
65
66
 
66
- let short = shortcuts ();
67
+ const short = shortcuts ();
67
68
 
68
69
 
69
70
 
@@ -71,26 +72,30 @@ let short = shortcuts ();
71
72
 
72
73
  describe ( 'Key plugin', () => {
73
74
 
74
- beforeEach ( async () => {
75
- short.load ( contextDefinition )
76
- let container = document.createElement ( 'div' )
77
- container.id = 'app'
78
- document.body.appendChild ( container )
79
- await html.publish ( Block, {}, 'app' )
80
- a = false, b = false
81
- }) // beforeEach
75
+ beforeEach ( async () => {
76
+ short.load ( contextDefinition )
77
+ container = document.createElement ( 'div' )
78
+ container.id = 'app'
79
+ document.body.appendChild ( container )
80
+ await html.publish ( Block, {}, 'app' )
81
+ a = false, b = false
82
+ }) // beforeEach
82
83
 
83
84
 
84
85
 
85
- afterEach ( async () => {
86
- short.reset ()
87
- a = false, b = false, c = null;
88
- }) // afterEach
86
+ afterEach ( async () => {
87
+ short.reset ()
88
+ short.disablePlugin ( 'key' )
89
+ if (container && document.body.contains(container)) {
90
+ document.body.removeChild(container);
91
+ }
92
+ a = false, b = false, c = null;
93
+ }) // afterEach
89
94
 
90
95
 
91
96
 
92
97
  it ( 'No "key" plugin installed', () => {
93
- let r = short.listShortcuts ('general');
98
+ const r = short.listShortcuts ('general');
94
99
  // Shortcut name is the same as it was set
95
100
  expect ( r[0]).to.equal ( ' key : shift+a' )
96
101
  }) // it no 'key' plugin installed
@@ -146,7 +151,7 @@ describe ( 'Key plugin', () => {
146
151
  short.load ({
147
152
  'local': {
148
153
  'key: x,y,z' : ({ dependencies }) => {
149
- let { result } = dependencies;
154
+ const { result } = dependencies;
150
155
  result.push ( i++ )
151
156
  }
152
157
  }
@@ -156,6 +161,7 @@ describe ( 'Key plugin', () => {
156
161
 
157
162
  // Test 1: Plugin should work normally
158
163
  await userEvent.keyboard ( 'xyz' )
164
+ await wait ( 480 )
159
165
  await waitFor ( () => {
160
166
  // We checking if the shortcut works
161
167
  expect ( result ).to.have.lengthOf ( 2 )
@@ -171,9 +177,11 @@ describe ( 'Key plugin', () => {
171
177
  expect ( i ).to.equal ( 1 )
172
178
  }, { timeout: 1000, interval: 12 })
173
179
 
180
+
174
181
  // Test 3: Unmute plugin - should work again
175
182
  short.unmutePlugin ( 'key' )
176
183
  await userEvent.keyboard ( 'xyz' )
184
+ await wait ( 480 )
177
185
  await waitFor ( () => {
178
186
  // Plugin is unmuted, should work again
179
187
  expect ( result ).to.have.lengthOf ( 3 )
@@ -199,7 +207,7 @@ describe ( 'Key plugin', () => {
199
207
  * // Body of the handler. Do something...
200
208
  * }
201
209
  */
202
- let test = [];
210
+ const test = [];
203
211
  let i = 0;
204
212
  short.enablePlugin ( pluginKey )
205
213
  short.setDependencies ({ test })
@@ -237,7 +245,7 @@ describe ( 'Key plugin', () => {
237
245
  await wait ( 50 ) // Wait for key processing
238
246
  await waitFor ( () => {
239
247
  expect ( i ).to.be.equal ( 1 )
240
- let result = test[0];
248
+ const result = test[0];
241
249
  expect ( result.wait ).to.be.equal ( 'function' )
242
250
  expect ( result.end ).to.be.equal ( 'function' )
243
251
  expect ( result.ignore ).to.be.equal ( 'function' )
@@ -245,28 +253,221 @@ describe ( 'Key plugin', () => {
245
253
  expect ( result.context ).to.be.equal ( 'local' )
246
254
  expect ( result.type ).to.be.equal ( 'key' )
247
255
  }, { timeout: 1000, interval: 12 })
248
- }) // it arguments of key handler
249
-
250
-
251
- it ( 'Pause and resume', async () => {
252
- short.enablePlugin ( pluginKey )
253
- expect ( b ).to.be.equal ( false )
254
- short.changeContext ( 'extra' )
255
- // Shortcut name will be normalized by the plugin
256
- short.pause ( 'key : p,r,o,b,a' )
257
- // Execute key sequence: 'p,r,o,b,a'
258
- await userEvent.keyboard ( 'proba' )
259
- await wait ( 500 )
260
- await waitFor ( () => {
261
- expect ( b ).to.be.equal ( false )
256
+ }) // it arguments of key handler
257
+
258
+
259
+
260
+ it ( 'Pause and resume', async () => {
261
+ short.enablePlugin ( pluginKey )
262
+ expect ( b ).to.be.equal ( false )
263
+ short.changeContext ( 'extra' )
264
+ // Shortcut name will be normalized by the plugin
265
+ short.pause ( 'key : p,r,o,b,a' )
266
+ // Execute key sequence: 'p,r,o,b,a'
267
+ await userEvent.keyboard ( 'proba' )
268
+ await wait ( 500 )
269
+ await waitFor ( () => {
270
+ expect ( b ).to.be.equal ( false )
271
+ }, { timeout: 1000, interval: 30 })
272
+
273
+ short.resume ( 'key : p,r,o,b,a' )
274
+ await userEvent.keyboard ( 'proba' )
275
+ await wait ( 500 )
276
+ await waitFor ( () => {
277
+ expect ( b ).to.be.equal ( true )
262
278
  }, { timeout: 1000, interval: 30 })
263
-
264
- short.resume ( 'key : p,r,o,b,a' )
265
- await userEvent.keyboard ( 'proba' )
266
- await wait ( 500 )
267
- await waitFor ( () => {
268
- expect ( b ).to.be.equal ( true )
269
- }, { timeout: 1000, interval: 30 })
270
279
  }) // it pause and resume
271
280
 
281
+
282
+
283
+ it ( 'Wait and ignore in key sequence', async () => {
284
+ const emitted = [];
285
+ short.setDependencies ({ emitted })
286
+ short.load ({
287
+ 'waittest' : {
288
+ 'key: a' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
289
+ if ( !isWaiting () ) {
290
+ // Switch sequence timer off and wait for edit the sequence
291
+ wait ()
292
+ ignore ()
293
+ return
294
+ }
295
+ // Sequence was ended. Proceed
296
+ dependencies.emitted.push ( 'a' )
297
+ end ()
298
+ },
299
+ 'key: b' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
300
+ dependencies.emitted.push ( 'b' )
301
+ },
302
+ 'key: r' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
303
+ // Ignore 'r' in the sequence
304
+ ignore ()
305
+ },
306
+ 'key: b,a' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
307
+ dependencies.emitted.push ( 'b,a' )
308
+ },
309
+ 'key: esc' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
310
+ // Ignore 'escape in the sequence'
311
+ ignore ()
312
+ }
313
+ }
314
+ })
315
+
316
+ short.enablePlugin ( pluginKey )
317
+ short.changeContext ( 'waittest' )
318
+ // Press 'a' - should call handler, set wait and ignore
319
+ await userEvent.keyboard ( 'a' )
320
+ await wait ( 500 )
321
+
322
+ await userEvent.keyboard ('{Escape}')
323
+ // Then press 'b' - since waiting, should emit 'b'
324
+ await userEvent.keyboard ( 'b' )
325
+ // Then press 'r' - will ignore 'r' in sequence because of use of 'ignore' in a handler
326
+ await userEvent.keyboard ( 'r' )
327
+ await userEvent.keyboard ( 'a' )
328
+ await waitFor ( () => {
329
+ expect ( emitted ).to.deep.equal ( [ 'b', 'a', 'b,a'] )
330
+ }, { timeout: 1000, interval: 12 })
331
+ }) // it wait and ignore in key sequence
332
+
333
+
334
+
335
+ it ( 'Ignore keys after sequence', async () => {
336
+ const emitted = [];
337
+ short.setDependencies ({ emitted })
338
+ short.load ({
339
+ 'waittest' : {
340
+ 'key: a,b,esc' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
341
+ dependencies.emitted.push ( 'a,b,esc' )
342
+ },
343
+ 'key: r' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
344
+ dependencies.emitted.push ( 'r' )
345
+ },
346
+ 'key : s' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
347
+ dependencies.emitted.push ( 's' )
348
+ },
349
+ 'key: esc' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
350
+ dependencies.emitted.push ( 'esc' )
351
+ }
352
+ }
353
+ })
354
+
355
+
356
+ short.enablePlugin ( pluginKey )
357
+ short.changeContext ( 'waittest' )
358
+
359
+ await userEvent.keyboard ( 'a' )
360
+ await userEvent.keyboard ( 'b' )
361
+ await userEvent.keyboard ( '{Escape}' )
362
+
363
+ // Will be ignored
364
+ await userEvent.keyboard ( 'r' )
365
+ await userEvent.keyboard ( 'r' )
366
+ await userEvent.keyboard ( '{Escape}' )
367
+
368
+ // Wait above keyWait time to activate sequence again
369
+ await wait ( 500 )
370
+ // New sequence - will emit 'key: s'
371
+ await userEvent.keyboard ( 's' )
372
+ await wait ( 500 )
373
+ await waitFor ( () => {
374
+ expect ( emitted ).to.deep.equal ( [ 'a,b,esc', 's' ] )
375
+ }, { timeout: 1000, interval: 12 })
376
+ }) // it ignore keys after sequence
377
+
378
+
379
+
380
+ it ( 'Stop a plugin durring a sequence', async () => {
381
+ const emitted = [];
382
+ short.setDependencies ({ emitted })
383
+ short.load ({
384
+ 'waittest' : {
385
+ 'key: a,b,esc' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
386
+ dependencies.emitted.push ( 'a,b,esc' )
387
+ },
388
+ 'key: r' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
389
+ dependencies.emitted.push ( 'r' )
390
+ },
391
+ 'key : s' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
392
+ dependencies.emitted.push ( 's' )
393
+ },
394
+ 'key: esc' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
395
+ dependencies.emitted.push ( 'esc' )
396
+ }
397
+ }
398
+ })
399
+
400
+
401
+ short.enablePlugin ( pluginKey )
402
+ short.changeContext ( 'waittest' )
403
+
404
+ await userEvent.keyboard ( 'a' )
405
+ await userEvent.keyboard ( 'b' )
406
+ short.disablePlugin ( 'key' )
407
+ await userEvent.keyboard ( '{Escape}' )
408
+ expect ( emitted ).to.deep.equal ( [] )
409
+ }) // it stop a plugin durring a sequence
410
+
411
+
412
+
413
+ it ( 'Key setup event', async () => {
414
+ const emitted = [];
415
+ short.setDependencies ({ emitted })
416
+ short.load ({
417
+ 'local' : {
418
+ 'key: a' : ({ wait, ignore, dependencies, isWaiting, type, end }) => {
419
+ dependencies.emitted.push ( 'a' )
420
+ }
421
+ , 'key: setup' : ({ dependencies, defaults }) => {
422
+ dependencies.emitted.push ( 'setup' )
423
+ expect ( defaults.keyWait ).to.equal ( 480 )
424
+ // Setup should return an object with required param changes
425
+ // It's not possible to test...
426
+ return { keyWait: 100 }
427
+ }
428
+ }
429
+ })
430
+
431
+ short.enablePlugin ( pluginKey )
432
+ short.changeContext ( 'local' )
433
+ const start = performance.now ()
434
+ await userEvent.keyboard ( 'a' )
435
+ await wait ( 150 )
436
+ await userEvent.keyboard ( 'a' )
437
+ await waitFor ( () => {
438
+ const end = performance.now ()
439
+ expect ( end - start ).to.be.lessThan ( 200 )
440
+ expect ( emitted ).to.deep.equal ( [ 'setup', 'a', 'a' ] )
441
+ }, { timeout: 1000, interval: 12 })
442
+ }) // it key setup
443
+
444
+
445
+
446
+ it ( 'Extra parameters to plugin options', async () => {
447
+ short.enablePlugin ( pluginKey )
448
+ const emit = [];
449
+ const setupContext = {
450
+ 'key:setup' : () => {
451
+ emit.push ( 'setup' )
452
+ return { keyWait: 100, emit }
453
+ },
454
+ 'key:a' : ({options}) => {
455
+ expect ( options.keyWait ).to.equal ( 100 )
456
+ options.emit.push ( 'a' )
457
+ }
458
+ } // setupContext
459
+
460
+ short.load ({ setupContext })
461
+ short.changeContext ( 'setupContext' )
462
+
463
+ // Setup event execution is on change context:
464
+ expect ( emit[0] ).to.equal ( 'setup' )
465
+
466
+ // Click and measure time
467
+ await userEvent.keyboard ( 'a')
468
+ await waitFor ( () => {
469
+ expect ( emit ).to.deep.equal ( [ 'setup', 'a' ] )
470
+ }, { timeout: 1000, interval: 12 })
471
+ }) // it extra parameters to plugin options
472
+
272
473
  })