apostrophe 3.4.1 → 3.8.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 (111) hide show
  1. package/.eslintrc +4 -0
  2. package/.scratch.md +2 -0
  3. package/CHANGELOG.md +114 -2
  4. package/README.md +1 -1
  5. package/deploy-test-count +1 -1
  6. package/index.js +125 -5
  7. package/lib/moog-require.js +41 -3
  8. package/lib/moog.js +20 -8
  9. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +42 -23
  10. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
  11. package/modules/@apostrophecms/area/index.js +9 -0
  12. package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
  13. package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
  14. package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +3 -0
  15. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +6 -6
  16. package/modules/@apostrophecms/asset/index.js +85 -21
  17. package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
  18. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +5 -2
  19. package/modules/@apostrophecms/attachment/index.js +1 -0
  20. package/modules/@apostrophecms/db/index.js +5 -6
  21. package/modules/@apostrophecms/doc/index.js +13 -3
  22. package/modules/@apostrophecms/doc-type/index.js +24 -4
  23. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
  24. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
  25. package/modules/@apostrophecms/i18n/i18n/en.json +26 -6
  26. package/modules/@apostrophecms/i18n/i18n/es.json +382 -0
  27. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +379 -0
  28. package/modules/@apostrophecms/i18n/i18n/sk.json +380 -0
  29. package/modules/@apostrophecms/i18n/index.js +10 -1
  30. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +153 -121
  31. package/modules/@apostrophecms/image/index.js +2 -1
  32. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
  33. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +24 -13
  34. package/modules/@apostrophecms/image-widget/index.js +2 -1
  35. package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
  36. package/modules/@apostrophecms/job/index.js +164 -212
  37. package/modules/@apostrophecms/login/index.js +36 -17
  38. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +8 -0
  39. package/modules/@apostrophecms/migration/index.js +1 -1
  40. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
  41. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
  42. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +9 -7
  43. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
  44. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
  45. package/modules/@apostrophecms/module/index.js +1 -1
  46. package/modules/@apostrophecms/notification/index.js +116 -8
  47. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
  48. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
  49. package/modules/@apostrophecms/page/index.js +37 -30
  50. package/modules/@apostrophecms/permission/index.js +1 -1
  51. package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
  52. package/modules/@apostrophecms/piece-type/index.js +178 -61
  53. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -47
  54. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
  55. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
  56. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +42 -10
  57. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -0
  58. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +6 -10
  59. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +64 -0
  60. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +15 -0
  61. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +23 -0
  62. package/modules/@apostrophecms/schema/index.js +97 -20
  63. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
  64. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
  65. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
  66. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +8 -5
  67. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
  68. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
  69. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
  70. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
  71. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
  72. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
  73. package/modules/@apostrophecms/task/index.js +2 -2
  74. package/modules/@apostrophecms/template/index.js +63 -36
  75. package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
  76. package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
  77. package/modules/@apostrophecms/ui/index.js +6 -2
  78. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +21 -3
  79. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
  80. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
  81. package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
  82. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
  83. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
  84. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +3 -0
  85. package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +3 -0
  86. package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +2 -1
  87. package/modules/@apostrophecms/user/index.js +21 -0
  88. package/modules/@apostrophecms/util/index.js +2 -2
  89. package/modules/@apostrophecms/util/ui/src/http.js +12 -8
  90. package/modules/@apostrophecms/util/ui/src/util.js +15 -0
  91. package/modules/@apostrophecms/widget-type/index.js +1 -1
  92. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
  93. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
  94. package/package.json +4 -4
  95. package/test/extra_node_modules/improve-global/index.js +7 -0
  96. package/test/extra_node_modules/improve-piece-type/index.js +7 -0
  97. package/test/improve-overrides.js +30 -0
  98. package/test/job.js +224 -0
  99. package/test/login.js +183 -0
  100. package/test/modules/@apostrophecms/global/index.js +8 -0
  101. package/test/modules/fragment-all/views/aux-test.html +7 -0
  102. package/test/modules/fragment-all/views/fragment.html +5 -0
  103. package/test/moog.js +47 -0
  104. package/test/package.json +5 -4
  105. package/test/pieces.js +17 -0
  106. package/test/reverse-relationship.js +170 -0
  107. package/test/subdir-project/app.js +3 -0
  108. package/test/subdir-project.js +26 -0
  109. package/test/templates.js +7 -1
  110. package/test-lib/test.js +23 -12
  111. package/test-lib/util.js +33 -0
package/test/login.js CHANGED
@@ -161,4 +161,187 @@ describe('Login', function() {
161
161
  assert(page.match(/logged out/));
162
162
  });
163
163
 
164
+ it('Changing a user\'s password should invalidate sessions for that user', async function() {
165
+
166
+ const jar = apos.http.jar();
167
+
168
+ // establish session
169
+ let page = await apos.http.get(
170
+ '/',
171
+ {
172
+ jar
173
+ }
174
+ );
175
+
176
+ assert(page.match(/logged out/));
177
+
178
+ await apos.http.post(
179
+ '/api/v1/@apostrophecms/login/login',
180
+ {
181
+ method: 'POST',
182
+ body: {
183
+ username: 'HarryPutter',
184
+ password: 'crookshanks',
185
+ session: true
186
+ },
187
+ jar
188
+ }
189
+ );
190
+
191
+ page = await apos.http.get(
192
+ '/',
193
+ {
194
+ jar
195
+ }
196
+ );
197
+
198
+ assert(page.match(/logged in/));
199
+
200
+ const req = apos.task.getReq();
201
+ let user = await apos.user.find(req, {
202
+ username: 'HarryPutter'
203
+ }).toObject();
204
+ assert(user);
205
+ user.password = 'VeryPasswordManySecure🐶';
206
+ await apos.user.update(req, user);
207
+
208
+ page = await apos.http.get(
209
+ '/',
210
+ {
211
+ jar
212
+ }
213
+ );
214
+
215
+ assert(!page.match(/logged in/));
216
+ assert(page.match(/logged out/));
217
+
218
+ // Make sure we can come back from that
219
+ await apos.http.post(
220
+ '/api/v1/@apostrophecms/login/login',
221
+ {
222
+ method: 'POST',
223
+ body: {
224
+ username: 'HarryPutter',
225
+ password: 'VeryPasswordManySecure🐶',
226
+ session: true
227
+ },
228
+ jar
229
+ }
230
+ );
231
+
232
+ page = await apos.http.get(
233
+ '/',
234
+ {
235
+ jar
236
+ }
237
+ );
238
+
239
+ assert(page.match(/logged in/));
240
+
241
+ // So we do not have a stale _passwordUpdated flag
242
+ user = await apos.user.find(req, {
243
+ _id: user._id
244
+ }).toObject();
245
+
246
+ // Unrelated writes to user should not invalidate sessions
247
+ user.title = 'Extra Cool Putter';
248
+ await apos.user.update(req, user);
249
+
250
+ page = await apos.http.get(
251
+ '/',
252
+ {
253
+ jar
254
+ }
255
+ );
256
+
257
+ assert(page.match(/logged in/));
258
+
259
+ // Marking a user account as disabled should invalidate sessions
260
+ user.disabled = true;
261
+ await apos.user.update(req, user);
262
+
263
+ page = await apos.http.get(
264
+ '/',
265
+ {
266
+ jar
267
+ }
268
+ );
269
+
270
+ assert(page.match(/logged out/));
271
+
272
+ // Restore access for next test
273
+ user.disabled = false;
274
+ await apos.user.update(req, user);
275
+
276
+ });
277
+
278
+ it('Changing a user\'s password should invalidate bearer tokens for that user', async function() {
279
+
280
+ // Log in
281
+ let response = await apos.http.post('/api/v1/@apostrophecms/login/login', {
282
+ body: {
283
+ username: 'HarryPutter',
284
+ password: 'VeryPasswordManySecure🐶'
285
+ }
286
+ });
287
+ assert(response.token);
288
+ let token = response.token;
289
+
290
+ // For verification: can't do this without an admin bearer token
291
+ await apos.http.get(
292
+ '/api/v1/@apostrophecms/user',
293
+ {
294
+ headers: {
295
+ Authorization: `Bearer ${token}`
296
+ }
297
+ }
298
+ );
299
+
300
+ const req = apos.task.getReq();
301
+ const user = await apos.user.find(req, {
302
+ username: 'HarryPutter'
303
+ }).toObject();
304
+ assert(user);
305
+ user.password = 'AnotherLovelyPassword';
306
+ await apos.user.update(req, user);
307
+
308
+ let failed = false;
309
+ try {
310
+ await apos.http.get(
311
+ '/api/v1/@apostrophecms/user',
312
+ {
313
+ headers: {
314
+ Authorization: `Bearer ${token}`
315
+ }
316
+ }
317
+ );
318
+ // Should NOT work
319
+ assert(false);
320
+ } catch (e) {
321
+ failed = true;
322
+ assert.strictEqual(e.status, 401);
323
+ }
324
+ assert(failed);
325
+
326
+ // Make sure we can come back from that
327
+ response = await apos.http.post('/api/v1/@apostrophecms/login/login', {
328
+ body: {
329
+ username: 'HarryPutter',
330
+ password: 'AnotherLovelyPassword'
331
+ }
332
+ });
333
+ assert(response.token);
334
+ token = response.token;
335
+
336
+ await apos.http.get(
337
+ '/api/v1/@apostrophecms/user',
338
+ {
339
+ headers: {
340
+ Authorization: `Bearer ${token}`
341
+ }
342
+ }
343
+ );
344
+
345
+ });
346
+
164
347
  });
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ options: {
3
+ verifyProjectLevelLoaded: true,
4
+ // Verify we can override these
5
+ testPieceTypeLevel: false,
6
+ testGlobalLevel: false
7
+ }
8
+ };
@@ -0,0 +1,7 @@
1
+ {% extends data.outerLayout %}
2
+
3
+ {% import "fragment.html" as fragment %}
4
+
5
+ {% block main %}
6
+ {% render fragment.auxTest('Gee Whiz') %}
7
+ {% endblock %}
@@ -28,3 +28,8 @@
28
28
  {% fragment _print(text) %}
29
29
  {{ text }}
30
30
  {% endfragment %}
31
+
32
+ {% fragment auxTest(s) %}
33
+ {{ apos.util.slugify(s) }}
34
+ {{ __t('apostrophe:modifyOrDelete') }}
35
+ {% endfragment %}
package/test/moog.js CHANGED
@@ -237,6 +237,53 @@ describe('moog', function() {
237
237
  assert(myObject.extended(5) === 20);
238
238
  });
239
239
 
240
+ it('should support inheriting field group fields rather than requiring all fields to be restated', async function() {
241
+ const moog = require('../lib/moog.js')({});
242
+
243
+ moog.define('myObject', {
244
+ cascades: [ 'fields' ],
245
+ fields: {
246
+ add: {
247
+ one: { type: 'string' },
248
+ two: { type: 'string' },
249
+ three: { type: 'string' }
250
+ },
251
+ group: {
252
+ basics: {
253
+ fields: [ 'one', 'two', 'three' ]
254
+ }
255
+ }
256
+ }
257
+ });
258
+
259
+ moog.define('myObject', {
260
+ fields: {
261
+ add: {
262
+ four: { type: 'string' },
263
+ five: { type: 'string' }
264
+ },
265
+ group: {
266
+ basics: {
267
+ fields: [ 'four', 'five' ]
268
+ },
269
+ other: {
270
+ fields: [ 'one' ]
271
+ }
272
+ }
273
+ }
274
+ });
275
+
276
+ const myObject = await moog.create('myObject', {});
277
+ assert(myObject);
278
+ assert(myObject.fieldsGroups);
279
+ assert(!myObject.fieldsGroups.basics.fields.includes('one'));
280
+ assert(myObject.fieldsGroups.other.fields.includes('one'));
281
+ assert(myObject.fieldsGroups.basics.fields.includes('two'));
282
+ assert(myObject.fieldsGroups.basics.fields.includes('three'));
283
+ assert(myObject.fieldsGroups.basics.fields.includes('four'));
284
+ assert(myObject.fieldsGroups.basics.fields.includes('five'));
285
+ });
286
+
240
287
  // ==================================================
241
288
  // `redefine` AND `isDefined`
242
289
  // ==================================================
package/test/package.json CHANGED
@@ -1,10 +1,11 @@
1
-
2
1
  {
3
2
  "name": "test",
4
3
  "dependencies": {
5
- "apostrophe": "^3.0.0"
4
+ "apostrophe": "^3.0.0",
5
+ "improve-global": "1.0.0",
6
+ "improve-piece-type": "1.0.0"
6
7
  },
7
8
  "devDependencies": {
8
- "test-bundle": {}
9
- }
9
+ "test-bundle": "1.0.0"
10
+ }
10
11
  }
package/test/pieces.js CHANGED
@@ -768,6 +768,23 @@ describe('Pieces', function() {
768
768
  relatedArticleId = response._articles[0]._id;
769
769
  });
770
770
 
771
+ it('can GET a single product using projections', async () => {
772
+ const response = await apos.http.get(`/api/v1/product/${relatedProductId}`, {
773
+ qs: {
774
+ project: {
775
+ _id: 1,
776
+ title: 1
777
+ }
778
+ }
779
+ });
780
+
781
+ const keys = Object.keys(response);
782
+
783
+ assert(response);
784
+ assert(keys.length === 2);
785
+ assert(keys.every((key) => [ '_id', 'title' ].includes(key)));
786
+ });
787
+
771
788
  it('can GET a single article with reverse relationships', async () => {
772
789
  const response = await apos.http.get(`/api/v1/article/${relatedArticleId}`);
773
790
  assert(response);
@@ -0,0 +1,170 @@
1
+ // Bug report that motivated these tests:
2
+ //
3
+ // If products are related to salespeople, and salespeople have a reverse relationship
4
+ // back to products allowing that relationship to be viewed from the other end, everything
5
+ // works.
6
+ //
7
+ // But if products are also related to locations, the reverse relationship back from
8
+ // salespeople stops working.
9
+
10
+ const t = require('../test-lib/test.js');
11
+ const assert = require('assert');
12
+
13
+ describe('Basic reverse relationships', function() {
14
+
15
+ this.timeout(t.timeout);
16
+
17
+ it('basic reverse relationship query works', async function () {
18
+ let apos;
19
+ try {
20
+ apos = await t.create({
21
+ root: module,
22
+ modules: {
23
+ product: {
24
+ options: {
25
+ alias: 'product'
26
+ },
27
+ extend: '@apostrophecms/piece-type',
28
+ fields: {
29
+ add: {
30
+ _salespeople: {
31
+ type: 'relationship',
32
+ withType: 'salesperson'
33
+ }
34
+ }
35
+ }
36
+ },
37
+ salesperson: {
38
+ options: {
39
+ alias: 'salesperson'
40
+ },
41
+ extend: '@apostrophecms/piece-type',
42
+ fields: {
43
+ add: {
44
+ _products: {
45
+ type: 'relationshipReverse',
46
+ withType: 'product',
47
+ reverseOf: '_salespeople'
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ });
54
+
55
+ const req = apos.task.getReq();
56
+ const salesperson = await apos.salesperson.insert(req, {
57
+ title: 'Willie Loman'
58
+ });
59
+ await apos.salesperson.insert(req, {
60
+ title: 'Bernie Sanders'
61
+ });
62
+ await apos.product.insert(req, {
63
+ title: 'Soap',
64
+ _salespeople: [ salesperson ]
65
+ });
66
+ await apos.product.insert(req, {
67
+ title: 'Rope'
68
+ });
69
+ const fetched = await apos.salesperson.find(req, {
70
+ title: 'Willie Loman'
71
+ }).toObject();
72
+ assert(fetched);
73
+ assert.strictEqual(fetched.title, 'Willie Loman');
74
+ assert(fetched._products);
75
+ assert.strictEqual(fetched._products.length, 1);
76
+ assert.strictEqual(fetched._products[0].title, 'Soap');
77
+ } finally {
78
+ if (apos) {
79
+ await t.destroy(apos);
80
+ }
81
+ }
82
+ });
83
+ });
84
+
85
+ describe('Reverse relationships plus an extra relationship', function() {
86
+
87
+ this.timeout(t.timeout);
88
+
89
+ it('basic reverse relationship query works in the presence of an extra relationship with the types configured in an unexpected order', async function () {
90
+ let apos;
91
+ try {
92
+ apos = await t.create({
93
+ root: module,
94
+ modules: {
95
+ salesperson: {
96
+ options: {
97
+ alias: 'salesperson'
98
+ },
99
+ extend: '@apostrophecms/piece-type',
100
+ fields: {
101
+ add: {
102
+ _products: {
103
+ type: 'relationshipReverse',
104
+ withType: 'product',
105
+ reverseOf: '_salespeople'
106
+ }
107
+ }
108
+ }
109
+ },
110
+ location: {
111
+ options: {
112
+ alias: 'location'
113
+ },
114
+ extend: '@apostrophecms/piece-type'
115
+ },
116
+ product: {
117
+ options: {
118
+ alias: 'product'
119
+ },
120
+ extend: '@apostrophecms/piece-type',
121
+ fields: {
122
+ add: {
123
+ _salespeople: {
124
+ type: 'relationship',
125
+ withType: 'salesperson'
126
+ },
127
+ _location: {
128
+ type: 'relationship',
129
+ withType: 'location'
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ });
136
+
137
+ const req = apos.task.getReq();
138
+ const salesperson = await apos.salesperson.insert(req, {
139
+ title: 'Willie Loman'
140
+ });
141
+ await apos.salesperson.insert(req, {
142
+ title: 'Bernie Sanders'
143
+ });
144
+ await apos.product.insert(req, {
145
+ title: 'Soap',
146
+ _salespeople: [ salesperson ]
147
+ });
148
+ await apos.product.insert(req, {
149
+ title: 'Rope'
150
+ });
151
+ const fetched = await apos.salesperson.find(req, {
152
+ title: 'Willie Loman'
153
+ }).toObject();
154
+ const soap = await apos.product.find(req, {
155
+ title: 'Soap'
156
+ }).toObject();
157
+ assert(fetched);
158
+ assert.strictEqual(fetched.title, 'Willie Loman');
159
+ assert(fetched._products);
160
+ assert.strictEqual(fetched._products.length, 1);
161
+ assert.strictEqual(fetched._products[0].title, 'Soap');
162
+ assert.strictEqual(soap.title, 'Soap');
163
+ assert.strictEqual(soap._salespeople[0].title, 'Willie Loman');
164
+ } finally {
165
+ if (apos) {
166
+ await t.destroy(apos);
167
+ }
168
+ }
169
+ });
170
+ });
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ root: module
3
+ };
@@ -0,0 +1,26 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+
4
+ describe('Project with package.json in its parent folder works', function() {
5
+
6
+ this.timeout(t.timeout);
7
+
8
+ /// ///
9
+ // EXISTENCE
10
+ /// ///
11
+
12
+ it('should allow a project relying on a package.json in its parent folder', async function() {
13
+ let apos;
14
+ try {
15
+ apos = await t.create(require('./subdir-project/app.js'));
16
+ // Sniff test: a normal apos object
17
+ assert(apos.user);
18
+ } finally {
19
+ if (apos) {
20
+ await t.destroy(apos);
21
+ }
22
+ }
23
+ assert(apos);
24
+ });
25
+
26
+ });
package/test/templates.js CHANGED
@@ -182,7 +182,6 @@ describe('Templates', function() {
182
182
  it('should render fragments containing async components correctly', async () => {
183
183
  const req = apos.task.getReq();
184
184
  const result = await apos.modules['fragment-page'].renderPage(req, 'page');
185
-
186
185
  const aboveFragment = result.indexOf('Above Fragment');
187
186
  const beforeComponent = result.indexOf('Before Component');
188
187
  const componentText = result.indexOf('Component Text');
@@ -295,4 +294,11 @@ describe('Templates', function() {
295
294
  ]);
296
295
  });
297
296
 
297
+ it('should support apos helpers and localization in fragments', async () => {
298
+ const req = apos.task.getReq();
299
+ const result = await apos.modules['fragment-all'].renderPage(req, 'aux-test');
300
+ assert(result.includes('gee-whiz'));
301
+ assert(result.includes('Modify / Delete'));
302
+ });
303
+
298
304
  });
package/test-lib/test.js CHANGED
@@ -1,9 +1,16 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
 
4
- fs.removeSync(path.join(__dirname, '/../test/node_modules'));
5
- fs.mkdirSync(path.join(__dirname, '/../test/node_modules'));
6
- fs.symlinkSync(path.join(__dirname, '/..'), path.join(__dirname, '/../test/node_modules/apostrophe'), 'dir');
4
+ const testNodeModules = path.join(__dirname, '/../test/node_modules');
5
+ fs.removeSync(testNodeModules);
6
+ fs.mkdirSync(testNodeModules);
7
+ fs.symlinkSync(path.join(__dirname, '/..'), path.join(testNodeModules, 'apostrophe'), 'dir');
8
+
9
+ const extras = path.join(__dirname, '../test/extra_node_modules');
10
+ const dirs = fs.existsSync(extras) ? fs.readdirSync(extras) : [];
11
+ for (const dir of dirs) {
12
+ fs.symlinkSync(path.join(extras, dir), path.join(testNodeModules, dir), 'dir');
13
+ }
7
14
 
8
15
  // Need a "project level" package.json for functionality that checks
9
16
  // whether packages in node_modules are project level or not
@@ -12,15 +19,19 @@ const packageJson = path.join(__dirname, '/../test/package.json');
12
19
  // Remove it first, in case it's the old-style symlink to the main package.json,
13
20
  // which would break
14
21
  fs.removeSync(packageJson);
15
- fs.writeFileSync(packageJson, `
16
- {
17
- "name": "test",
18
- "dependencies": {
19
- "apostrophe": "^3.0.0"
22
+ const packageJsonInfo = {
23
+ name: 'test',
24
+ dependencies: {
25
+ apostrophe: '^3.0.0'
20
26
  },
21
- "devDependencies": {
22
- "test-bundle": {}
23
- }
24
- }`);
27
+ devDependencies: {
28
+ 'test-bundle': '1.0.0'
29
+ }
30
+ };
31
+ for (const dir of dirs) {
32
+ packageJsonInfo.dependencies[dir] = '1.0.0';
33
+ }
34
+
35
+ fs.writeFileSync(packageJson, JSON.stringify(packageJsonInfo, null, ' '));
25
36
 
26
37
  module.exports = require('./util.js');
package/test-lib/util.js CHANGED
@@ -37,6 +37,7 @@ async function create(options) {
37
37
  _: [],
38
38
  'ignore-orphan-modules': true
39
39
  },
40
+ test: true,
40
41
  autoBuild: false,
41
42
  ...options
42
43
  };
@@ -56,8 +57,40 @@ async function create(options) {
56
57
  return require('../index.js')(config);
57
58
  }
58
59
 
60
+ // Create an admin user. By default the username and password are both 'admin'
61
+ async function createAdmin(apos, { username, password } = {}) {
62
+ const user = apos.user.newInstance();
63
+ const name = username || 'admin';
64
+
65
+ user.title = name;
66
+ user.username = name;
67
+ user.password = password || 'admin';
68
+ user.email = `${name}@admin.io`;
69
+ user.role = 'admin';
70
+
71
+ return await apos.user.insert(apos.task.getReq(), user);
72
+ }
73
+
74
+ async function getUserJar(apos, { username, password } = {}) {
75
+ const jar = apos.http.jar();
76
+
77
+ // Log in
78
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
79
+ body: {
80
+ username: username || 'admin',
81
+ password: password || 'admin',
82
+ session: true
83
+ },
84
+ jar
85
+ });
86
+
87
+ return jar;
88
+ }
89
+
59
90
  module.exports = {
60
91
  destroy,
61
92
  create,
93
+ createAdmin,
94
+ getUserJar,
62
95
  timeout: (process.env.TEST_TIMEOUT && parseInt(process.env.TEST_TIMEOUT)) || 20000
63
96
  };