apostrophe 3.5.0 → 3.8.1

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 (88) hide show
  1. package/.eslintrc +4 -0
  2. package/.scratch.md +2 -0
  3. package/CHANGELOG.md +96 -3
  4. package/README.md +1 -1
  5. package/index.js +108 -3
  6. package/lib/moog-require.js +23 -0
  7. package/lib/moog.js +1 -0
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
  9. package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
  10. package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +2 -2
  12. package/modules/@apostrophecms/asset/index.js +77 -13
  13. package/modules/@apostrophecms/attachment/index.js +1 -0
  14. package/modules/@apostrophecms/db/index.js +5 -6
  15. package/modules/@apostrophecms/doc/index.js +2 -0
  16. package/modules/@apostrophecms/doc-type/index.js +39 -16
  17. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
  18. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
  19. package/modules/@apostrophecms/i18n/i18n/en.json +19 -6
  20. package/modules/@apostrophecms/i18n/i18n/es.json +382 -0
  21. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +379 -0
  22. package/modules/@apostrophecms/i18n/i18n/sk.json +380 -0
  23. package/modules/@apostrophecms/i18n/index.js +10 -1
  24. package/modules/@apostrophecms/image/index.js +2 -1
  25. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
  26. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +2 -1
  27. package/modules/@apostrophecms/image-widget/index.js +2 -1
  28. package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
  29. package/modules/@apostrophecms/job/index.js +164 -212
  30. package/modules/@apostrophecms/login/index.js +1 -16
  31. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +5 -0
  32. package/modules/@apostrophecms/migration/index.js +1 -1
  33. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
  34. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
  35. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +8 -6
  36. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
  37. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
  38. package/modules/@apostrophecms/notification/index.js +116 -8
  39. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
  40. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
  41. package/modules/@apostrophecms/page/index.js +37 -30
  42. package/modules/@apostrophecms/permission/index.js +1 -1
  43. package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
  44. package/modules/@apostrophecms/piece-type/index.js +178 -61
  45. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -47
  46. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
  47. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
  48. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +35 -6
  49. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +1 -3
  50. package/modules/@apostrophecms/schema/index.js +97 -20
  51. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
  52. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
  53. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
  54. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
  55. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
  56. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
  57. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
  58. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
  59. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
  60. package/modules/@apostrophecms/task/index.js +2 -2
  61. package/modules/@apostrophecms/template/index.js +63 -36
  62. package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
  63. package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
  64. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -2
  65. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
  66. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
  67. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
  68. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
  69. package/modules/@apostrophecms/util/index.js +2 -2
  70. package/modules/@apostrophecms/util/ui/src/http.js +12 -8
  71. package/modules/@apostrophecms/util/ui/src/util.js +15 -0
  72. package/modules/@apostrophecms/widget-type/index.js +1 -1
  73. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
  74. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
  75. package/package.json +3 -3
  76. package/test/extra_node_modules/improve-global/index.js +7 -0
  77. package/test/extra_node_modules/improve-piece-type/index.js +7 -0
  78. package/test/improve-overrides.js +30 -0
  79. package/test/job.js +224 -0
  80. package/test/modules/@apostrophecms/global/index.js +8 -0
  81. package/test/modules/fragment-all/views/aux-test.html +7 -0
  82. package/test/modules/fragment-all/views/fragment.html +5 -0
  83. package/test/package.json +5 -4
  84. package/test/pieces.js +34 -0
  85. package/test/reverse-relationship.js +170 -0
  86. package/test/templates.js +7 -1
  87. package/test-lib/test.js +23 -12
  88. package/test-lib/util.js +33 -0
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ improve: '@apostrophecms/global',
3
+ options: {
4
+ testGlobalLevelLoaded: true,
5
+ testGlobalLevel: true
6
+ }
7
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ improve: '@apostrophecms/piece-type',
3
+ options: {
4
+ testPieceTypeLevelLoaded: true,
5
+ testPieceTypeLevel: true
6
+ }
7
+ };
@@ -0,0 +1,30 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+
4
+ describe('Improve Overrides', function() {
5
+
6
+ this.timeout(t.timeout);
7
+
8
+ it('"improve" should work, but project level should override it', async function() {
9
+ let apos;
10
+ try {
11
+ apos = await t.create({
12
+ root: module,
13
+ modules: {
14
+ 'improve-piece-type': {},
15
+ 'improve-global': {}
16
+ }
17
+ });
18
+ assert(apos.global.options.verifyProjectLevelLoaded);
19
+ assert.strictEqual(apos.user.options.testPieceTypeLevelLoaded, true);
20
+ assert.strictEqual(apos.user.options.testPieceTypeLevel, true);
21
+ assert.strictEqual(apos.global.options.testPieceTypeLevelLoaded, true);
22
+ assert.strictEqual(apos.global.options.testPieceTypeLevel, false);
23
+ assert.strictEqual(apos.global.options.testGlobalLevelLoaded, true);
24
+ assert.strictEqual(apos.global.options.testGlobalLevel, false);
25
+ } finally {
26
+ t.destroy(apos);
27
+ }
28
+ });
29
+
30
+ });
package/test/job.js ADDED
@@ -0,0 +1,224 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+ const Promise = require('bluebird');
4
+ let apos;
5
+
6
+ describe('Job module', function() {
7
+
8
+ this.timeout(t.timeout);
9
+
10
+ after(async function() {
11
+ return t.destroy(apos);
12
+ });
13
+
14
+ let jobModule;
15
+
16
+ it('should be a property of the apos object', async function() {
17
+ this.timeout(t.timeout);
18
+ this.slow(2000);
19
+
20
+ apos = await t.create({
21
+ root: module,
22
+ modules: {
23
+ article: {
24
+ extend: '@apostrophecms/piece-type'
25
+ }
26
+ }
27
+ });
28
+ jobModule = apos.modules['@apostrophecms/job'];
29
+ assert(apos.modules['@apostrophecms/job']);
30
+ });
31
+
32
+ it('has a related database collection', async function () {
33
+ assert(jobModule.db);
34
+ });
35
+
36
+ let jobOne;
37
+
38
+ it('should create a new job', async function () {
39
+ jobOne = await jobModule.start({});
40
+
41
+ assert(jobOne._id);
42
+
43
+ const found = await jobModule.db.findOne({ _id: jobOne._id });
44
+
45
+ assert(found);
46
+ assert(found.status === 'running');
47
+ assert(found.ended === false);
48
+ });
49
+
50
+ it('should end a job and mark it as successful', async function () {
51
+ const result = await jobModule.end(jobOne, 'success', { testing: 'testing' });
52
+
53
+ assert(result.result.nModified === 1);
54
+
55
+ const found = await jobModule.db.findOne({ _id: jobOne._id });
56
+
57
+ assert(found);
58
+ assert(found.status === 'completed');
59
+ assert(found.ended === true);
60
+ });
61
+ let jar;
62
+ it('should get admin jar', async () => {
63
+ await t.createAdmin(apos);
64
+
65
+ jar = await t.getUserJar(apos);
66
+
67
+ assert(jar);
68
+ });
69
+
70
+ it('should access a job via REST API GET request', async function () {
71
+ const job = await apos.http.get(`/api/v1/@apostrophecms/job/${jobOne._id}`, {
72
+ jar
73
+ });
74
+
75
+ assert(job._id === jobOne._id);
76
+ });
77
+
78
+ let articleIds;
79
+
80
+ it('can insert many test articles', async function () {
81
+ const req = apos.task.getReq();
82
+
83
+ const promises = [];
84
+
85
+ for (let i = 1; i <= 500; i++) {
86
+ promises.push(insert(req, apos.modules.article, 'article', {}, i));
87
+ }
88
+
89
+ const inserted = await Promise.all(promises);
90
+ articleIds = inserted.map(doc => doc._id);
91
+
92
+ assert(inserted.length === 500);
93
+ assert(!!inserted[0]._id);
94
+ });
95
+
96
+ let jobTwo;
97
+ it('can run a batch job', async function () {
98
+ const req = apos.task.getReq();
99
+
100
+ jobTwo = await jobModule.runBatch(
101
+ req,
102
+ articleIds,
103
+ async function(req, id) {
104
+ await apos.doc.db.updateOne({
105
+ _id: id
106
+ }, {
107
+ $set: {
108
+ checked: true
109
+ }
110
+ });
111
+ }
112
+ );
113
+
114
+ assert(!!jobTwo.jobId);
115
+ });
116
+
117
+ it('can follow the second job as it works', async function () {
118
+ const { completed } = await pollJob({
119
+ route: `${jobModule.action}/${jobTwo.jobId}`
120
+ }, {
121
+ jar
122
+ });
123
+
124
+ assert(completed === articleIds.length);
125
+ const index = Math.floor(Math.random() * (articleIds.length - 1));
126
+
127
+ const article = await apos.http.get(`/api/v1/article/${articleIds[index]}`, {
128
+ jar
129
+ });
130
+
131
+ assert(article.checked === true);
132
+ });
133
+
134
+ const logged = [];
135
+
136
+ let jobThree;
137
+
138
+ it('can run a generic job', async function () {
139
+ const req = apos.task.getReq();
140
+
141
+ jobThree = await jobModule.run(
142
+ req,
143
+ async function(req, reporters) {
144
+ let count = 1;
145
+ reporters.setTotal(articleIds.length);
146
+
147
+ for (const id of articleIds) {
148
+ await Promise.delay(3);
149
+ logged.push(id);
150
+ if (count % 2) {
151
+ reporters.success();
152
+ } else {
153
+ reporters.failure();
154
+ }
155
+ count++;
156
+ }
157
+ }
158
+ );
159
+
160
+ assert(!!jobThree.jobId);
161
+ });
162
+
163
+ it('can follow the third job as it works', async function () {
164
+ const route = `${jobModule.action}/${jobThree.jobId}`;
165
+ const { total } = await apos.http.get(route, { jar });
166
+ // Tests setTotal()
167
+ assert(total === articleIds.length);
168
+
169
+ const {
170
+ completed,
171
+ good,
172
+ bad
173
+ } = await pollJob({
174
+ route
175
+ }, {
176
+ jar
177
+ });
178
+
179
+ assert(completed === articleIds.length);
180
+ // Tests success()
181
+ assert(good === (articleIds.length / 2));
182
+ // Tests failure()
183
+ assert(bad === (articleIds.length / 2));
184
+ });
185
+ });
186
+
187
+ function padInteger (i, places) {
188
+ let s = i + '';
189
+ while (s.length < places) {
190
+ s = '0' + s;
191
+ }
192
+ return s;
193
+ }
194
+
195
+ async function insert (req, pieceModule, title, data, i) {
196
+ const docData = Object.assign(pieceModule.newInstance(), {
197
+ title: `${title} #${padInteger(i, 5)}`,
198
+ slug: `${title}-${padInteger(i, 5)}`,
199
+ ...data
200
+ });
201
+
202
+ return pieceModule.insert(req, docData);
203
+ };
204
+
205
+ async function pollJob(job, { jar }) {
206
+ const {
207
+ processed,
208
+ total,
209
+ good,
210
+ bad
211
+ } = await apos.http.get(job.route, { jar });
212
+
213
+ if (processed < total) {
214
+ Promise.delay(100);
215
+
216
+ return await pollJob(job, { jar });
217
+ } else {
218
+ return {
219
+ completed: processed,
220
+ good,
221
+ bad
222
+ };
223
+ }
224
+ }
@@ -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/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);
@@ -1246,4 +1263,21 @@ describe('Pieces', function() {
1246
1263
  assert(fs.readFileSync(path.join(__dirname, 'public', resume.attachment._url), 'utf8') === fs.readFileSync(path.join(__dirname, '/public/static-test.txt'), 'utf8'));
1247
1264
  });
1248
1265
 
1266
+ it('should convert a piece keeping only the present fields', async () => {
1267
+ const req = apos.task.getReq();
1268
+
1269
+ const inputPiece = {
1270
+ title: 'new product name'
1271
+ };
1272
+
1273
+ const existingPiece = {
1274
+ color: 'red'
1275
+ };
1276
+
1277
+ await apos.modules.product.convert(req, inputPiece, existingPiece, { presentFieldsOnly: true });
1278
+
1279
+ assert(Object.keys(existingPiece).length === 2);
1280
+ assert(existingPiece.title === 'new product name');
1281
+ assert(existingPiece.color === 'red');
1282
+ });
1249
1283
  });
@@ -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
+ });
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
  };