apostrophe 3.11.0 → 3.14.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.
- package/CHANGELOG.md +56 -0
- package/index.js +37 -1
- package/lib/moog.js +2 -2
- package/modules/@apostrophecms/asset/index.js +24 -9
- package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
- package/modules/@apostrophecms/attachment/index.js +1 -1
- package/modules/@apostrophecms/doc/index.js +9 -3
- package/modules/@apostrophecms/doc-type/index.js +2 -2
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +0 -7
- package/modules/@apostrophecms/express/index.js +50 -38
- package/modules/@apostrophecms/http/index.js +0 -20
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +23 -1
- package/modules/@apostrophecms/i18n/index.js +62 -13
- package/modules/@apostrophecms/login/index.js +282 -42
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +242 -77
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +108 -0
- package/modules/@apostrophecms/module/index.js +12 -2
- package/modules/@apostrophecms/page/index.js +98 -82
- package/modules/@apostrophecms/permission/index.js +1 -1
- package/modules/@apostrophecms/piece-page-type/index.js +1 -1
- package/modules/@apostrophecms/piece-type/index.js +86 -73
- package/modules/@apostrophecms/schema/index.js +18 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +12 -5
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +4 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +18 -0
- package/modules/@apostrophecms/util/index.js +3 -9
- package/modules/@apostrophecms/util/ui/src/http.js +1 -7
- package/package.json +2 -1
- package/test/express.js +2 -26
- package/test/http.js +0 -24
- package/test/login-requirements.js +328 -0
- package/test/modules/base-type/i18n/custom/en.json +4 -0
- package/test/modules/base-type/i18n/en.json +3 -0
- package/test/modules/nested-module-subdirs/example1/index.js +5 -0
- package/test/modules/nested-module-subdirs/modules.js +7 -0
- package/test/modules/subtype/i18n/custom/en.json +4 -0
- package/test/modules/subtype/index.js +7 -0
- package/test/pages-rest.js +39 -0
- package/test/pieces-page-type.js +63 -0
- package/test/static-i18n.js +28 -0
- package/test/with-nested-module-subdirs.js +32 -0
- package/test/without-nested-module-subdirs.js +31 -0
package/test/express.js
CHANGED
|
@@ -39,37 +39,13 @@ describe('Express', function() {
|
|
|
39
39
|
assert(body.toString() === 'ok');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it('should flunk a POST request
|
|
42
|
+
it('should flunk a POST request without the CSRF cookie', async function() {
|
|
43
43
|
try {
|
|
44
44
|
await apos.http.post('/tests/body', {
|
|
45
45
|
body: {
|
|
46
46
|
person: {
|
|
47
47
|
age: '30'
|
|
48
48
|
}
|
|
49
|
-
},
|
|
50
|
-
jar,
|
|
51
|
-
csrf: false
|
|
52
|
-
});
|
|
53
|
-
assert(false);
|
|
54
|
-
} catch (e) {
|
|
55
|
-
assert(e);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should flunk a POST request with the wrong CSRF token', async function() {
|
|
60
|
-
const csrfToken = 'BOGOSITY';
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
await apos.http.post('/tests/body', {
|
|
64
|
-
body: {
|
|
65
|
-
person: {
|
|
66
|
-
age: '30'
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
jar,
|
|
70
|
-
csrf: false,
|
|
71
|
-
headers: {
|
|
72
|
-
'X-XSRF-TOKEN': csrfToken
|
|
73
49
|
}
|
|
74
50
|
});
|
|
75
51
|
assert(false);
|
|
@@ -78,7 +54,7 @@ describe('Express', function() {
|
|
|
78
54
|
}
|
|
79
55
|
});
|
|
80
56
|
|
|
81
|
-
it('should use the extended bodyParser for submitted forms', async function() {
|
|
57
|
+
it('should use the extended bodyParser for submitted forms, and pass CSRF with the cookie', async function() {
|
|
82
58
|
|
|
83
59
|
const response = await apos.http.post('/tests/body', {
|
|
84
60
|
send: 'form',
|
package/test/http.js
CHANGED
|
@@ -40,30 +40,6 @@ describe('Http', function() {
|
|
|
40
40
|
assert(result.match(/logged out/));
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
it('should not be able to make an http POST request without csrf header', async () => {
|
|
44
|
-
try {
|
|
45
|
-
await apos.http.post('/csrf-test', {
|
|
46
|
-
jar,
|
|
47
|
-
csrf: false
|
|
48
|
-
});
|
|
49
|
-
assert(false);
|
|
50
|
-
} catch (e) {
|
|
51
|
-
assert(e.status === 403);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should be able to make an http POST request with manually built csrf header', async () => {
|
|
56
|
-
const response = await apos.http.post('/csrf-test?manual=1', {
|
|
57
|
-
jar,
|
|
58
|
-
headers: {
|
|
59
|
-
'X-XSRF-TOKEN': apos.http.getCookie(jar, '/', `${apos.options.shortName}.csrf`)
|
|
60
|
-
},
|
|
61
|
-
body: {},
|
|
62
|
-
csrf: false
|
|
63
|
-
});
|
|
64
|
-
assert(response.ok === true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
43
|
it('should be able to make an http POST request with csrf header via default csrf convenience of http.post', async () => {
|
|
68
44
|
const response = await apos.http.post('/csrf-test', {
|
|
69
45
|
jar,
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
const t = require('../test-lib/test.js');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
let apos;
|
|
5
|
+
|
|
6
|
+
describe('Login', function() {
|
|
7
|
+
|
|
8
|
+
this.timeout(20000);
|
|
9
|
+
|
|
10
|
+
after(function() {
|
|
11
|
+
return t.destroy(apos);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// EXISTENCE
|
|
15
|
+
|
|
16
|
+
it('should initialize', async function() {
|
|
17
|
+
apos = await t.create({
|
|
18
|
+
root: module,
|
|
19
|
+
modules: {
|
|
20
|
+
'@apostrophecms/login': {
|
|
21
|
+
requirements(self) {
|
|
22
|
+
return {
|
|
23
|
+
add: {
|
|
24
|
+
WeakCaptcha: {
|
|
25
|
+
phase: 'beforeSubmit',
|
|
26
|
+
async props(req) {
|
|
27
|
+
return {
|
|
28
|
+
hint: 'xyz'
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
async verify(req, data) {
|
|
32
|
+
if (data !== 'xyz') {
|
|
33
|
+
throw self.apos.error('invalid', 'captcha code incorrect');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
ExtraSecret: {
|
|
39
|
+
phase: 'afterPasswordVerified',
|
|
40
|
+
async props(req, user) {
|
|
41
|
+
return {
|
|
42
|
+
// Verify we had access to the user here
|
|
43
|
+
hint: user.username
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
async verify(req, data, user) {
|
|
47
|
+
if (data !== user.extraSecret) {
|
|
48
|
+
throw self.apos.error('invalid', 'extra secret incorrect');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
assert(apos.modules['@apostrophecms/login']);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should be able to insert test user', async function() {
|
|
63
|
+
assert(apos.user.newInstance);
|
|
64
|
+
const user = apos.user.newInstance();
|
|
65
|
+
assert(user);
|
|
66
|
+
|
|
67
|
+
user.title = 'Harry Putter';
|
|
68
|
+
user.username = 'HarryPutter';
|
|
69
|
+
user.password = 'crookshanks';
|
|
70
|
+
user.email = 'hputter@aol.com';
|
|
71
|
+
user.role = 'admin';
|
|
72
|
+
user.extraSecret = 'roll-on';
|
|
73
|
+
|
|
74
|
+
assert(user.type === '@apostrophecms/user');
|
|
75
|
+
assert(apos.user.insert);
|
|
76
|
+
const doc = await apos.user.insert(apos.task.getReq(), user);
|
|
77
|
+
assert(doc._id);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should not be able to login a user without meeting a beforeSubmit requirement', async function() {
|
|
81
|
+
|
|
82
|
+
const jar = apos.http.jar();
|
|
83
|
+
|
|
84
|
+
// establish session
|
|
85
|
+
let page = await apos.http.get(
|
|
86
|
+
'/',
|
|
87
|
+
{
|
|
88
|
+
jar
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
assert(page.match(/logged out/));
|
|
93
|
+
|
|
94
|
+
const context = await apos.http.post(
|
|
95
|
+
'/api/v1/@apostrophecms/login/context',
|
|
96
|
+
{
|
|
97
|
+
method: 'POST',
|
|
98
|
+
body: {},
|
|
99
|
+
jar
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
assert(context.requirementProps);
|
|
103
|
+
assert(context.requirementProps.WeakCaptcha);
|
|
104
|
+
assert.strictEqual(context.requirementProps.WeakCaptcha.hint, 'xyz');
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await apos.http.post(
|
|
108
|
+
'/api/v1/@apostrophecms/login/login',
|
|
109
|
+
{
|
|
110
|
+
method: 'POST',
|
|
111
|
+
body: {
|
|
112
|
+
username: 'HarryPutter',
|
|
113
|
+
password: 'crookshanks',
|
|
114
|
+
session: true
|
|
115
|
+
},
|
|
116
|
+
jar
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
assert(false);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
assert(e.status === 400);
|
|
122
|
+
assert.strictEqual(e.body.message, 'captcha code incorrect');
|
|
123
|
+
assert.strictEqual(e.body.data.requirement, 'WeakCaptcha');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Make sure it really didn't work
|
|
127
|
+
page = await apos.http.get(
|
|
128
|
+
'/',
|
|
129
|
+
{
|
|
130
|
+
jar
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
assert(page.match(/logged out/));
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should not be able to login a user with the wrong value for a beforeSubmit requirement', async function() {
|
|
138
|
+
|
|
139
|
+
const jar = apos.http.jar();
|
|
140
|
+
|
|
141
|
+
// establish session
|
|
142
|
+
let page = await apos.http.get(
|
|
143
|
+
'/',
|
|
144
|
+
{
|
|
145
|
+
jar
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
assert(page.match(/logged out/));
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
await apos.http.post(
|
|
153
|
+
'/api/v1/@apostrophecms/login/login',
|
|
154
|
+
{
|
|
155
|
+
method: 'POST',
|
|
156
|
+
body: {
|
|
157
|
+
username: 'HarryPutter',
|
|
158
|
+
password: 'crookshanks',
|
|
159
|
+
session: true,
|
|
160
|
+
requirements: {
|
|
161
|
+
WeakCaptcha: 'abc'
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
jar
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
assert(false);
|
|
168
|
+
} catch (e) {
|
|
169
|
+
assert(e.status === 400);
|
|
170
|
+
assert.strictEqual(e.body.message, 'captcha code incorrect');
|
|
171
|
+
assert.strictEqual(e.body.data.requirement, 'WeakCaptcha');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Make sure it really didn't work
|
|
175
|
+
page = await apos.http.get(
|
|
176
|
+
'/',
|
|
177
|
+
{
|
|
178
|
+
jar
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
assert(page.match(/logged out/));
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('initial login should produce an incompleteToken, convertible with the afterPasswordVerified requirements', async function() {
|
|
186
|
+
|
|
187
|
+
const jar = apos.http.jar();
|
|
188
|
+
|
|
189
|
+
// establish session
|
|
190
|
+
let page = await apos.http.get(
|
|
191
|
+
'/',
|
|
192
|
+
{
|
|
193
|
+
jar
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
assert(page.match(/logged out/));
|
|
198
|
+
|
|
199
|
+
const result = await apos.http.post(
|
|
200
|
+
'/api/v1/@apostrophecms/login/login',
|
|
201
|
+
{
|
|
202
|
+
method: 'POST',
|
|
203
|
+
body: {
|
|
204
|
+
username: 'HarryPutter',
|
|
205
|
+
password: 'crookshanks',
|
|
206
|
+
session: true,
|
|
207
|
+
requirements: {
|
|
208
|
+
WeakCaptcha: 'xyz'
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
jar
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
assert(result.incompleteToken);
|
|
216
|
+
|
|
217
|
+
// Make sure it did not create a login session prematurely
|
|
218
|
+
page = await apos.http.get(
|
|
219
|
+
'/',
|
|
220
|
+
{
|
|
221
|
+
jar
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
assert(page.match(/logged out/));
|
|
226
|
+
|
|
227
|
+
// Make sure it won't convert with an incorrect ExtraSecret
|
|
228
|
+
|
|
229
|
+
const token = result.incompleteToken;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
await apos.http.post('/api/v1/@apostrophecms/login/requirement-verify', {
|
|
233
|
+
body: {
|
|
234
|
+
incompleteToken: token,
|
|
235
|
+
session: true,
|
|
236
|
+
name: 'ExtraSecret',
|
|
237
|
+
value: 'roll-off'
|
|
238
|
+
},
|
|
239
|
+
jar
|
|
240
|
+
});
|
|
241
|
+
} catch ({ status, body }) {
|
|
242
|
+
assert(status === 400);
|
|
243
|
+
assert.strictEqual(body.message, 'extra secret incorrect');
|
|
244
|
+
assert.strictEqual(body.data.requirement, 'ExtraSecret');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// If we try the final login without
|
|
248
|
+
// having successfully verified all requirements we get an error
|
|
249
|
+
try {
|
|
250
|
+
await apos.http.post(
|
|
251
|
+
'/api/v1/@apostrophecms/login/login',
|
|
252
|
+
{
|
|
253
|
+
method: 'POST',
|
|
254
|
+
body: {
|
|
255
|
+
incompleteToken: token,
|
|
256
|
+
session: true
|
|
257
|
+
},
|
|
258
|
+
jar
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
} catch ({ status, body }) {
|
|
262
|
+
assert(status === 403);
|
|
263
|
+
assert.strictEqual(body.message, 'All requirements must be verified');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Make sure it did not create a login session prematurely
|
|
267
|
+
page = await apos.http.get(
|
|
268
|
+
'/',
|
|
269
|
+
{
|
|
270
|
+
jar
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
assert(page.match(/logged out/));
|
|
275
|
+
|
|
276
|
+
// Fetch props for afterPasswordVerified component
|
|
277
|
+
|
|
278
|
+
const props = await apos.http.post(
|
|
279
|
+
'/api/v1/@apostrophecms/login/requirement-props',
|
|
280
|
+
{
|
|
281
|
+
method: 'POST',
|
|
282
|
+
body: {
|
|
283
|
+
incompleteToken: token,
|
|
284
|
+
name: 'ExtraSecret'
|
|
285
|
+
},
|
|
286
|
+
jar
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
assert.strictEqual(props.hint, 'HarryPutter');
|
|
291
|
+
|
|
292
|
+
// Now convert token to an actual login session
|
|
293
|
+
// by providing the post-password-verification requirements,
|
|
294
|
+
// correctly
|
|
295
|
+
|
|
296
|
+
await apos.http.post('/api/v1/@apostrophecms/login/requirement-verify', {
|
|
297
|
+
body: {
|
|
298
|
+
incompleteToken: token,
|
|
299
|
+
session: true,
|
|
300
|
+
name: 'ExtraSecret',
|
|
301
|
+
value: 'roll-on'
|
|
302
|
+
},
|
|
303
|
+
jar
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
await apos.http.post(
|
|
307
|
+
'/api/v1/@apostrophecms/login/login',
|
|
308
|
+
{
|
|
309
|
+
method: 'POST',
|
|
310
|
+
body: {
|
|
311
|
+
incompleteToken: token,
|
|
312
|
+
session: true
|
|
313
|
+
},
|
|
314
|
+
jar
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
page = await apos.http.get(
|
|
319
|
+
'/',
|
|
320
|
+
{
|
|
321
|
+
jar
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
assert(page.match(/logged in/));
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
});
|
package/test/pages-rest.js
CHANGED
|
@@ -1486,6 +1486,45 @@ describe('Pages REST', function() {
|
|
|
1486
1486
|
assert(doc.title === 'Advisory Test Patched Again');
|
|
1487
1487
|
});
|
|
1488
1488
|
|
|
1489
|
+
let diacriticsId;
|
|
1490
|
+
it('is able to make a page including diacritics', async function() {
|
|
1491
|
+
const body = {
|
|
1492
|
+
slug: '/ḑiaçritiçs-čharaćters',
|
|
1493
|
+
visibility: 'public',
|
|
1494
|
+
type: 'test-page',
|
|
1495
|
+
title: 'Ḑiaçritiçs Čharaćters',
|
|
1496
|
+
_targetId: '_home',
|
|
1497
|
+
_position: '1'
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
const page = await apos.http.post('/api/v1/@apostrophecms/page', {
|
|
1501
|
+
body,
|
|
1502
|
+
jar
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
assert(page);
|
|
1506
|
+
assert(page.title === 'Ḑiaçritiçs Čharaćters');
|
|
1507
|
+
diacriticsId = page._id;
|
|
1508
|
+
// Accesses the published page.
|
|
1509
|
+
const published = await apos.http.get('/ḑiaçritiçs-čharaćters');
|
|
1510
|
+
assert(published);
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
it('can archive the diacritics page then access the draft preview', async function () {
|
|
1514
|
+
// Now only a draft preview will be available.
|
|
1515
|
+
await apos.page.archive(apos.task.getReq(), diacriticsId);
|
|
1516
|
+
|
|
1517
|
+
try {
|
|
1518
|
+
const rendered = await apos.http.get('/ḑiaçritiçs-čharaćters', {
|
|
1519
|
+
jar
|
|
1520
|
+
});
|
|
1521
|
+
assert(rendered.match(/Sing to me, Oh Muse\./));
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
console.error(error);
|
|
1524
|
+
assert(false);
|
|
1525
|
+
}
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1489
1528
|
let jar2;
|
|
1490
1529
|
|
|
1491
1530
|
it('should be able to log in as second user', async () => {
|
package/test/pieces-page-type.js
CHANGED
|
@@ -34,6 +34,24 @@ describe('Pieces Pages', function() {
|
|
|
34
34
|
perPage: 10
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
+
home: {
|
|
38
|
+
extend: '@apostrophecms/piece-type',
|
|
39
|
+
options: {
|
|
40
|
+
name: 'home',
|
|
41
|
+
label: 'Home',
|
|
42
|
+
alias: 'home',
|
|
43
|
+
sort: { title: 1 }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
'home-page': {
|
|
47
|
+
extend: '@apostrophecms/piece-page-type',
|
|
48
|
+
options: {
|
|
49
|
+
name: 'homePiecePage',
|
|
50
|
+
label: 'Home Piece Page',
|
|
51
|
+
alias: 'homePiecePage',
|
|
52
|
+
perPage: 10
|
|
53
|
+
}
|
|
54
|
+
},
|
|
37
55
|
'@apostrophecms/page': {
|
|
38
56
|
options: {
|
|
39
57
|
park: [
|
|
@@ -42,6 +60,12 @@ describe('Pieces Pages', function() {
|
|
|
42
60
|
type: 'eventPage',
|
|
43
61
|
slug: '/events',
|
|
44
62
|
parkedId: 'events'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
title: 'Home piece page',
|
|
66
|
+
type: 'homePiecePage',
|
|
67
|
+
slug: '/',
|
|
68
|
+
parkedId: 'home'
|
|
45
69
|
}
|
|
46
70
|
]
|
|
47
71
|
}
|
|
@@ -81,6 +105,36 @@ describe('Pieces Pages', function() {
|
|
|
81
105
|
return apos.doc.db.insertMany(testItems);
|
|
82
106
|
});
|
|
83
107
|
|
|
108
|
+
it('should be able to use db to insert test "home" pieces', async function() {
|
|
109
|
+
assert(apos.modules.home);
|
|
110
|
+
const testItems = [];
|
|
111
|
+
const total = 100;
|
|
112
|
+
for (let i = 1; (i <= total); i++) {
|
|
113
|
+
const paddedInt = apos.launder.padInteger(i, 3);
|
|
114
|
+
|
|
115
|
+
testItems.push({
|
|
116
|
+
_id: 'home' + paddedInt,
|
|
117
|
+
slug: 'home-' + paddedInt,
|
|
118
|
+
visibility: 'public',
|
|
119
|
+
type: 'home',
|
|
120
|
+
title: 'Home ' + paddedInt,
|
|
121
|
+
titleSortified: 'home ' + paddedInt,
|
|
122
|
+
body: {
|
|
123
|
+
metaType: 'area',
|
|
124
|
+
_id: apos.util.generateId(),
|
|
125
|
+
items: [
|
|
126
|
+
{
|
|
127
|
+
metaType: 'widget',
|
|
128
|
+
type: '@apostrophecms/rich-text',
|
|
129
|
+
content: '<p>This is some content.</p>'
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return apos.doc.db.insertMany(testItems);
|
|
137
|
+
});
|
|
84
138
|
it('should populate the ._url property of pieces in any docs query', async function() {
|
|
85
139
|
const piece = await apos.doc.find(apos.task.getAnonReq(), {
|
|
86
140
|
type: 'event',
|
|
@@ -104,6 +158,15 @@ describe('Pieces Pages', function() {
|
|
|
104
158
|
assert((!piece._url) || (piece._url.match(/undefined/)));
|
|
105
159
|
});
|
|
106
160
|
|
|
161
|
+
it('should not create a double-slashed _url on a piece-page-type set as the homepage', async function() {
|
|
162
|
+
const piece = await apos.doc.find(apos.task.getAnonReq(), {
|
|
163
|
+
type: 'home',
|
|
164
|
+
title: 'Home 001'
|
|
165
|
+
}).toObject();
|
|
166
|
+
assert(piece);
|
|
167
|
+
assert(piece._url === '/home-001');
|
|
168
|
+
});
|
|
169
|
+
|
|
107
170
|
it('should correctly populate the ._url property of pieces in a docs query if _url itself is "projected"', async function() {
|
|
108
171
|
const piece = await apos.doc.find(apos.task.getAnonReq(), {
|
|
109
172
|
type: 'event',
|
package/test/static-i18n.js
CHANGED
|
@@ -33,9 +33,19 @@ describe('static i18n', function() {
|
|
|
33
33
|
'apos-fr': {
|
|
34
34
|
options: {
|
|
35
35
|
i18n: {
|
|
36
|
+
// Legacy technique must work
|
|
36
37
|
ns: 'apostrophe'
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
},
|
|
41
|
+
// A base class that contributes some namespaced phrases in the new style way (subdirs)
|
|
42
|
+
'base-type': {
|
|
43
|
+
instantiate: false
|
|
44
|
+
},
|
|
45
|
+
// Also contributes namespaced phrases in the new style way (subdirs)
|
|
46
|
+
// plus default locale phrases in the root i18n folder
|
|
47
|
+
subtype: {
|
|
48
|
+
extend: 'base-type'
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
51
|
});
|
|
@@ -60,4 +70,22 @@ describe('static i18n', function() {
|
|
|
60
70
|
assert.strictEqual(apos.task.getReq({ locale: 'fr' }).t('apostrophe:richTextAlignCenter'), 'Aligner Le Centre');
|
|
61
71
|
});
|
|
62
72
|
|
|
73
|
+
it('should fetch default locale phrases from main i18n dir with no i18n option necessary', function() {
|
|
74
|
+
assert.strictEqual(apos.task.getReq().t('defaultTestOne'), 'Default Test One');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should fetch custom locale phrases from corresponding subdir', function() {
|
|
78
|
+
assert.strictEqual(apos.task.getReq().t('custom:customTestTwo'), 'Custom Test Two From Base Type');
|
|
79
|
+
assert.strictEqual(apos.task.getReq().t('custom:customTestThree'), 'Custom Test Three From Subtype');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('last appearance in inheritance + configuration order wins', function() {
|
|
83
|
+
assert.strictEqual(apos.task.getReq().t('custom:customTestOne'), 'Custom Test One From Subtype');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should honor the browser: true flag in the i18n section of an index.js file', function() {
|
|
87
|
+
const browserData = apos.i18n.getBrowserData(apos.task.getReq());
|
|
88
|
+
assert.strictEqual(browserData.i18n.en.custom.customTestOne, 'Custom Test One From Subtype');
|
|
89
|
+
});
|
|
90
|
+
|
|
63
91
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const t = require('../test-lib/test.js');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
describe('With Nested Module Subdirs', function() {
|
|
5
|
+
this.timeout(t.timeout);
|
|
6
|
+
|
|
7
|
+
let apos;
|
|
8
|
+
|
|
9
|
+
after(function () {
|
|
10
|
+
return t.destroy(apos);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/// ///
|
|
14
|
+
// EXISTENCE
|
|
15
|
+
/// ///
|
|
16
|
+
|
|
17
|
+
it('should initialize', async function() {
|
|
18
|
+
apos = await t.create({
|
|
19
|
+
root: module,
|
|
20
|
+
nestedModuleSubdirs: true,
|
|
21
|
+
modules: {
|
|
22
|
+
example1: {}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
assert(apos.modules.example1);
|
|
26
|
+
// With nestedModuleSubdirs switched on, the index.js should be found,
|
|
27
|
+
// and modules.js should be loaded
|
|
28
|
+
assert(apos.modules.example1.options.folderLevelOption);
|
|
29
|
+
assert(apos.modules.example1.initialized);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const t = require('../test-lib/test.js');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
describe('Without Nested Module Subdirs', function() {
|
|
5
|
+
this.timeout(t.timeout);
|
|
6
|
+
|
|
7
|
+
let apos;
|
|
8
|
+
|
|
9
|
+
after(function () {
|
|
10
|
+
return t.destroy(apos);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/// ///
|
|
14
|
+
// EXISTENCE
|
|
15
|
+
/// ///
|
|
16
|
+
|
|
17
|
+
it('should initialize', async function() {
|
|
18
|
+
apos = await t.create({
|
|
19
|
+
root: module,
|
|
20
|
+
modules: {
|
|
21
|
+
example1: {}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
assert(apos.modules.example1);
|
|
25
|
+
// Should fail because we didn't turn on nestedModuleSubdirs,
|
|
26
|
+
// so the index.js was not found and modules.js was not loaded
|
|
27
|
+
assert(!apos.modules.example1.options.folderLevelOption);
|
|
28
|
+
assert(!apos.modules.example1.initialized);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
});
|