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.
- package/.eslintrc +4 -0
- package/.scratch.md +2 -0
- package/CHANGELOG.md +96 -3
- package/README.md +1 -1
- package/index.js +108 -3
- package/lib/moog-require.js +23 -0
- package/lib/moog.js +1 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
- package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
- package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +2 -2
- package/modules/@apostrophecms/asset/index.js +77 -13
- package/modules/@apostrophecms/attachment/index.js +1 -0
- package/modules/@apostrophecms/db/index.js +5 -6
- package/modules/@apostrophecms/doc/index.js +2 -0
- package/modules/@apostrophecms/doc-type/index.js +39 -16
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
- package/modules/@apostrophecms/i18n/i18n/en.json +19 -6
- package/modules/@apostrophecms/i18n/i18n/es.json +382 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +379 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +380 -0
- package/modules/@apostrophecms/i18n/index.js +10 -1
- package/modules/@apostrophecms/image/index.js +2 -1
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +2 -1
- package/modules/@apostrophecms/image-widget/index.js +2 -1
- package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
- package/modules/@apostrophecms/job/index.js +164 -212
- package/modules/@apostrophecms/login/index.js +1 -16
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +5 -0
- package/modules/@apostrophecms/migration/index.js +1 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +8 -6
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
- package/modules/@apostrophecms/notification/index.js +116 -8
- package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
- package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
- package/modules/@apostrophecms/page/index.js +37 -30
- package/modules/@apostrophecms/permission/index.js +1 -1
- package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
- package/modules/@apostrophecms/piece-type/index.js +178 -61
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -47
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +35 -6
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +1 -3
- package/modules/@apostrophecms/schema/index.js +97 -20
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
- package/modules/@apostrophecms/task/index.js +2 -2
- package/modules/@apostrophecms/template/index.js +63 -36
- package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
- package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
- package/modules/@apostrophecms/util/index.js +2 -2
- package/modules/@apostrophecms/util/ui/src/http.js +12 -8
- package/modules/@apostrophecms/util/ui/src/util.js +15 -0
- package/modules/@apostrophecms/widget-type/index.js +1 -1
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
- package/package.json +3 -3
- package/test/extra_node_modules/improve-global/index.js +7 -0
- package/test/extra_node_modules/improve-piece-type/index.js +7 -0
- package/test/improve-overrides.js +30 -0
- package/test/job.js +224 -0
- package/test/modules/@apostrophecms/global/index.js +8 -0
- package/test/modules/fragment-all/views/aux-test.html +7 -0
- package/test/modules/fragment-all/views/fragment.html +5 -0
- package/test/package.json +5 -4
- package/test/pieces.js +34 -0
- package/test/reverse-relationship.js +170 -0
- package/test/templates.js +7 -1
- package/test-lib/test.js +23 -12
- package/test-lib/util.js +33 -0
|
@@ -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
|
+
}
|
package/test/package.json
CHANGED
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
|
-
|
|
5
|
-
fs.
|
|
6
|
-
fs.
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"apostrophe": "^3.0.0"
|
|
22
|
+
const packageJsonInfo = {
|
|
23
|
+
name: 'test',
|
|
24
|
+
dependencies: {
|
|
25
|
+
apostrophe: '^3.0.0'
|
|
20
26
|
},
|
|
21
|
-
|
|
22
|
-
|
|
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
|
};
|