apostrophe 4.6.1 → 4.7.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 (124) hide show
  1. package/.github/workflows/main.yml +1 -1
  2. package/CHANGELOG.md +39 -1
  3. package/lib/big-upload-client.js +100 -0
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +5 -3
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +6 -3
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarUser.vue +4 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +24 -16
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +1 -0
  9. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposSavingIndicator.vue +7 -5
  10. package/modules/@apostrophecms/area/index.js +5 -2
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +20 -12
  12. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +11 -7
  13. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +20 -12
  14. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenuItem.vue +3 -1
  15. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +15 -11
  16. package/modules/@apostrophecms/attachment/index.js +4 -2
  17. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +28 -24
  18. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +17 -11
  19. package/modules/@apostrophecms/doc/index.js +22 -19
  20. package/modules/@apostrophecms/doc-type/index.js +6 -2
  21. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +9 -5
  22. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocLocalePicker.vue +10 -5
  23. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +12 -0
  24. package/modules/@apostrophecms/http/index.js +19 -3
  25. package/modules/@apostrophecms/http/lib/big-upload-middleware.js +251 -0
  26. package/modules/@apostrophecms/i18n/i18n/de.json +1 -1
  27. package/modules/@apostrophecms/i18n/i18n/en.json +9 -1
  28. package/modules/@apostrophecms/i18n/i18n/es.json +1 -1
  29. package/modules/@apostrophecms/i18n/i18n/fr.json +1 -1
  30. package/modules/@apostrophecms/i18n/i18n/it.json +1 -1
  31. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -1
  32. package/modules/@apostrophecms/i18n/i18n/sk.json +1 -1
  33. package/modules/@apostrophecms/i18n/index.js +3 -0
  34. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +30 -16
  35. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalizeErrors.vue +7 -5
  36. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +5 -1
  37. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +10 -6
  38. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +40 -18
  39. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +35 -25
  40. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +11 -5
  41. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +15 -9
  42. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +39 -31
  43. package/modules/@apostrophecms/job/index.js +1 -1
  44. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +9 -7
  45. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +17 -13
  46. package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +30 -20
  47. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +5 -0
  48. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +4 -1
  49. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +8 -4
  50. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +14 -8
  51. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +32 -22
  52. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +16 -14
  53. package/modules/@apostrophecms/modal/ui/apos/components/AposWidgetModalTabs.vue +16 -14
  54. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +93 -91
  55. package/modules/@apostrophecms/page/index.js +482 -13
  56. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +43 -23
  57. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +248 -156
  58. package/modules/@apostrophecms/permission/ui/apos/components/AposPermissionGrid.vue +9 -5
  59. package/modules/@apostrophecms/piece-type/index.js +7 -7
  60. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +92 -36
  61. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +30 -24
  62. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapDivider.vue +4 -2
  63. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +2 -1
  64. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapMarks.vue +5 -3
  65. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +5 -3
  66. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapTable.vue +5 -2
  67. package/modules/@apostrophecms/schema/index.js +26 -5
  68. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +42 -9
  69. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +4 -2
  70. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +8 -4
  71. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +6 -4
  72. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +5 -3
  73. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +19 -13
  74. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +6 -2
  75. package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +6 -4
  76. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +28 -25
  77. package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +13 -7
  78. package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +11 -6
  79. package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +5 -3
  80. package/modules/@apostrophecms/ui/ui/apos/components/AposAvatar.vue +14 -12
  81. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +14 -11
  82. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +7 -3
  83. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +4 -2
  84. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +23 -17
  85. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +25 -10
  86. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +7 -5
  87. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +10 -8
  88. package/modules/@apostrophecms/ui/ui/apos/components/AposEmptyState.vue +9 -5
  89. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +9 -6
  90. package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
  91. package/modules/@apostrophecms/ui/ui/apos/components/AposLoadingBlock.vue +3 -1
  92. package/modules/@apostrophecms/ui/ui/apos/components/AposLocale.vue +3 -1
  93. package/modules/@apostrophecms/ui/ui/apos/components/AposLocalePicker.vue +11 -9
  94. package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +5 -3
  95. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +4 -2
  96. package/modules/@apostrophecms/ui/ui/apos/components/AposPagerDots.vue +8 -6
  97. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +25 -17
  98. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +5 -9
  99. package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +10 -6
  100. package/modules/@apostrophecms/ui/ui/apos/components/AposTag.vue +9 -7
  101. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +8 -4
  102. package/modules/@apostrophecms/ui/ui/apos/components/AposTagList.vue +4 -2
  103. package/modules/@apostrophecms/ui/ui/apos/components/AposTagListItem.vue +7 -5
  104. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +16 -0
  105. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +3 -1
  106. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +11 -9
  107. package/modules/@apostrophecms/ui/ui/apos/mixins/AposArchiveMixin.js +2 -2
  108. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +6 -6
  109. package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +30 -22
  110. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +22 -18
  111. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tooltips.scss +18 -15
  112. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_input_mixins.scss +8 -6
  113. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_mixins.scss +3 -1
  114. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_theme_mixins.scss +34 -19
  115. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_type_mixins.scss +3 -1
  116. package/modules/@apostrophecms/ui/ui/apos/utils/index.js +140 -51
  117. package/modules/@apostrophecms/widget-type/index.js +3 -2
  118. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +5 -1
  119. package/package.json +5 -6
  120. package/test/big-upload.js +111 -0
  121. package/test/change-doc-ids.js +60 -1
  122. package/test/pages.js +488 -0
  123. package/test/schemas.js +327 -0
  124. package/test/utils.js +266 -5
package/test/schemas.js CHANGED
@@ -326,6 +326,63 @@ describe('Schemas', function() {
326
326
  }
327
327
  };
328
328
  }
329
+ },
330
+ topic: {
331
+ extend: '@apostrophecms/piece-type',
332
+ options: {
333
+ alias: 'topic'
334
+ },
335
+ fields(self) {
336
+ return {
337
+ add: {
338
+ title: {
339
+ label: 'Title',
340
+ type: 'string',
341
+ required: true
342
+ },
343
+ area: {
344
+ label: 'Area',
345
+ type: 'area',
346
+ options: {
347
+ widgets: {
348
+ '@apostrophecms/image': {}
349
+ }
350
+ }
351
+ },
352
+ _rel: {
353
+ label: 'Rel',
354
+ type: 'relationship',
355
+ withType: 'article'
356
+ },
357
+ array: {
358
+ label: 'Array',
359
+ type: 'array',
360
+ fields: {
361
+ add: {
362
+ _arrayRel: {
363
+ label: 'Array Rel',
364
+ type: 'relationship',
365
+ withType: 'article'
366
+ }
367
+ }
368
+ }
369
+ },
370
+ object: {
371
+ label: 'Object',
372
+ type: 'object',
373
+ fields: {
374
+ add: {
375
+ _objectRel: {
376
+ label: 'Object Rel',
377
+ type: 'relationship',
378
+ withType: 'article'
379
+ }
380
+ }
381
+ }
382
+ }
383
+ }
384
+ };
385
+ }
329
386
  }
330
387
  }
331
388
  });
@@ -2401,6 +2458,276 @@ describe('Schemas', function() {
2401
2458
  assert.deepEqual(actual, expected);
2402
2459
  });
2403
2460
 
2461
+ it('should allow to simulate a populate of relationships on database data', async function() {
2462
+ const req = apos.task.getReq();
2463
+ const doc = {
2464
+ _id: 'gvoyi3l0ncsj2yq36743zeum:en:draft',
2465
+ title: 'topic',
2466
+ array: [
2467
+ {
2468
+ _id: 'hy56r2znrs427m47e15yesfb',
2469
+ metaType: 'arrayItem',
2470
+ scopedArrayName: 'doc.topic.array',
2471
+ arrayRelIds: [ 'nkr88dg4lds8noal9l4vrcgv' ],
2472
+ arrayRelFields: { nkr88dg4lds8noal9l4vrcgv: {} }
2473
+ }
2474
+ ],
2475
+ object: {
2476
+ _id: 'tfpq5v6lwlwomuampavh6zi8',
2477
+ metaType: 'object',
2478
+ scopedObjectName: 'doc.topic.object',
2479
+ objectRelIds: [ 'nkr88dg4lds8noal9l4vrcgv', 'az82H8dg4lds8noal9l4vazd' ],
2480
+ objectRelFields: {
2481
+ nkr88dg4lds8noal9l4vrcgv: {},
2482
+ az82H8dg4lds8noal9l4vazd: {}
2483
+ }
2484
+ },
2485
+ slug: 'topic',
2486
+ visibility: 'public',
2487
+ scheduledPublish: null,
2488
+ scheduledUnpublish: null,
2489
+ aposIsTemplate: false,
2490
+ aposPreviewImage: {
2491
+ _id: 'gx4ankjn1s0i2g16qs16is0q',
2492
+ items: [],
2493
+ metaType: 'area'
2494
+ },
2495
+ userPermissions: [],
2496
+ groupPermissions: [],
2497
+ archived: false,
2498
+ type: 'topic',
2499
+ aposLocale: 'en:draft',
2500
+ aposMode: 'draft',
2501
+ aposDocId: 'gvoyi3l0ncsj2yq36743zeum',
2502
+ metaType: 'doc',
2503
+ createdAt: '2024-08-07T07:11:34.234Z',
2504
+ updatedAt: '2024-08-07T07:24:17.889Z',
2505
+ cacheInvalidatedAt: '2024-08-07T07:24:17.889Z',
2506
+ updatedBy: {
2507
+ _id: 'vndh00in6p565asvbivvzjos',
2508
+ title: 'admin',
2509
+ username: 'admin'
2510
+ },
2511
+ titleSortified: 'topic',
2512
+ highSearchText: 'topic topic topic topic public',
2513
+ highSearchWords: [ 'topic', 'public' ],
2514
+ lowSearchText: 'topic topic topic topic public',
2515
+ searchSummary: '',
2516
+ aposPermissions: [],
2517
+ modified: false,
2518
+ lastPublishedAt: '2024-08-07T07:24:17.956Z',
2519
+ relIds: [ 'reyvb1txf54noynckm1xy34a' ],
2520
+ relFields: { reyvb1txf54noynckm1xy34a: {} },
2521
+ area: {
2522
+ _id: 'hfwxext12500kxy74coc3nf1',
2523
+ items: [
2524
+ {
2525
+ _id: 'x38pvsmyqie83vzy57akpwb2',
2526
+ metaType: 'widget',
2527
+ type: '@apostrophecms/image',
2528
+ aposPlaceholder: false,
2529
+ imageIds: [ 'u2gcjvq9wvds1hkd8xibn9h1' ],
2530
+ imageFields: {
2531
+ u2gcjvq9wvds1hkd8xibn9h1: {
2532
+ top: null,
2533
+ left: null,
2534
+ width: null,
2535
+ height: null,
2536
+ x: null,
2537
+ y: null
2538
+ }
2539
+ }
2540
+ }
2541
+ ],
2542
+ metaType: 'area'
2543
+ }
2544
+ };
2545
+
2546
+ apos.schema.simulateRelationshipsFromStorage(req, doc);
2547
+
2548
+ const actual = {
2549
+ rel: doc._rel,
2550
+ area: doc.area.items[0]._image,
2551
+ array: doc.array[0]._arrayRel,
2552
+ object: doc.object._objectRel
2553
+ };
2554
+
2555
+ const expected = {
2556
+ rel: [
2557
+ {
2558
+ _id: 'reyvb1txf54noynckm1xy34a:en:draft',
2559
+ _fields: {}
2560
+ }
2561
+ ],
2562
+ area: [
2563
+ {
2564
+ _id: 'u2gcjvq9wvds1hkd8xibn9h1:en:draft',
2565
+ _fields: {
2566
+ top: null,
2567
+ left: null,
2568
+ width: null,
2569
+ height: null,
2570
+ x: null,
2571
+ y: null
2572
+ }
2573
+ }
2574
+ ],
2575
+ array: [
2576
+ {
2577
+ _id: 'nkr88dg4lds8noal9l4vrcgv:en:draft',
2578
+ _fields: {}
2579
+ }
2580
+ ],
2581
+ object: [
2582
+ {
2583
+ _id: 'nkr88dg4lds8noal9l4vrcgv:en:draft',
2584
+ _fields: {}
2585
+ },
2586
+ {
2587
+ _id: 'az82H8dg4lds8noal9l4vazd:en:draft',
2588
+ _fields: {}
2589
+ }
2590
+ ]
2591
+ };
2592
+
2593
+ assert.deepEqual(actual, expected);
2594
+ });
2595
+
2596
+ it('should allow to convert a document without fetching relationships', async function() {
2597
+ const req = apos.task.getReq();
2598
+ const doc = {
2599
+ _id: 'gvoyi3l0ncsj2yq36743zeum:en:draft',
2600
+ title: 'topic',
2601
+ array: [
2602
+ {
2603
+ _id: 'hy56r2znrs427m47e15yesfb',
2604
+ metaType: 'arrayItem',
2605
+ scopedArrayName: 'doc.topic.array',
2606
+ arrayRelIds: [ 'nkr88dg4lds8noal9l4vrcgv' ],
2607
+ arrayRelFields: { nkr88dg4lds8noal9l4vrcgv: {} }
2608
+ }
2609
+ ],
2610
+ object: {
2611
+ _id: 'tfpq5v6lwlwomuampavh6zi8',
2612
+ metaType: 'object',
2613
+ scopedObjectName: 'doc.topic.object',
2614
+ objectRelIds: [ 'nkr88dg4lds8noal9l4vrcgv', 'az82H8dg4lds8noal9l4vazd' ],
2615
+ objectRelFields: {
2616
+ nkr88dg4lds8noal9l4vrcgv: {},
2617
+ az82H8dg4lds8noal9l4vazd: {}
2618
+ }
2619
+ },
2620
+ slug: 'topic',
2621
+ visibility: 'public',
2622
+ scheduledPublish: null,
2623
+ scheduledUnpublish: null,
2624
+ aposIsTemplate: false,
2625
+ aposPreviewImage: {
2626
+ _id: 'gx4ankjn1s0i2g16qs16is0q',
2627
+ items: [],
2628
+ metaType: 'area'
2629
+ },
2630
+ userPermissions: [],
2631
+ groupPermissions: [],
2632
+ archived: false,
2633
+ type: 'topic',
2634
+ aposLocale: 'en:draft',
2635
+ aposMode: 'draft',
2636
+ aposDocId: 'gvoyi3l0ncsj2yq36743zeum',
2637
+ metaType: 'doc',
2638
+ createdAt: '2024-08-07T07:11:34.234Z',
2639
+ updatedAt: '2024-08-07T07:24:17.889Z',
2640
+ cacheInvalidatedAt: '2024-08-07T07:24:17.889Z',
2641
+ updatedBy: {
2642
+ _id: 'vndh00in6p565asvbivvzjos',
2643
+ title: 'admin',
2644
+ username: 'admin'
2645
+ },
2646
+ titleSortified: 'topic',
2647
+ highSearchText: 'topic topic topic topic public',
2648
+ highSearchWords: [ 'topic', 'public' ],
2649
+ lowSearchText: 'topic topic topic topic public',
2650
+ searchSummary: '',
2651
+ aposPermissions: [],
2652
+ modified: false,
2653
+ lastPublishedAt: '2024-08-07T07:24:17.956Z',
2654
+ relIds: [ 'reyvb1txf54noynckm1xy34a' ],
2655
+ relFields: { reyvb1txf54noynckm1xy34a: {} },
2656
+ area: {
2657
+ _id: 'hfwxext12500kxy74coc3nf1',
2658
+ items: [
2659
+ {
2660
+ _id: 'x38pvsmyqie83vzy57akpwb2',
2661
+ metaType: 'widget',
2662
+ type: '@apostrophecms/image',
2663
+ aposPlaceholder: false,
2664
+ imageIds: [ 'u2gcjvq9wvds1hkd8xibn9h1' ],
2665
+ imageFields: {
2666
+ u2gcjvq9wvds1hkd8xibn9h1: {
2667
+ top: null,
2668
+ left: null,
2669
+ width: null,
2670
+ height: null,
2671
+ x: null,
2672
+ y: null
2673
+ }
2674
+ }
2675
+ }
2676
+ ],
2677
+ metaType: 'area'
2678
+ }
2679
+ };
2680
+
2681
+ apos.schema.simulateRelationshipsFromStorage(req, doc);
2682
+ const docToInsert = doc;
2683
+ const schema = apos.doc.getManager('topic').schema;
2684
+ await apos.schema.convert(req, schema, doc, docToInsert, {
2685
+ fetchRelationships: false
2686
+ });
2687
+
2688
+ const actual = {
2689
+ rel: docToInsert._rel,
2690
+ area: docToInsert.area.items[0]._image,
2691
+ array: docToInsert.array[0]._arrayRel,
2692
+ object: docToInsert.object._objectRel
2693
+ };
2694
+ const expected = {
2695
+ rel: [ {
2696
+ _id: 'reyvb1txf54noynckm1xy34a:en:draft',
2697
+ _fields: {}
2698
+ } ],
2699
+ area: [
2700
+ {
2701
+ _id: 'u2gcjvq9wvds1hkd8xibn9h1:en:draft',
2702
+ _fields: {
2703
+ top: null,
2704
+ left: null,
2705
+ width: null,
2706
+ height: null,
2707
+ x: null,
2708
+ y: null
2709
+ }
2710
+ }
2711
+ ],
2712
+ array: [ {
2713
+ _id: 'nkr88dg4lds8noal9l4vrcgv:en:draft',
2714
+ _fields: {}
2715
+ } ],
2716
+ object: [
2717
+ {
2718
+ _id: 'nkr88dg4lds8noal9l4vrcgv:en:draft',
2719
+ _fields: {}
2720
+ },
2721
+ {
2722
+ _id: 'az82H8dg4lds8noal9l4vazd:en:draft',
2723
+ _fields: {}
2724
+ }
2725
+ ]
2726
+ };
2727
+
2728
+ assert.deepEqual(actual, expected);
2729
+ });
2730
+
2404
2731
  describe('field.readOnly with default value', function() {
2405
2732
  const givenSchema = [
2406
2733
  {
package/test/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const t = require('../test-lib/test.js');
2
2
  const assert = require('assert');
3
3
  const _ = require('lodash');
4
- const { debounce, throttle } = require('../modules/@apostrophecms/ui/ui/apos/utils/index');
4
+ const { debounceAsync, throttle } = require('../modules/@apostrophecms/ui/ui/apos/utils/index');
5
5
 
6
6
  describe('Utils', function() {
7
7
 
@@ -376,10 +376,10 @@ describe('Utils', function() {
376
376
  const calledAsyncSlow = [];
377
377
  let asyncErrCatched = false;
378
378
 
379
- const debouncedNormal = debounce(normalFn, 50);
380
- const debouncedAsync = debounce(asyncFn, 50);
381
- const debouncedAsyncSlow = debounce(asyncSlowFn, 50);
382
- const debouncedAsyncErr = debounce(AsyncErrFn, 50);
379
+ const debouncedNormal = debounceAsync(normalFn, 50);
380
+ const debouncedAsync = debounceAsync(asyncFn, 50);
381
+ const debouncedAsyncSlow = debounceAsync(asyncSlowFn, 50);
382
+ const debouncedAsyncErr = debounceAsync(AsyncErrFn, 50);
383
383
 
384
384
  debouncedNormal(1);
385
385
  debouncedNormal(2);
@@ -441,6 +441,267 @@ describe('Utils', function() {
441
441
 
442
442
  });
443
443
 
444
+ it('should cancel debounced calls (sync)', async function () {
445
+ let calledSync = [];
446
+
447
+ function syncFn(num) {
448
+ calledSync.push(num);
449
+ return 'test';
450
+ };
451
+
452
+ const debouncedSync = debounceAsync(syncFn, 50);
453
+
454
+ debouncedSync(1);
455
+ await wait(200);
456
+ debouncedSync(2);
457
+ debouncedSync(3);
458
+ debouncedSync.cancel();
459
+ assert.deepEqual(calledSync, [ 1 ], 'should cancel all calls after the first call');
460
+ calledSync = [];
461
+
462
+ debouncedSync(1);
463
+ debouncedSync(2);
464
+ debouncedSync(3);
465
+ debouncedSync.cancel();
466
+ await wait(200);
467
+ assert.deepEqual(calledSync, [], 'should cancel all calls when canceled after the 3rd call');
468
+ calledSync = [];
469
+
470
+ debouncedSync(1);
471
+ debouncedSync(2);
472
+ debouncedSync.cancel();
473
+ debouncedSync(3);
474
+ await wait(100);
475
+ assert.deepEqual(calledSync, [], 'should cancel all calls when canceled after the 2nd call');
476
+ calledSync = [];
477
+
478
+ debouncedSync(1);
479
+ debouncedSync(2);
480
+ debouncedSync.cancel();
481
+ await wait(100);
482
+ debouncedSync(3);
483
+ await wait(100);
484
+ assert.deepEqual(
485
+ calledSync,
486
+ [],
487
+ 'should cancel all calls when canceled and called again after some time'
488
+ );
489
+ });
490
+
491
+ it('should cancel debounced calls (async)', async function () {
492
+ let calledAsync = [];
493
+
494
+ async function asyncFn(num) {
495
+ await wait(50);
496
+ calledAsync.push(num);
497
+ // Keep all console.debug around to easy debug - ensure
498
+ // all promises are awaited before the test ends.
499
+ console.debug('calledAsync', num);
500
+ return 'async';
501
+ }
502
+
503
+ const debouncedAsync = debounceAsync(asyncFn, 50);
504
+
505
+ debouncedAsync(1);
506
+ await wait(100);
507
+ debouncedAsync(2);
508
+ debouncedAsync(3);
509
+ debouncedAsync.cancel();
510
+ await wait(200);
511
+ assert.deepEqual(calledAsync, [ 1 ], 'should cancel all calls after the first call');
512
+ calledAsync = [];
513
+
514
+ debouncedAsync(1);
515
+ debouncedAsync(2);
516
+ debouncedAsync(3);
517
+ debouncedAsync.cancel();
518
+ await wait(200);
519
+ assert.deepEqual(calledAsync, [], 'should cancel all calls when canceled after the 3rd call');
520
+ calledAsync = [];
521
+
522
+ debouncedAsync(1);
523
+ debouncedAsync(2);
524
+ debouncedAsync.cancel();
525
+ debouncedAsync(3);
526
+ await wait(200);
527
+ assert.deepEqual(calledAsync, [], 'should cancel all calls when canceled after the 2nd call');
528
+ calledAsync = [];
529
+
530
+ debouncedAsync(1);
531
+ debouncedAsync(2);
532
+ debouncedAsync.cancel();
533
+ await wait(200);
534
+ debouncedAsync(3);
535
+ await wait(100);
536
+ assert.deepEqual(
537
+ calledAsync,
538
+ [],
539
+ 'should cancel all calls when canceled and called again after some time'
540
+ );
541
+ });
542
+
543
+ it('should reject ongoing promises after debounce cancel', async function () {
544
+ const calledAsync = [];
545
+ async function asyncFn(num, time = 50) {
546
+ await wait(time);
547
+ calledAsync.push(num);
548
+ console.debug('unstoppable async call', num);
549
+ return 'async';
550
+ }
551
+
552
+ const debouncedAsync = debounceAsync(asyncFn, 50);
553
+ const promise = debouncedAsync(1, 300);
554
+ await wait(100);
555
+ debouncedAsync.cancel();
556
+
557
+ await assert.rejects(promise, {
558
+ name: 'debounce.canceled',
559
+ message: 'debounce:canceled'
560
+ });
561
+
562
+ assert.deepEqual(calledAsync, [ 1 ], 'the original promise should always resolve');
563
+ });
564
+
565
+ it('should NOT INVOKE onSuccess callback and not reject when canceled', async function () {
566
+ const calledAsync = [];
567
+ async function asyncStatelessFn(num, time = 50) {
568
+ await wait(time);
569
+ console.debug('asyncStatelessFn:', num);
570
+ return `async ${num}`;
571
+ }
572
+ async function asyncSideEffectFn(result) {
573
+ await wait(50);
574
+ console.debug('asyncSideEffectFn:', result);
575
+ calledAsync.push(result + ' side effect');
576
+ }
577
+
578
+ const debouncedAsync = debounceAsync(asyncStatelessFn, 50, {
579
+ onSuccess: asyncSideEffectFn
580
+ });
581
+ const promise = debouncedAsync(1, 300);
582
+ await wait(100);
583
+ debouncedAsync.cancel();
584
+
585
+ const result = await promise;
586
+
587
+ assert.strictEqual(result, null);
588
+ assert.deepEqual(
589
+ calledAsync,
590
+ [],
591
+ 'the side effect should not be called'
592
+ );
593
+ });
594
+
595
+ it('should invoke onSuccess callback when not canceled', async function () {
596
+ const calledAsync = [];
597
+ async function asyncStatelessFn(num, time = 50) {
598
+ await wait(time);
599
+ console.debug('asyncStatelessFn:', num);
600
+ return `async ${num}`;
601
+ }
602
+ async function asyncSideEffectFn(result) {
603
+ await wait(50);
604
+ console.debug('asyncSideEffectFn:', result);
605
+ calledAsync.push(result + ' side effect');
606
+ }
607
+
608
+ const debouncedAsync = debounceAsync(asyncStatelessFn, 50, {
609
+ onSuccess: asyncSideEffectFn
610
+ });
611
+ const result = await debouncedAsync(1);
612
+ await wait(300);
613
+
614
+ assert.deepEqual(
615
+ calledAsync,
616
+ [ 'async 1 side effect' ],
617
+ 'the side effect should be called'
618
+ );
619
+ assert.strictEqual(result, null);
620
+ });
621
+
622
+ it('should skip the next delay when invoked via skipDelay', async function () {
623
+ const invoked = [];
624
+ const calledAsync = [];
625
+ async function asyncStatelessFn(num, time = 50) {
626
+ invoked.push(num);
627
+ await wait(time);
628
+ console.debug('asyncStatelessFn:', num);
629
+ return `async ${num}`;
630
+ }
631
+ async function asyncSideEffectFn(result) {
632
+ await wait(50);
633
+ console.debug('asyncSideEffectFn:', result);
634
+ calledAsync.push(result + ' side effect');
635
+ }
636
+
637
+ const debouncedAsync = debounceAsync(asyncStatelessFn, 300, {
638
+ onSuccess: asyncSideEffectFn
639
+ });
640
+ debouncedAsync.skipDelay(1, 20);
641
+ await wait(100);
642
+ debouncedAsync(2);
643
+
644
+ assert.deepEqual(
645
+ calledAsync,
646
+ [ 'async 1 side effect' ],
647
+ 'the side effect should be called'
648
+ );
649
+ assert.deepEqual(
650
+ invoked,
651
+ [ 1 ],
652
+ 'bad invokation of the original function'
653
+ );
654
+
655
+ // Wait for the entire delay time to pass.
656
+ await wait(500);
657
+ assert.deepEqual(
658
+ calledAsync,
659
+ [ 'async 1 side effect', 'async 2 side effect' ],
660
+ 'the side effect should be called'
661
+ );
662
+ assert.deepEqual(
663
+ invoked,
664
+ [ 1, 2 ],
665
+ 'bad invokation of the original function after the end of the delay'
666
+ );
667
+ });
668
+
669
+ it('should bounce skipDelay as well', async function () {
670
+ const invoked = [];
671
+ const calledAsync = [];
672
+
673
+ async function asyncStatelessFn(num, time = 50) {
674
+ invoked.push(num);
675
+ await wait(time);
676
+ console.debug('asyncStatelessFn:', num);
677
+ return `async ${num}`;
678
+ }
679
+ async function asyncSideEffectFn(result) {
680
+ await wait(50);
681
+ console.debug('asyncSideEffectFn:', result);
682
+ calledAsync.push(result + ' side effect');
683
+ }
684
+
685
+ const debouncedAsync = debounceAsync(asyncStatelessFn, 100, {
686
+ onSuccess: asyncSideEffectFn
687
+ });
688
+ debouncedAsync(1);
689
+ debouncedAsync.skipDelay(2);
690
+ debouncedAsync(3);
691
+ await wait(500);
692
+
693
+ assert.deepEqual(
694
+ calledAsync,
695
+ [ 'async 1 side effect', 'async 3 side effect' ],
696
+ 'the side effect should be called'
697
+ );
698
+ assert.deepEqual(
699
+ invoked,
700
+ [ 1, 3 ],
701
+ 'bad invokation of the original function'
702
+ );
703
+ });
704
+
444
705
  it('can throttle functions', async function () {
445
706
  const calledNormal = [];
446
707
  const calledAsync = [];