apostrophe 3.61.1 → 3.63.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 (104) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +1 -1
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +6 -4
  4. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +9 -1
  5. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +8 -0
  6. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +6 -3
  7. package/modules/@apostrophecms/doc/index.js +256 -7
  8. package/modules/@apostrophecms/doc/ui/apos/mixins/AposFieldMetaUtilsMixin.js +93 -0
  9. package/modules/@apostrophecms/doc-type/index.js +78 -12
  10. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +9 -1
  11. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +24 -6
  12. package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
  13. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +8 -7
  14. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +1 -5
  15. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +5 -2
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -5
  17. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +4 -2
  18. package/modules/@apostrophecms/login/index.js +25 -19
  19. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +11 -1
  20. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +46 -2
  21. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +8 -3
  22. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +3 -0
  23. package/modules/@apostrophecms/page/index.js +118 -27
  24. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +3 -1
  25. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +7 -0
  26. package/modules/@apostrophecms/page-type/index.js +81 -4
  27. package/modules/@apostrophecms/permission/index.js +60 -31
  28. package/modules/@apostrophecms/piece-type/index.js +19 -41
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +9 -1
  30. package/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +16 -1
  31. package/modules/@apostrophecms/rich-text-widget/index.js +141 -1
  32. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +8 -0
  33. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +7 -7
  34. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +38 -79
  35. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Link.js +11 -0
  36. package/modules/@apostrophecms/schema/index.js +9 -0
  37. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +22 -2
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +4 -1
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +7 -8
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -0
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +1 -0
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +1 -0
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +76 -30
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +2 -4
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
  47. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +7 -0
  48. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +13 -1
  49. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +5 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +21 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +12 -8
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +35 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +6 -0
  54. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +41 -0
  55. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -0
  56. package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +1 -0
  57. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +7 -0
  58. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +8 -0
  59. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +10 -4
  60. package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +2 -2
  61. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +7 -0
  62. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +6 -0
  63. package/package.json +2 -2
  64. package/test/areas.js +1 -1
  65. package/test/assets.js +2 -2
  66. package/test/attachments.js +2 -2
  67. package/test/base-module.js +2 -1
  68. package/test/base-url-env-var.js +2 -2
  69. package/test/change-doc-ids.js +33 -31
  70. package/test/command-menu.js +2 -2
  71. package/test/content-i18n.js +47 -46
  72. package/test/db.js +2 -2
  73. package/test/docs.js +301 -126
  74. package/test/draft-published.js +2 -2
  75. package/test/email.js +2 -1
  76. package/test/express.js +3 -2
  77. package/test/external-front.js +4 -4
  78. package/test/field-meta.js +363 -0
  79. package/test/global.js +2 -1
  80. package/test/http.js +4 -2
  81. package/test/images.js +87 -88
  82. package/test/job.js +34 -34
  83. package/test/locks.js +2 -2
  84. package/test/login-requirements.js +3 -2
  85. package/test/middleware-and-route-order.js +2 -2
  86. package/test/page-type.js +2 -1
  87. package/test/pages-autocomplete.js +192 -0
  88. package/test/pages-public-api.js +2 -2
  89. package/test/pages-rest.js +4 -4
  90. package/test/pages.js +389 -57
  91. package/test/parked-pages.js +47 -47
  92. package/test/permissions.js +76 -0
  93. package/test/pieces-page-type.js +2 -1
  94. package/test/pieces-public-api.js +38 -38
  95. package/test/pieces.js +4 -4
  96. package/test/published-pages.js +16 -16
  97. package/test/rich-text-widget.js +164 -0
  98. package/test/schema-queryBuilders.js +180 -0
  99. package/test/schemaBuilders.js +4 -4
  100. package/test/schemas.js +220 -221
  101. package/test/search.js +2 -2
  102. package/test/soft-redirects.js +2 -1
  103. package/test/templates.js +2 -2
  104. package/test/users.js +2 -1
package/test/docs.js CHANGED
@@ -2,19 +2,18 @@ const t = require('../test-lib/test.js');
2
2
  const assert = require('assert');
3
3
  const _ = require('lodash');
4
4
 
5
- let apos;
6
-
7
5
  describe('Docs', function() {
6
+ let apos;
8
7
 
9
8
  this.timeout(t.timeout);
10
9
 
11
- after(function() {
10
+ after(async function() {
11
+ await apos.doc.db.deleteMany({});
12
+ await apos.lock.db.deleteMany({});
12
13
  return t.destroy(apos);
13
14
  });
14
15
 
15
- // EXISTENCE
16
-
17
- it('should be a property of the apos object', async function() {
16
+ before(async function() {
18
17
  apos = await t.create({
19
18
  root: module,
20
19
 
@@ -32,6 +31,22 @@ describe('Docs', function() {
32
31
  }
33
32
  }
34
33
  },
34
+ unlocalized: {
35
+ extend: '@apostrophecms/piece-type',
36
+ options: {
37
+ localized: false
38
+ },
39
+ fields: {
40
+ add: {
41
+ _friends: {
42
+ type: 'relationship',
43
+ max: 1,
44
+ withType: 'test-people',
45
+ label: 'Friends'
46
+ }
47
+ }
48
+ }
49
+ },
35
50
  '@apostrophecms/page': {
36
51
  options: {
37
52
  park: [],
@@ -48,19 +63,17 @@ describe('Docs', function() {
48
63
  }
49
64
  }
50
65
  });
66
+ });
51
67
 
52
- assert(apos.doc);
53
-
68
+ afterEach(async function () {
69
+ await apos.doc.db.deleteMany({ type: 'test-people' });
70
+ await apos.lock.db.deleteMany({});
54
71
  });
55
72
 
56
73
  it('should have a db property', function() {
57
74
  assert(apos.doc.db);
58
75
  });
59
76
 
60
- /// ///
61
- // SETUP
62
- /// ///
63
-
64
77
  it('should make sure all of the expected indexes are configured', async function() {
65
78
  const expectedIndexes = [
66
79
  'type',
@@ -84,64 +97,8 @@ describe('Docs', function() {
84
97
  assert(info.highSearchText_text_lowSearchText_text_title_text_searchBoost_text[0][1] === 'text');
85
98
  });
86
99
 
87
- it('should make sure there is no test data hanging around from last time', async function() {
88
- // Attempt to purge the entire aposDocs collection
89
- await apos.doc.db.deleteMany({});
90
-
91
- // Make sure it went away
92
- const docs = await apos.doc.db.find({ slug: 'larry' }).toArray();
93
-
94
- assert(docs.length === 0);
95
- });
96
-
97
- it('should be able to use db to insert documents', async function() {
98
- const testItems = [
99
- {
100
- _id: 'lori:en:published',
101
- aposDocId: 'lori',
102
- aposLocale: 'en:published',
103
- slug: 'lori',
104
- visibility: 'public',
105
- type: 'test-people',
106
- firstName: 'Lori',
107
- lastName: 'Pizzaroni',
108
- age: 32,
109
- alive: true
110
- },
111
- {
112
- _id: 'larry:en:published',
113
- aposDocId: 'larry',
114
- aposLocale: 'en:published',
115
- slug: 'larry',
116
- visibility: 'public',
117
- type: 'test-people',
118
- firstName: 'Larry',
119
- lastName: 'Cherber',
120
- age: 28,
121
- alive: true
122
- },
123
- {
124
- _id: 'carl:en:published',
125
- aposDocId: 'carl',
126
- aposLocale: 'en:published',
127
- slug: 'carl',
128
- visibility: 'public',
129
- type: 'test-people',
130
- firstName: 'Carl',
131
- lastName: 'Sagan',
132
- age: 62,
133
- alive: false,
134
- friendsIds: [ 'larry' ]
135
- }
136
- ];
137
-
138
- const response = await apos.doc.db.insertMany(testItems);
139
-
140
- assert(response.result.ok === 1);
141
- assert(response.insertedCount === 3);
142
- });
143
-
144
100
  it('should be able to fetch schema relationships', async function() {
101
+ await insertPeople(apos);
145
102
  const manager = apos.doc.getManager('test-people');
146
103
  const req = apos.task.getAnonReq();
147
104
 
@@ -299,6 +256,7 @@ describe('Docs', function() {
299
256
  });
300
257
 
301
258
  it('should be able to find all test documents and output them as an array', async function () {
259
+ await insertPeople(apos);
302
260
  const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' });
303
261
 
304
262
  const docs = await cursor.toArray();
@@ -314,6 +272,7 @@ describe('Docs', function() {
314
272
  /// ///
315
273
 
316
274
  it('should be able to specify which fields to get by passing a projection object', async function() {
275
+ await insertPeople(apos);
317
276
  const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }, {
318
277
  project: {
319
278
  age: 1
@@ -332,6 +291,7 @@ describe('Docs', function() {
332
291
  /// ///
333
292
 
334
293
  it('should be able to sort', async function () {
294
+ await insertPeople(apos);
335
295
  const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }).sort({ age: 1 });
336
296
  const docs = await cursor.toArray();
337
297
 
@@ -339,6 +299,7 @@ describe('Docs', function() {
339
299
  });
340
300
 
341
301
  it('should be able to sort by multiple keys', async function () {
302
+ await insertPeople(apos);
342
303
  const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }).sort({
343
304
  firstName: 1,
344
305
  age: 1
@@ -353,19 +314,8 @@ describe('Docs', function() {
353
314
  // INSERTING
354
315
  /// ///
355
316
 
356
- it('should have an "insert" method that returns a new database object', async function() {
357
- const object = {
358
- slug: 'one',
359
- visibility: 'public',
360
- type: 'test-people',
361
- firstName: 'Lori',
362
- lastName: 'Ferber',
363
- age: 15,
364
- alive: true
365
- };
366
-
367
- const response = await apos.doc.insert(apos.task.getReq(), object);
368
-
317
+ it('should be able to insert a new object into the docs collection in the database', async function() {
318
+ const response = await insertOne(apos);
369
319
  assert(response);
370
320
  assert(response._id);
371
321
  assert(response._id.endsWith(':en:published'));
@@ -377,12 +327,9 @@ describe('Docs', function() {
377
327
  });
378
328
  assert(draft);
379
329
  // Unique index allows for duplicates across locales
380
- assert(object.slug === draft.slug);
330
+ assert(draft.slug === 'one');
381
331
  // Content properties coming through
382
332
  assert(draft.firstName === response.firstName);
383
- });
384
-
385
- it('should be able to insert a new object into the docs collection in the database', async function() {
386
333
  const cursor = apos.doc.find(apos.task.getReq(), {
387
334
  type: 'test-people',
388
335
  slug: 'one'
@@ -393,6 +340,7 @@ describe('Docs', function() {
393
340
  });
394
341
 
395
342
  it('should append the slug property with a numeral if inserting an object whose slug already exists in the database', async function() {
343
+ await insertOne(apos);
396
344
  const object = {
397
345
  slug: 'one',
398
346
  visibility: 'public',
@@ -410,21 +358,8 @@ describe('Docs', function() {
410
358
  });
411
359
 
412
360
  it('should add the aposDocId to the related documents\' relatedReverseIds field and update their `cacheInvalidatedAt` field', async function() {
413
- const object = {
414
- aposDocId: 'paul',
415
- aposLocale: 'en:published',
416
- slug: 'paul',
417
- visibility: 'public',
418
- type: 'test-people',
419
- firstName: 'Paul',
420
- lastName: 'McCartney',
421
- age: 24,
422
- alive: false,
423
- friendsIds: [ 'carl', 'larry' ],
424
- _friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
425
- };
426
-
427
- const response = await apos.doc.insert(apos.task.getReq(), object);
361
+ await insertPeople(apos);
362
+ const response = await insertOneWithRelated(apos);
428
363
 
429
364
  const carlDoc = await apos.doc.db.findOne({
430
365
  slug: 'carl',
@@ -443,6 +378,103 @@ describe('Docs', function() {
443
378
  assert(larryDoc.relatedReverseIds.length === 1);
444
379
  assert(larryDoc.relatedReverseIds[0] === 'paul');
445
380
  assert(larryDoc.cacheInvalidatedAt.getTime() === response.updatedAt.getTime());
381
+
382
+ apos.doc.db.deleteMany({ slug: { $in: [ 'paul', 'carl', 'larry' ] } });
383
+ });
384
+
385
+ it('should remove the related reverse IDs when you delete a draft document', async function () {
386
+ const req = apos.task.getReq();
387
+ await insertPeople(apos);
388
+ const personWithRelatedPublished = await insertOneWithRelated(apos);
389
+ const personWithRelatedDraft = await apos.doc.find(apos.task.getReq({ mode: 'draft' }), { slug: 'paul' }).toObject();
390
+
391
+ await apos.doc.delete(req, personWithRelatedPublished);
392
+
393
+ const larryDoc = await apos.doc.db.findOne({
394
+ slug: 'larry',
395
+ aposLocale: 'en:published'
396
+ });
397
+ const carlDoc = await apos.doc.db.findOne({
398
+ slug: 'carl',
399
+ aposLocale: 'en:published'
400
+ });
401
+
402
+ await apos.doc.delete(req, personWithRelatedDraft);
403
+
404
+ const larryDocUpdated = await apos.doc.db.findOne({
405
+ slug: 'larry',
406
+ aposLocale: 'en:published'
407
+ });
408
+ const carlDocUpdated = await apos.doc.db.findOne({
409
+ slug: 'carl',
410
+ aposLocale: 'en:published'
411
+ });
412
+
413
+ const actual = {
414
+ larryHasRelatedBeforeDelete: larryDoc.relatedReverseIds.includes('paul'),
415
+ carlHasRelatedBeforeDelete: carlDoc.relatedReverseIds.includes('paul'),
416
+ larryHasRelatedAfterDelete: larryDocUpdated.relatedReverseIds.includes('paul'),
417
+ carlHasRelatedAfterDelete: carlDocUpdated.relatedReverseIds.includes('paul')
418
+ };
419
+
420
+ const expected = {
421
+ larryHasRelatedBeforeDelete: true,
422
+ carlHasRelatedBeforeDelete: true,
423
+ larryHasRelatedAfterDelete: false,
424
+ carlHasRelatedAfterDelete: false
425
+ };
426
+
427
+ assert.deepEqual(actual, expected);
428
+ });
429
+
430
+ it('should remove the related reverse IDs when you delete an unlocalized document', async function () {
431
+ const req = apos.task.getReq();
432
+ await insertPeople(apos);
433
+ const unlocalizedDoc = await apos.doc.insert(req, {
434
+ aposDocId: 'paul',
435
+ aposLocale: 'en:published',
436
+ slug: 'paul',
437
+ visibility: 'public',
438
+ type: 'unlocalized',
439
+ friendsIds: [ 'carl', 'larry' ],
440
+ _friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
441
+ });
442
+
443
+ const larryDoc = await apos.doc.db.findOne({
444
+ slug: 'larry',
445
+ aposLocale: 'en:published'
446
+ });
447
+ const carlDoc = await apos.doc.db.findOne({
448
+ aposDocId: 'carl',
449
+ aposLocale: 'en:published'
450
+ });
451
+
452
+ await apos.doc.delete(req, unlocalizedDoc);
453
+
454
+ const larryDocUpdated = await apos.doc.db.findOne({
455
+ slug: 'larry',
456
+ aposLocale: 'en:published'
457
+ });
458
+ const carlDocUpdated = await apos.doc.db.findOne({
459
+ slug: 'carl',
460
+ aposLocale: 'en:published'
461
+ });
462
+
463
+ const actual = {
464
+ larryHasRelatedBeforeDelete: larryDoc.relatedReverseIds.includes('paul'),
465
+ carlHasRelatedBeforeDelete: carlDoc.relatedReverseIds.includes('paul'),
466
+ larryHasRelatedAfterDelete: larryDocUpdated.relatedReverseIds.includes('paul'),
467
+ carlHasRelatedAfterDelete: carlDocUpdated.relatedReverseIds.includes('paul')
468
+ };
469
+
470
+ const expected = {
471
+ larryHasRelatedBeforeDelete: true,
472
+ carlHasRelatedBeforeDelete: true,
473
+ larryHasRelatedAfterDelete: false,
474
+ carlHasRelatedAfterDelete: false
475
+ };
476
+
477
+ assert.deepEqual(actual, expected);
446
478
  });
447
479
 
448
480
  it('should not allow you to call the insert method if you are not an admin', async function() {
@@ -469,6 +501,7 @@ describe('Docs', function() {
469
501
  /// ///
470
502
 
471
503
  it('should have an "update" method on docs that updates an existing database object', async function() {
504
+ await insertOne(apos);
472
505
  const req = apos.task.getReq();
473
506
  const docs = await apos.doc.find(req, { slug: 'one' }).toArray();
474
507
 
@@ -488,41 +521,55 @@ describe('Docs', function() {
488
521
  });
489
522
 
490
523
  it('should append an updated slug with a numeral if the updated slug already exists', async function() {
524
+ await insertPeople(apos);
525
+ await insertOne(apos);
491
526
  const req = apos.task.getReq();
492
527
  const cursor = apos.doc.find(req, {
493
528
  type: 'test-people',
494
529
  slug: 'one'
495
530
  });
496
531
  const doc = await cursor.toObject();
497
-
498
532
  assert(doc);
499
533
 
500
534
  doc.slug = 'peter';
501
-
502
535
  const updated = await apos.doc.update(req, doc);
503
536
  assert(updated);
504
537
  // Has the updated slug been appended?
505
538
  assert(updated.slug.match(/^peter\d+$/));
506
539
  });
540
+
507
541
  it('should be able to fetch all unique firstNames with toDistinct', async function() {
542
+ await insertPeople(apos);
508
543
  const firstNames = await apos.doc.find(apos.task.getReq(), {
509
544
  type: 'test-people'
510
545
  }).toDistinct('firstName');
511
546
 
512
547
  assert(Array.isArray(firstNames));
513
- assert(firstNames.length === 5);
548
+ assert(firstNames.length === 4);
514
549
  assert(_.includes(firstNames, 'Larry'));
515
550
  });
516
551
 
517
552
  it('should be able to fetch all unique firstNames and their counts with toDistinct and distinctCounts', async function() {
553
+ await insertPeople(apos);
518
554
  const req = apos.task.getReq();
555
+ await apos.doc.db.insertOne({
556
+ _id: 'random:en:published',
557
+ slug: 'random',
558
+ aposDocId: 'lori2',
559
+ aposLocale: 'en:published',
560
+ type: 'test-people',
561
+ visibility: 'loginRequired',
562
+ firstName: 'Lori',
563
+ lastName: 'Lora',
564
+ age: 70
565
+ });
519
566
  const cursor = apos.doc.find(req, {
520
567
  type: 'test-people'
521
568
  }).distinctCounts(true);
522
569
  const firstNames = await cursor.toDistinct('firstName');
523
570
 
524
571
  assert(Array.isArray(firstNames));
525
- assert(firstNames.length === 5);
572
+ assert(firstNames.length === 4);
526
573
  assert(_.includes(firstNames, 'Larry'));
527
574
 
528
575
  const counts = await cursor.get('distinctCounts');
@@ -532,6 +579,8 @@ describe('Docs', function() {
532
579
  });
533
580
 
534
581
  it('should remove the aposDocId from the related documents\' relatedReverseIds field and update their `cacheInvalidatedAt` field', async function() {
582
+ await insertPeople(apos);
583
+ await insertOneWithRelated(apos);
535
584
  const paulDoc = await apos.doc.db.findOne({
536
585
  slug: 'paul',
537
586
  aposLocale: 'en:published'
@@ -565,6 +614,7 @@ describe('Docs', function() {
565
614
  });
566
615
 
567
616
  it('should update the related reverse documents\' `cacheInvalidatedAt` field', async function() {
617
+ await insertPeople(apos);
568
618
  const object = {
569
619
  aposDocId: 'john',
570
620
  aposLocale: 'en:published',
@@ -656,21 +706,26 @@ describe('Docs', function() {
656
706
  /// ///
657
707
 
658
708
  it('should archive docs by updating them', async function() {
709
+ await insertPeople(apos);
659
710
  const req = apos.task.getReq();
660
711
  const doc = await apos.doc.find(req, {
661
712
  type: 'test-people',
662
713
  slug: 'carl'
663
714
  }).toObject();
664
- const archived = await apos.doc.update(req, {
665
- ...doc,
666
- archived: true
667
- });
715
+ const archived = await archiveDoc(apos, doc);
668
716
 
669
717
  assert(archived.archived === true);
670
718
  });
671
719
 
672
720
  it('should not be able to find the archived object', async function() {
673
721
  const req = apos.task.getReq();
722
+ await insertPeople(apos);
723
+ const carlDoc = await apos.doc.find(req, {
724
+ type: 'test-people',
725
+ slug: 'carl'
726
+ }).toObject();
727
+ await archiveDoc(apos, carlDoc);
728
+
674
729
  const doc = await apos.doc.find(req, {
675
730
  slug: 'carl'
676
731
  }).toObject();
@@ -690,11 +745,17 @@ describe('Docs', function() {
690
745
  });
691
746
 
692
747
  it('should be able to find the archived object when using the "archived" method on find()', async function() {
748
+ const req = apos.task.getReq();
749
+ await insertPeople(apos);
750
+ const carlDoc = await apos.doc.find(req, {
751
+ type: 'test-people',
752
+ slug: 'carl'
753
+ }).toObject();
754
+ await archiveDoc(apos, carlDoc);
693
755
  // Look for the archived doc with the `deduplicate-` + its `_id` + its `name` properties.
694
- const doc = await apos.doc.find(apos.task.getReq(), {
756
+ const doc = await apos.doc.find(req, {
695
757
  slug: 'deduplicate-carl-carl'
696
758
  }).archived(true).toObject();
697
-
698
759
  assert(doc);
699
760
  assert(doc.archived);
700
761
  });
@@ -705,6 +766,12 @@ describe('Docs', function() {
705
766
 
706
767
  it('should rescue a doc by updating the "archived" property from an object', async function() {
707
768
  const req = apos.task.getReq();
769
+ await insertPeople(apos);
770
+ const carlDoc = await apos.doc.find(req, {
771
+ type: 'test-people',
772
+ slug: 'carl'
773
+ }).toObject();
774
+ await archiveDoc(apos, carlDoc);
708
775
 
709
776
  const doc = await apos.doc.find(req, {
710
777
  slug: 'deduplicate-carl-carl'
@@ -724,6 +791,12 @@ describe('Docs', function() {
724
791
 
725
792
  it('should not allow you to call the restore method if you are not an admin', async function() {
726
793
  try {
794
+ await insertPeople(apos);
795
+ const carlDoc = await apos.doc.find(apos.task.getReq(), {
796
+ type: 'test-people',
797
+ slug: 'carl'
798
+ }).toObject();
799
+ await archiveDoc(apos, carlDoc);
727
800
  await apos.doc.restore(apos.task.getAnonReq(), {
728
801
  slug: 'carl'
729
802
  });
@@ -769,21 +842,19 @@ describe('Docs', function() {
769
842
  assert(docs[2]._id === 'i27:en:published');
770
843
  assert(docs[3]._id === 'i9:en:published');
771
844
  assert(!docs[4]);
772
- });
773
845
 
774
- it('should respect _ids with skip and limit', async function() {
775
- // Relies on test data of previous test
776
- const docs = await apos.doc.find(apos.task.getAnonReq(), {})
846
+ const filteredDocs = await apos.doc.find(apos.task.getAnonReq(), {})
777
847
  ._ids([ 'i7:en:published', 'i3:en:published', 'i27:en:published', 'i9:en:published' ]).skip(2).limit(2).toArray();
778
848
 
779
- assert(docs[0]._id === 'i27:en:published');
780
- assert(docs[1]._id === 'i9:en:published');
781
- assert(!docs[2]);
849
+ assert(filteredDocs[0]._id === 'i27:en:published');
850
+ assert(filteredDocs[1]._id === 'i9:en:published');
851
+ assert(!filteredDocs[2]);
782
852
  });
783
853
 
784
854
  it('should be able to lock a document', async function() {
785
855
  const req = apos.task.getReq();
786
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
856
+ await insertPeople(apos);
857
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
787
858
  try {
788
859
  await apos.doc.lock(req, doc, 'abc');
789
860
  } catch (e) {
@@ -793,10 +864,13 @@ describe('Docs', function() {
793
864
 
794
865
  it('should not be able to lock a document with a different tabId', async function() {
795
866
  const req = apos.task.getReq();
796
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
867
+ await insertPeople(apos);
868
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
797
869
 
798
870
  try {
799
- await apos.doc.lock(req, doc, 'def');
871
+ await apos.doc.lock(req, doc, 'abc');
872
+ const locked = await apos.doc.db.findOne({ _id: 'carl:en:published' });
873
+ await apos.doc.lock(req, locked, 'def');
800
874
  } catch (e) {
801
875
  assert(e);
802
876
  assert(e.name === 'locked');
@@ -805,10 +879,17 @@ describe('Docs', function() {
805
879
 
806
880
  it('should be able to refresh the lock with the same tabId', async function() {
807
881
  const req = apos.task.getReq();
808
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
882
+ await insertPeople(apos);
883
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
884
+ const wait = (time) => new Promise((resolve) => setTimeout(() => {
885
+ resolve();
886
+ }, time));
809
887
 
810
888
  try {
811
889
  await apos.doc.lock(req, doc, 'abc');
890
+ const locked = await apos.doc.db.findOne({ _id: 'carl:en:published' });
891
+ await wait(500);
892
+ await apos.doc.lock(req, locked, 'abc');
812
893
  } catch (e) {
813
894
  assert(!e);
814
895
  }
@@ -816,9 +897,11 @@ describe('Docs', function() {
816
897
 
817
898
  it('should be able to unlock a document', async function() {
818
899
  const req = apos.task.getReq();
819
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
900
+ await insertPeople(apos);
901
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
820
902
 
821
903
  try {
904
+ await apos.doc.lock(req, doc, 'abc');
822
905
  await apos.doc.unlock(req, doc, 'abc');
823
906
  } catch (e) {
824
907
  assert(false);
@@ -827,9 +910,12 @@ describe('Docs', function() {
827
910
 
828
911
  it('should be able to re-lock an unlocked document', async function() {
829
912
  const req = apos.task.getReq();
830
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
913
+ await insertPeople(apos);
914
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
831
915
 
832
916
  try {
917
+ await apos.doc.lock(req, doc, 'abc');
918
+ await apos.doc.unlock(req, doc, 'abc');
833
919
  await apos.doc.lock(req, doc, 'def');
834
920
  } catch (e) {
835
921
  assert(false);
@@ -838,9 +924,11 @@ describe('Docs', function() {
838
924
 
839
925
  it('should be able to lock a locked document with force: true', async function() {
840
926
  const req = apos.task.getReq();
841
- const doc = await apos.doc.db.findOne({ _id: 'i27:en:published' });
927
+ await insertPeople(apos);
928
+ const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
842
929
 
843
930
  try {
931
+ await apos.doc.lock(req, doc, 'def');
844
932
  await apos.doc.lock(req, doc, 'abc', { force: true });
845
933
  } catch (e) {
846
934
  assert(false);
@@ -959,5 +1047,92 @@ describe('Docs', function() {
959
1047
  assert(response.cacheInvalidatedAt.getTime() === response.updatedAt.getTime());
960
1048
  assert(draft.cacheInvalidatedAt.getTime() === draft.updatedAt.getTime());
961
1049
  });
962
-
963
1050
  });
1051
+
1052
+ async function insertPeople(apos) {
1053
+ return apos.doc.db.insertMany([
1054
+ {
1055
+ _id: 'lori:en:published',
1056
+ aposDocId: 'lori',
1057
+ aposLocale: 'en:published',
1058
+ slug: 'lori',
1059
+ visibility: 'public',
1060
+ type: 'test-people',
1061
+ firstName: 'Lori',
1062
+ lastName: 'Pizzaroni',
1063
+ age: 32,
1064
+ alive: true
1065
+ },
1066
+ {
1067
+ _id: 'larry:en:published',
1068
+ aposDocId: 'larry',
1069
+ aposLocale: 'en:published',
1070
+ slug: 'larry',
1071
+ visibility: 'public',
1072
+ type: 'test-people',
1073
+ firstName: 'Larry',
1074
+ lastName: 'Cherber',
1075
+ age: 28,
1076
+ alive: true
1077
+ },
1078
+ {
1079
+ _id: 'carl:en:published',
1080
+ aposDocId: 'carl',
1081
+ aposLocale: 'en:published',
1082
+ slug: 'carl',
1083
+ visibility: 'public',
1084
+ type: 'test-people',
1085
+ firstName: 'Carl',
1086
+ lastName: 'Sagan',
1087
+ age: 62,
1088
+ alive: false,
1089
+ friendsIds: [ 'larry' ]
1090
+ },
1091
+ {
1092
+ _id: 'peter:en:published',
1093
+ aposDocId: 'peter',
1094
+ aposLocale: 'en:published',
1095
+ type: 'test-people',
1096
+ visibility: 'loginRequired',
1097
+ firstName: 'Peter',
1098
+ lastName: 'Pan',
1099
+ age: 70,
1100
+ slug: 'peter'
1101
+ }
1102
+ ]);
1103
+ }
1104
+
1105
+ async function insertOne(apos) {
1106
+ return apos.doc.insert(apos.task.getReq(), {
1107
+ slug: 'one',
1108
+ visibility: 'public',
1109
+ type: 'test-people',
1110
+ firstName: 'Lori',
1111
+ lastName: 'Ferber',
1112
+ age: 15,
1113
+ alive: true
1114
+ });
1115
+ }
1116
+
1117
+ async function insertOneWithRelated(apos) {
1118
+ return apos.doc.insert(apos.task.getReq(), {
1119
+ aposDocId: 'paul',
1120
+ aposLocale: 'en:published',
1121
+ slug: 'paul',
1122
+ visibility: 'public',
1123
+ type: 'test-people',
1124
+ firstName: 'Paul',
1125
+ lastName: 'McCartney',
1126
+ age: 24,
1127
+ alive: false,
1128
+ friendsIds: [ 'carl', 'larry' ],
1129
+ _friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
1130
+ });
1131
+ }
1132
+
1133
+ async function archiveDoc(apos, doc) {
1134
+ return apos.doc.update(apos.task.getReq(), {
1135
+ ...doc,
1136
+ archived: true
1137
+ });
1138
+ }
@@ -1,10 +1,10 @@
1
1
  const t = require('../test-lib/test.js');
2
2
  const assert = require('assert');
3
3
 
4
- let apos;
5
-
6
4
  describe('Draft / Published', function() {
7
5
 
6
+ let apos;
7
+
8
8
  this.timeout(t.timeout);
9
9
 
10
10
  after(async function () {