apostrophe 3.33.0 → 3.35.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 (59) hide show
  1. package/.github/workflows/main.yml +3 -0
  2. package/CHANGELOG.md +31 -0
  3. package/defaults.js +2 -1
  4. package/index.js +2 -1
  5. package/modules/@apostrophecms/admin-bar/index.js +74 -0
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +16 -6
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +24 -1
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +15 -0
  9. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextUndoRedo.vue +20 -18
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +22 -1
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +35 -0
  12. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +6 -1
  13. package/modules/@apostrophecms/asset/lib/globalIcons.js +41 -17
  14. package/modules/@apostrophecms/command-menu/index.js +375 -0
  15. package/modules/@apostrophecms/command-menu/ui/apos/apps/AposCommandMenu.js +34 -0
  16. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +94 -0
  17. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKeyList.vue +106 -0
  18. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +223 -0
  19. package/modules/@apostrophecms/command-menu/ui/apos/components/TheAposCommandMenu.vue +116 -0
  20. package/modules/@apostrophecms/doc/index.js +9 -0
  21. package/modules/@apostrophecms/doc-type/index.js +117 -1
  22. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +7 -0
  23. package/modules/@apostrophecms/i18n/i18n/de.json +446 -0
  24. package/modules/@apostrophecms/i18n/i18n/en.json +27 -0
  25. package/modules/@apostrophecms/i18n/i18n/es.json +19 -0
  26. package/modules/@apostrophecms/i18n/i18n/fr.json +19 -0
  27. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +19 -0
  28. package/modules/@apostrophecms/i18n/i18n/sk.json +19 -0
  29. package/modules/@apostrophecms/image/index.js +7 -0
  30. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +2 -0
  31. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +11 -0
  32. package/modules/@apostrophecms/login/index.js +1 -1
  33. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +10 -1
  34. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +38 -3
  35. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +32 -2
  36. package/modules/@apostrophecms/page/index.js +43 -1
  37. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +4 -0
  38. package/modules/@apostrophecms/piece-type/index.js +145 -20
  39. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +5 -1
  40. package/modules/@apostrophecms/rich-text-widget/index.js +153 -5
  41. package/modules/@apostrophecms/rich-text-widget/ui/apos/apps/AposRichTextPermalinkResolver.js +28 -0
  42. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +88 -14
  43. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapAnchor.vue +253 -0
  44. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +134 -24
  45. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Anchor.js +59 -0
  46. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +12 -4
  47. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/ListItem.js +6 -0
  48. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +17 -0
  49. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +4 -2
  50. package/modules/@apostrophecms/search/index.js +27 -28
  51. package/modules/@apostrophecms/template/views/outerLayoutBase.html +1 -0
  52. package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +1 -1
  53. package/modules/@apostrophecms/user/index.js +24 -8
  54. package/modules/@apostrophecms/util/index.js +13 -0
  55. package/package.json +2 -2
  56. package/test/command-menu.js +877 -0
  57. package/test/concurrent-array-relationships.js +0 -1
  58. package/test/users.js +21 -0
  59. package/test/utils/commands.js +204 -0
@@ -0,0 +1,877 @@
1
+ const assert = require('assert').strict;
2
+ const t = require('../test-lib/test.js');
3
+ const {
4
+ moduleA,
5
+ moduleB,
6
+ moduleC,
7
+ moduleD,
8
+ article,
9
+ topic
10
+ } = require('./utils/commands.js');
11
+
12
+ describe('Command-Menu', function() {
13
+ this.timeout(t.timeout);
14
+
15
+ let apos;
16
+
17
+ before(async function() {
18
+ apos = await t.create({
19
+ root: module,
20
+ modules: {
21
+ 'module-a': moduleA,
22
+ 'module-b': moduleB,
23
+ 'module-c': moduleC,
24
+ 'module-d': moduleD
25
+ }
26
+ });
27
+ });
28
+
29
+ after(function() {
30
+ return t.destroy(apos);
31
+ });
32
+
33
+ it('should compose removes', function() {
34
+ const initialState = {
35
+ definitions: [
36
+ [ moduleA.commands ],
37
+ [ moduleB.commands ],
38
+ [ moduleC.commands ],
39
+ [ {
40
+ ...moduleD.commands,
41
+ remove: [
42
+ 'toggle-publish-draft-mode'
43
+ ]
44
+ } ],
45
+ [ {
46
+ remove: [
47
+ 'redo',
48
+ 'command-menu'
49
+ ]
50
+ } ]
51
+ ]
52
+ };
53
+
54
+ const actual = apos.commandMenu.composeRemoves(initialState);
55
+ const expected = {
56
+ ...initialState,
57
+ removes: [
58
+ '@apostrophecms/command-menu:test',
59
+ 'toggle-publish-draft-mode',
60
+ 'redo',
61
+ 'command-menu'
62
+ ]
63
+ };
64
+
65
+ assert.deepEqual(actual, expected);
66
+ });
67
+
68
+ it('should compose removes sequentially', function() {
69
+ const initialState = {
70
+ definitions: [
71
+ [
72
+ {
73
+ add: {
74
+ '@apostrophecms/command-menu:test': {
75
+ type: 'item',
76
+ label: 'commandMenuShortcutTest',
77
+ action: {
78
+ type: 'test',
79
+ payload: {}
80
+ },
81
+ shortcut: 'Shift+G'
82
+ }
83
+ }
84
+ },
85
+ {
86
+ remove: [
87
+ '@apostrophecms/command-menu:test'
88
+ ]
89
+ }
90
+ ],
91
+ [ {
92
+ add: {
93
+ '@apostrophecms/command-menu:test': {
94
+ type: 'item',
95
+ label: 'commandMenuShortcutTest',
96
+ action: {
97
+ type: 'test',
98
+ payload: {}
99
+ },
100
+ shortcut: 'Shift+G'
101
+ }
102
+ }
103
+ } ]
104
+ ]
105
+ };
106
+
107
+ const actual = apos.commandMenu.composeRemoves(initialState);
108
+ const expected = {
109
+ ...initialState,
110
+ removes: []
111
+ };
112
+
113
+ assert.deepEqual(actual, expected);
114
+ });
115
+
116
+ it('should compose commands', function() {
117
+ const initialState = {
118
+ definitions: [
119
+ [ moduleA.commands ],
120
+ [ moduleB.commands ],
121
+ [ moduleC.commands ],
122
+ [ moduleD.commands ]
123
+ ]
124
+ };
125
+
126
+ const actual = apos.commandMenu.composeCommands(initialState);
127
+ const expected = {
128
+ ...initialState,
129
+ commands: {
130
+ '@apostrophecms/command-menu:toggle-shortcuts': {
131
+ action: {
132
+ payload: {},
133
+ type: 'toggle-shortcuts'
134
+ },
135
+ label: 'commandMenuShortcutToggleShortcuts',
136
+ shortcut: '?',
137
+ type: 'item'
138
+ },
139
+ '@apostrophecms/command-menu:test': {
140
+ type: 'item',
141
+ label: 'commandMenuShortcutTest',
142
+ action: {
143
+ type: 'test',
144
+ payload: {}
145
+ },
146
+ shortcut: 'Shift+G'
147
+ }
148
+ }
149
+ };
150
+
151
+ assert.deepEqual(actual, expected);
152
+ });
153
+
154
+ it('should compose groups', function() {
155
+ const initialState = {
156
+ definitions: [
157
+ [ moduleA.commands ],
158
+ [ moduleB.commands ],
159
+ [ moduleC.commands ],
160
+ [ moduleD.commands ]
161
+ ]
162
+ };
163
+
164
+ const actual = apos.commandMenu.composeGroups(initialState);
165
+ const expected = {
166
+ ...initialState,
167
+ groups: {
168
+ '@apostrophecms/command-menu:general': {
169
+ label: 'commandMenuGeneral',
170
+ commands: [
171
+ '@apostrophecms/command-menu:toggle-shortcuts',
172
+ 'command-menu'
173
+ ]
174
+ }
175
+ }
176
+ };
177
+
178
+ assert.deepEqual(actual, expected);
179
+ });
180
+
181
+ it('should compose groups and remove duplicate commands', function() {
182
+ const initialState = {
183
+ definitions: [
184
+ [ moduleA.commands ],
185
+ [ moduleB.commands ],
186
+ [ moduleC.commands ],
187
+ [ moduleD.commands ],
188
+ [ {
189
+ add: {
190
+ '@apostrophecms/command-menu:help': {
191
+ shortcut: '?'
192
+ }
193
+ },
194
+ group: {
195
+ '@apostrophecms/command-menu:help': {
196
+ label: 'commandMenuHelp',
197
+ commands: [
198
+ '@apostrophecms/command-menu:toggle-shortcuts',
199
+ '@apostrophecms/command-menu:help'
200
+ ]
201
+ }
202
+ }
203
+ } ]
204
+ ]
205
+ };
206
+
207
+ const actual = apos.commandMenu.composeGroups(initialState);
208
+ const expected = {
209
+ ...initialState,
210
+ groups: {
211
+ '@apostrophecms/command-menu:general': {
212
+ label: 'commandMenuGeneral',
213
+ commands: [
214
+ 'command-menu'
215
+ ]
216
+ },
217
+ '@apostrophecms/command-menu:help': {
218
+ label: 'commandMenuHelp',
219
+ commands: [
220
+ '@apostrophecms/command-menu:toggle-shortcuts',
221
+ '@apostrophecms/command-menu:help'
222
+ ]
223
+ }
224
+ }
225
+ };
226
+
227
+ assert.deepEqual(actual, expected);
228
+ });
229
+
230
+ it('should compose modals', function() {
231
+ const initialState = {
232
+ definitions: [
233
+ [ moduleA.commands ],
234
+ [ moduleB.commands ],
235
+ [ moduleC.commands ],
236
+ [ moduleD.commands ],
237
+ [ article.commands ],
238
+ [ topic.commands ]
239
+ ]
240
+ };
241
+
242
+ const actual = apos.commandMenu.composeModals(initialState);
243
+ const expected = {
244
+ ...initialState,
245
+ modals: {
246
+ default: {
247
+ '@apostrophecms/command-menu:content': {
248
+ label: 'commandMenuContent',
249
+ commands: [
250
+ '@apostrophecms/command-menu:undo',
251
+ '@apostrophecms/command-menu:redo',
252
+ '@apostrophecms/command-menu:toggle-shortcuts',
253
+ '@apostrophecms/command-menu:discard-draft',
254
+ '@apostrophecms/command-menu:publish-draft',
255
+ '@apostrophecms/command-menu:test'
256
+ ]
257
+ },
258
+ '@apostrophecms/command-menu:modes': {
259
+ label: 'commandMenuModes',
260
+ commands: [
261
+ '@apostrophecms/command-menu:toggle-edit-preview-mode',
262
+ '@apostrophecms/command-menu:toggle-publish-draft-mode'
263
+ ]
264
+ }
265
+ },
266
+ 'article:manager': {
267
+ '@apostrophecms/command-menu:manager': {
268
+ label: null,
269
+ commands: [
270
+ 'article:create-new',
271
+ 'article:search',
272
+ 'article:select-all',
273
+ 'article:archive-selected',
274
+ 'article:exit-manager'
275
+ ]
276
+ }
277
+ },
278
+ 'topic:manager': {
279
+ '@apostrophecms/command-menu:manager': {
280
+ label: null,
281
+ commands: [
282
+ 'topic:create-new',
283
+ 'topic:search',
284
+ 'topic:select-all',
285
+ 'topic:archive-selected',
286
+ 'topic:exit-manager'
287
+ ]
288
+ }
289
+ }
290
+ }
291
+ };
292
+
293
+ assert.deepEqual(actual, expected);
294
+ });
295
+
296
+ it('should validate commands', function() {
297
+ const initialState = apos.util.pipe(
298
+ apos.commandMenu.composeRemoves,
299
+ apos.commandMenu.composeCommands,
300
+ apos.commandMenu.composeGroups,
301
+ apos.commandMenu.composeModals
302
+ )({
303
+ definitions: [
304
+ [ moduleA.commands ],
305
+ [ moduleB.commands ],
306
+ [ moduleC.commands ],
307
+ [ moduleD.commands ]
308
+ ]
309
+ });
310
+
311
+ const actual = Object.entries(initialState.commands)
312
+ .map(([ name, command ]) => apos.commandMenu.validateCommand({
313
+ name,
314
+ command
315
+ }));
316
+ const expected = [
317
+ [ true, null ],
318
+ [ true, null ]
319
+ ];
320
+
321
+ assert.deepEqual(actual, expected);
322
+ });
323
+
324
+ it('should validate groups', function() {
325
+ const initialState = apos.util.pipe(
326
+ apos.commandMenu.composeRemoves,
327
+ apos.commandMenu.composeCommands,
328
+ apos.commandMenu.composeGroups,
329
+ apos.commandMenu.composeModals
330
+ )({
331
+ definitions: [
332
+ [ moduleA.commands ],
333
+ [ moduleB.commands ],
334
+ [ moduleC.commands ],
335
+ [ moduleD.commands ]
336
+ ]
337
+ });
338
+
339
+ const actual = Object.entries(initialState.groups)
340
+ .map(([ name, group ]) => apos.commandMenu.validateGroup({
341
+ name,
342
+ group
343
+ }));
344
+ const expected = [
345
+ [ true, null ]
346
+ ];
347
+
348
+ assert.deepEqual(actual, expected);
349
+ });
350
+
351
+ it('should validate modals', function() {
352
+ const initialState = apos.util.pipe(
353
+ apos.commandMenu.composeRemoves,
354
+ apos.commandMenu.composeCommands,
355
+ apos.commandMenu.composeGroups,
356
+ apos.commandMenu.composeModals
357
+ )({
358
+ definitions: [
359
+ [ moduleA.commands ],
360
+ [ moduleB.commands ],
361
+ [ moduleC.commands ],
362
+ [ moduleD.commands ]
363
+ ]
364
+ });
365
+
366
+ const actual = Object.entries(initialState.modals)
367
+ .flatMap(([ name, modal ]) => apos.commandMenu.validateModal({
368
+ name,
369
+ modal
370
+ }));
371
+ const expected = [
372
+ [ true, null ],
373
+ [ true, null ]
374
+ ];
375
+
376
+ assert.deepEqual(actual, expected);
377
+ });
378
+
379
+ it('should compile errors', function() {
380
+ const initialState = apos.util.pipe(
381
+ apos.commandMenu.composeRemoves,
382
+ apos.commandMenu.composeCommands,
383
+ apos.commandMenu.composeGroups,
384
+ apos.commandMenu.composeModals
385
+ )({
386
+ definitions: [
387
+ [ moduleA.commands ],
388
+ [ moduleB.commands ],
389
+ [ moduleC.commands ],
390
+ [ moduleD.commands ],
391
+ [ { add: { command1: {} } } ],
392
+ [ { group: { groupA: {} } } ],
393
+ [ { modal: { modalA: { groupB: {} } } } ]
394
+ ]
395
+ });
396
+
397
+ const result = [].concat(
398
+ Object.entries(initialState.commands)
399
+ .map(([ name, command ]) => apos.commandMenu.validateCommand({
400
+ name,
401
+ command
402
+ })),
403
+ Object.entries(initialState.groups)
404
+ .map(([ name, group ]) => apos.commandMenu.validateGroup({
405
+ name,
406
+ group
407
+ })),
408
+ Object.entries(initialState.modals)
409
+ .flatMap(([ name, modal ]) => apos.commandMenu.validateModal({
410
+ name,
411
+ modal
412
+ }))
413
+ );
414
+
415
+ const actual = () => apos.commandMenu.compileErrors(result);
416
+ const expected = {
417
+ name: 'Error',
418
+ message: 'Invalid',
419
+ cause: [
420
+ new assert.AssertionError({
421
+ message: 'Invalid command type, must be "item", for command1',
422
+ expected: 'item',
423
+ operator: 'strictEqual'
424
+ }),
425
+ new assert.AssertionError({
426
+ message: 'Invalid group label, must be a string, for groupA "undefined" provided',
427
+ actual: 'undefined',
428
+ expected: 'string',
429
+ operator: 'strictEqual'
430
+ }),
431
+ new assert.AssertionError({
432
+ message: 'Invalid group label, must be a string, for modalA:groupB "undefined" provided',
433
+ actual: 'undefined',
434
+ expected: 'string',
435
+ operator: 'strictEqual'
436
+ })
437
+ ]
438
+ };
439
+
440
+ assert.throws(actual, expected);
441
+ });
442
+
443
+ it('should build commands', function() {
444
+ const initialState = {
445
+ composed: apos.util.pipe(
446
+ apos.commandMenu.composeRemoves,
447
+ apos.commandMenu.composeCommands,
448
+ apos.commandMenu.composeGroups,
449
+ apos.commandMenu.composeModals
450
+ )({
451
+ definitions: [
452
+ [ moduleA.commands ],
453
+ [ moduleB.commands ],
454
+ [ moduleC.commands ],
455
+ [ moduleD.commands ]
456
+ ]
457
+ })
458
+ };
459
+
460
+ const actual = apos.commandMenu.buildCommands(initialState);
461
+ const expected = {
462
+ ...initialState,
463
+ commands: {
464
+ '@apostrophecms/command-menu:toggle-shortcuts': {
465
+ action: {
466
+ payload: {},
467
+ type: 'toggle-shortcuts'
468
+ },
469
+ label: 'commandMenuShortcutToggleShortcuts',
470
+ shortcut: '?',
471
+ type: 'item'
472
+ }
473
+ }
474
+ };
475
+
476
+ assert.deepEqual(actual, expected);
477
+ });
478
+
479
+ it('should build groups', function() {
480
+ const initialState = {
481
+ composed: apos.util.pipe(
482
+ apos.commandMenu.composeRemoves,
483
+ apos.commandMenu.composeCommands,
484
+ apos.commandMenu.composeGroups,
485
+ apos.commandMenu.composeModals
486
+ )({
487
+ definitions: [
488
+ [ moduleA.commands ],
489
+ [ moduleB.commands ],
490
+ [ moduleC.commands ],
491
+ [ moduleD.commands ]
492
+ ]
493
+ })
494
+ };
495
+
496
+ const { commands } = apos.commandMenu.buildCommands(initialState);
497
+
498
+ const actual = apos.commandMenu.buildGroups({
499
+ ...initialState,
500
+ commands
501
+ });
502
+ const expected = {
503
+ ...initialState,
504
+ commands,
505
+ groups: {
506
+ '@apostrophecms/command-menu:general': {
507
+ label: 'commandMenuGeneral',
508
+ commands: {
509
+ '@apostrophecms/command-menu:toggle-shortcuts': {
510
+ action: {
511
+ payload: {},
512
+ type: 'toggle-shortcuts'
513
+ },
514
+ label: 'commandMenuShortcutToggleShortcuts',
515
+ shortcut: '?',
516
+ type: 'item'
517
+ }
518
+ }
519
+ }
520
+ }
521
+ };
522
+
523
+ assert.deepEqual(actual, expected);
524
+ });
525
+
526
+ it('should build modals', function() {
527
+ const initialState = {
528
+ composed: apos.util.pipe(
529
+ apos.commandMenu.composeRemoves,
530
+ apos.commandMenu.composeCommands,
531
+ apos.commandMenu.composeGroups,
532
+ apos.commandMenu.composeModals
533
+ )({
534
+ definitions: [
535
+ [ moduleA.commands ],
536
+ [ moduleB.commands ],
537
+ [ moduleC.commands ],
538
+ [ moduleD.commands ],
539
+ [ article.commands ],
540
+ [ topic.commands ]
541
+ ]
542
+ })
543
+ };
544
+
545
+ const {
546
+ commands,
547
+ groups
548
+ } = apos.util.pipe(
549
+ apos.commandMenu.buildCommands,
550
+ apos.commandMenu.buildGroups
551
+ )(initialState);
552
+
553
+ const actual = apos.commandMenu.buildModals({
554
+ ...initialState,
555
+ commands,
556
+ groups
557
+ });
558
+ const expected = {
559
+ ...initialState,
560
+ commands,
561
+ groups,
562
+ modals: {
563
+ default: {
564
+ '@apostrophecms/command-menu:content': {
565
+ label: 'commandMenuContent',
566
+ commands: {
567
+ '@apostrophecms/command-menu:toggle-shortcuts': {
568
+ type: 'item',
569
+ label: 'commandMenuShortcutToggleShortcuts',
570
+ action: {
571
+ type: 'toggle-shortcuts',
572
+ payload: {}
573
+ },
574
+ shortcut: '?'
575
+ }
576
+ }
577
+ }
578
+ },
579
+ 'article:manager': {
580
+ '@apostrophecms/command-menu:manager': {
581
+ label: null,
582
+ commands: {
583
+ 'article:create-new': {
584
+ type: 'item',
585
+ label: 'apostrophe:commandMenuCreateNew',
586
+ action: {},
587
+ shortcut: '',
588
+ modal: 'article:manager'
589
+ },
590
+ 'article:search': {
591
+ type: 'item',
592
+ label: 'apostrophe:commandMenuSearch',
593
+ action: {},
594
+ shortcut: '',
595
+ modal: 'article:manager'
596
+ },
597
+ 'article:select-all': {
598
+ type: 'item',
599
+ label: 'apostrophe:commandMenuSelectAll',
600
+ action: {},
601
+ shortcut: '',
602
+ modal: 'article:manager'
603
+ },
604
+ 'article:archive-selected': {
605
+ type: 'item',
606
+ label: 'apostrophe:commandMenuArchiveSelected',
607
+ action: {},
608
+ shortcut: '',
609
+ modal: 'article:manager'
610
+ },
611
+ 'article:exit-manager': {
612
+ type: 'item',
613
+ label: 'apostrophe:commandMenuExitManager',
614
+ action: {},
615
+ shortcut: '',
616
+ modal: 'article:manager'
617
+ }
618
+ }
619
+ }
620
+ },
621
+ 'topic:manager': {
622
+ '@apostrophecms/command-menu:manager': {
623
+ label: null,
624
+ commands: {
625
+ 'topic:create-new': {
626
+ type: 'item',
627
+ label: 'apostrophe:commandMenuCreateNew',
628
+ action: {},
629
+ shortcut: ''
630
+ },
631
+ 'topic:search': {
632
+ type: 'item',
633
+ label: 'apostrophe:commandMenuSearch',
634
+ action: {},
635
+ shortcut: ''
636
+ },
637
+ 'topic:select-all': {
638
+ type: 'item',
639
+ label: 'apostrophe:commandMenuSelectAll',
640
+ action: {},
641
+ shortcut: ''
642
+ },
643
+ 'topic:archive-selected': {
644
+ type: 'item',
645
+ label: 'apostrophe:commandMenuArchiveSelected',
646
+ action: {},
647
+ shortcut: ''
648
+ },
649
+ 'topic:exit-manager': {
650
+ type: 'item',
651
+ label: 'apostrophe:commandMenuExitManager',
652
+ action: {},
653
+ shortcut: ''
654
+ }
655
+ }
656
+ }
657
+ }
658
+ }
659
+ };
660
+
661
+ assert.deepEqual(actual, expected);
662
+ });
663
+
664
+ it('should tell that the command is visible without command permission', function() {
665
+ const req = apos.task.getReq();
666
+ const command = {
667
+ type: 'item',
668
+ label: 'apostrophe:commandMenuCreateNew',
669
+ action: {},
670
+ shortcut: 'c'
671
+ };
672
+
673
+ const actual = apos.commandMenu.isCommandVisible(req, command);
674
+ const expected = true;
675
+
676
+ assert.equal(actual, expected);
677
+ });
678
+
679
+ it('should tell that the command not visible with correct command permission', function() {
680
+ const req = apos.task.getReq();
681
+ const command = {
682
+ type: 'item',
683
+ label: 'apostrophe:commandMenuCreateNew',
684
+ action: {},
685
+ shortcut: 'c',
686
+ permission: {
687
+ action: 'edit',
688
+ type: '@apostrophecms/page',
689
+ mode: 'draft'
690
+ }
691
+ };
692
+
693
+ const actual = apos.commandMenu.isCommandVisible(req, command);
694
+ const expected = true;
695
+
696
+ assert.equal(actual, expected);
697
+ });
698
+
699
+ it('should tell that the command is not visible with wrong command permission', function() {
700
+ const req = apos.task.getContributorReq();
701
+ const command = {
702
+ type: 'item',
703
+ label: 'apostrophe:commandMenuCreateNew',
704
+ action: {},
705
+ shortcut: 'c',
706
+ permission: {
707
+ action: 'edit',
708
+ type: '@apostrophecms/page',
709
+ mode: 'published'
710
+ }
711
+ };
712
+
713
+ const actual = apos.commandMenu.isCommandVisible(req, command);
714
+ const expected = false;
715
+
716
+ assert.equal(actual, expected);
717
+ });
718
+
719
+ it('should get visible groups', function() {
720
+ const initialState = {
721
+ composed: apos.util.pipe(
722
+ apos.commandMenu.composeRemoves,
723
+ apos.commandMenu.composeCommands,
724
+ apos.commandMenu.composeGroups,
725
+ apos.commandMenu.composeModals
726
+ )({
727
+ definitions: [
728
+ [ moduleA.commands ],
729
+ [ moduleB.commands ],
730
+ [ moduleC.commands ],
731
+ [ moduleD.commands ],
732
+ [ article.commands ],
733
+ [ topic.commands ]
734
+ ]
735
+ })
736
+ };
737
+
738
+ const built = apos.util.pipe(
739
+ apos.commandMenu.buildCommands,
740
+ apos.commandMenu.buildGroups,
741
+ apos.commandMenu.buildModals
742
+ )(initialState);
743
+
744
+ const visibleCommands = [
745
+ '@apostrophecms/command-menu:toggle-shortcuts'
746
+ ];
747
+
748
+ const actual = apos.commandMenu.getVisibleGroups(visibleCommands, built.groups);
749
+ const expected = {
750
+ '@apostrophecms/command-menu:general': {
751
+ label: 'commandMenuGeneral',
752
+ commands: {
753
+ '@apostrophecms/command-menu:toggle-shortcuts': {
754
+ type: 'item',
755
+ label: 'commandMenuShortcutToggleShortcuts',
756
+ action: {
757
+ type: 'toggle-shortcuts',
758
+ payload: {}
759
+ },
760
+ shortcut: '?'
761
+ }
762
+ }
763
+ }
764
+ };
765
+
766
+ assert.deepEqual(actual, expected);
767
+ });
768
+
769
+ it('should get visible modals', function() {
770
+ const initialState = {
771
+ composed: apos.util.pipe(
772
+ apos.commandMenu.composeRemoves,
773
+ apos.commandMenu.composeCommands,
774
+ apos.commandMenu.composeGroups,
775
+ apos.commandMenu.composeModals
776
+ )({
777
+ definitions: [
778
+ [ moduleA.commands ],
779
+ [ moduleB.commands ],
780
+ [ moduleC.commands ],
781
+ [ moduleD.commands ],
782
+ [ article.commands ],
783
+ [ topic.commands ]
784
+ ]
785
+ })
786
+ };
787
+
788
+ const built = apos.util.pipe(
789
+ apos.commandMenu.buildCommands,
790
+ apos.commandMenu.buildGroups,
791
+ apos.commandMenu.buildModals
792
+ )(initialState);
793
+
794
+ const visibleCommands = [
795
+ 'article:search',
796
+ 'article:select-all',
797
+ 'article:archive-selected',
798
+ 'article:exit-manager',
799
+ 'topic:search',
800
+ 'topic:select-all',
801
+ 'topic:archive-selected',
802
+ 'topic:exit-manager'
803
+ ];
804
+
805
+ const actual = apos.commandMenu.getVisibleModals(visibleCommands, built.modals);
806
+ const expected = {
807
+ 'article:manager': {
808
+ '@apostrophecms/command-menu:manager': {
809
+ label: null,
810
+ commands: {
811
+ 'article:search': {
812
+ type: 'item',
813
+ label: 'apostrophe:commandMenuSearch',
814
+ action: {},
815
+ shortcut: '',
816
+ modal: 'article:manager'
817
+ },
818
+ 'article:select-all': {
819
+ type: 'item',
820
+ label: 'apostrophe:commandMenuSelectAll',
821
+ action: {},
822
+ shortcut: '',
823
+ modal: 'article:manager'
824
+ },
825
+ 'article:archive-selected': {
826
+ type: 'item',
827
+ label: 'apostrophe:commandMenuArchiveSelected',
828
+ action: {},
829
+ shortcut: '',
830
+ modal: 'article:manager'
831
+ },
832
+ 'article:exit-manager': {
833
+ type: 'item',
834
+ label: 'apostrophe:commandMenuExitManager',
835
+ action: {},
836
+ shortcut: '',
837
+ modal: 'article:manager'
838
+ }
839
+ }
840
+ }
841
+ },
842
+ 'topic:manager': {
843
+ '@apostrophecms/command-menu:manager': {
844
+ label: null,
845
+ commands: {
846
+ 'topic:search': {
847
+ type: 'item',
848
+ label: 'apostrophe:commandMenuSearch',
849
+ action: {},
850
+ shortcut: ''
851
+ },
852
+ 'topic:select-all': {
853
+ type: 'item',
854
+ label: 'apostrophe:commandMenuSelectAll',
855
+ action: {},
856
+ shortcut: ''
857
+ },
858
+ 'topic:archive-selected': {
859
+ type: 'item',
860
+ label: 'apostrophe:commandMenuArchiveSelected',
861
+ action: {},
862
+ shortcut: ''
863
+ },
864
+ 'topic:exit-manager': {
865
+ type: 'item',
866
+ label: 'apostrophe:commandMenuExitManager',
867
+ action: {},
868
+ shortcut: ''
869
+ }
870
+ }
871
+ }
872
+ }
873
+ };
874
+
875
+ assert.deepEqual(actual, expected);
876
+ });
877
+ });