adapt-authoring-adaptframework 2.3.2 → 2.3.3
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.
|
@@ -70,8 +70,9 @@ class AdaptFrameworkModule extends AbstractModule {
|
|
|
70
70
|
*/
|
|
71
71
|
this.contentMigrations = []
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
this.app.waitForModule('content').then(content => {
|
|
74
|
+
content.accessCheckHook.tap(this.checkContentAccess.bind(this))
|
|
75
|
+
})
|
|
75
76
|
|
|
76
77
|
await this.installFramework()
|
|
77
78
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapt-authoring-adaptframework",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "Adapt framework integration for the Adapt authoring tool",
|
|
5
5
|
"homepage": "https://github.com/adapt-security/adapt-authoring-adaptframework",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -9,6 +9,34 @@ import AdaptFrameworkBuild from '../lib/AdaptFrameworkBuild.js'
|
|
|
9
9
|
|
|
10
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
|
|
12
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
function createBuild (overrides = {}) {
|
|
15
|
+
return new AdaptFrameworkBuild({
|
|
16
|
+
action: 'preview', courseId: 'c1', userId: 'u1', ...overrides
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createEmptyCourseData () {
|
|
21
|
+
return {
|
|
22
|
+
course: { dir: '/tmp', fileName: 'course.json', data: undefined },
|
|
23
|
+
config: { dir: '/tmp', fileName: 'config.json', data: undefined },
|
|
24
|
+
contentObject: { dir: '/tmp', fileName: 'contentObjects.json', data: [] },
|
|
25
|
+
article: { dir: '/tmp', fileName: 'articles.json', data: [] },
|
|
26
|
+
block: { dir: '/tmp', fileName: 'blocks.json', data: [] },
|
|
27
|
+
component: { dir: '/tmp', fileName: 'components.json', data: [] }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createTransformBuild (overrides = {}) {
|
|
32
|
+
const build = createBuild(overrides)
|
|
33
|
+
build.idMap = overrides.idMap || {}
|
|
34
|
+
build.assetData = overrides.assetData || { idMap: {} }
|
|
35
|
+
build.enabledPlugins = overrides.enabledPlugins || []
|
|
36
|
+
build.courseData = overrides.courseData || { course: { dir: '/tmp', data: {} } }
|
|
37
|
+
return build
|
|
38
|
+
}
|
|
39
|
+
|
|
12
40
|
/**
|
|
13
41
|
* transformContentItems is async and calls App.instance.waitForModule('tags')
|
|
14
42
|
* at the end. We extract the synchronous logic here to test it in isolation.
|
|
@@ -44,7 +72,7 @@ function transformContentItemsSync (build, items) {
|
|
|
44
72
|
describe('AdaptFrameworkBuild', () => {
|
|
45
73
|
describe('constructor', () => {
|
|
46
74
|
it('should set action and related boolean flags for preview', () => {
|
|
47
|
-
const build =
|
|
75
|
+
const build = createBuild()
|
|
48
76
|
assert.equal(build.action, 'preview')
|
|
49
77
|
assert.equal(build.isPreview, true)
|
|
50
78
|
assert.equal(build.isPublish, false)
|
|
@@ -52,64 +80,64 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
52
80
|
})
|
|
53
81
|
|
|
54
82
|
it('should set action and related boolean flags for publish', () => {
|
|
55
|
-
const build =
|
|
83
|
+
const build = createBuild({ action: 'publish' })
|
|
56
84
|
assert.equal(build.isPreview, false)
|
|
57
85
|
assert.equal(build.isPublish, true)
|
|
58
86
|
assert.equal(build.isExport, false)
|
|
59
87
|
})
|
|
60
88
|
|
|
61
89
|
it('should set action and related boolean flags for export', () => {
|
|
62
|
-
const build =
|
|
90
|
+
const build = createBuild({ action: 'export' })
|
|
63
91
|
assert.equal(build.isPreview, false)
|
|
64
92
|
assert.equal(build.isPublish, false)
|
|
65
93
|
assert.equal(build.isExport, true)
|
|
66
94
|
})
|
|
67
95
|
|
|
68
96
|
it('should default compress to false for preview', () => {
|
|
69
|
-
const build =
|
|
97
|
+
const build = createBuild()
|
|
70
98
|
assert.equal(build.compress, false)
|
|
71
99
|
})
|
|
72
100
|
|
|
73
101
|
it('should default compress to true for publish', () => {
|
|
74
|
-
const build =
|
|
102
|
+
const build = createBuild({ action: 'publish' })
|
|
75
103
|
assert.equal(build.compress, true)
|
|
76
104
|
})
|
|
77
105
|
|
|
78
106
|
it('should default compress to true for export', () => {
|
|
79
|
-
const build =
|
|
107
|
+
const build = createBuild({ action: 'export' })
|
|
80
108
|
assert.equal(build.compress, true)
|
|
81
109
|
})
|
|
82
110
|
|
|
83
111
|
it('should allow overriding compress', () => {
|
|
84
|
-
const build =
|
|
112
|
+
const build = createBuild({ compress: true })
|
|
85
113
|
assert.equal(build.compress, true)
|
|
86
114
|
})
|
|
87
115
|
|
|
88
116
|
it('should set courseId and userId', () => {
|
|
89
|
-
const build =
|
|
117
|
+
const build = createBuild({ courseId: 'course123', userId: 'user456' })
|
|
90
118
|
assert.equal(build.courseId, 'course123')
|
|
91
119
|
assert.equal(build.userId, 'user456')
|
|
92
120
|
})
|
|
93
121
|
|
|
94
122
|
it('should set expiresAt when provided', () => {
|
|
95
123
|
const expires = '2025-01-01T00:00:00.000Z'
|
|
96
|
-
const build =
|
|
124
|
+
const build = createBuild({ expiresAt: expires })
|
|
97
125
|
assert.equal(build.expiresAt, expires)
|
|
98
126
|
})
|
|
99
127
|
|
|
100
128
|
it('should initialise courseData as empty object', () => {
|
|
101
|
-
const build =
|
|
129
|
+
const build = createBuild()
|
|
102
130
|
assert.deepEqual(build.courseData, {})
|
|
103
131
|
})
|
|
104
132
|
|
|
105
133
|
it('should initialise enabledPlugins and disabledPlugins as empty arrays', () => {
|
|
106
|
-
const build =
|
|
134
|
+
const build = createBuild()
|
|
107
135
|
assert.deepEqual(build.enabledPlugins, [])
|
|
108
136
|
assert.deepEqual(build.disabledPlugins, [])
|
|
109
137
|
})
|
|
110
138
|
|
|
111
139
|
it('should set collectionName to "adaptbuilds"', () => {
|
|
112
|
-
const build =
|
|
140
|
+
const build = createBuild()
|
|
113
141
|
assert.equal(build.collectionName, 'adaptbuilds')
|
|
114
142
|
})
|
|
115
143
|
})
|
|
@@ -136,7 +164,7 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
136
164
|
|
|
137
165
|
describe('#createIdMap()', () => {
|
|
138
166
|
it('should create a mapping of _id to _friendlyId', () => {
|
|
139
|
-
const build =
|
|
167
|
+
const build = createBuild()
|
|
140
168
|
const items = [
|
|
141
169
|
{ _id: 'abc', _friendlyId: 'friendly-abc' },
|
|
142
170
|
{ _id: 'def', _friendlyId: 'friendly-def' }
|
|
@@ -149,7 +177,7 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
149
177
|
})
|
|
150
178
|
|
|
151
179
|
it('should handle items without _friendlyId', () => {
|
|
152
|
-
const build =
|
|
180
|
+
const build = createBuild()
|
|
153
181
|
const items = [{ _id: 'abc' }]
|
|
154
182
|
build.createIdMap(items)
|
|
155
183
|
assert.equal(build.idMap.abc, undefined)
|
|
@@ -158,15 +186,8 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
158
186
|
|
|
159
187
|
describe('#sortContentItems()', () => {
|
|
160
188
|
it('should sort content into correct types', () => {
|
|
161
|
-
const build =
|
|
162
|
-
build.courseData =
|
|
163
|
-
course: { dir: '/tmp', fileName: 'course.json', data: undefined },
|
|
164
|
-
config: { dir: '/tmp', fileName: 'config.json', data: undefined },
|
|
165
|
-
contentObject: { dir: '/tmp', fileName: 'contentObjects.json', data: [] },
|
|
166
|
-
article: { dir: '/tmp', fileName: 'articles.json', data: [] },
|
|
167
|
-
block: { dir: '/tmp', fileName: 'blocks.json', data: [] },
|
|
168
|
-
component: { dir: '/tmp', fileName: 'components.json', data: [] }
|
|
169
|
-
}
|
|
189
|
+
const build = createBuild()
|
|
190
|
+
build.courseData = createEmptyCourseData()
|
|
170
191
|
const items = [
|
|
171
192
|
{ _id: 'course1', _type: 'course' },
|
|
172
193
|
{ _id: 'config1', _type: 'config' },
|
|
@@ -186,15 +207,8 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
186
207
|
})
|
|
187
208
|
|
|
188
209
|
it('should sort siblings by _sortOrder', () => {
|
|
189
|
-
const build =
|
|
190
|
-
build.courseData =
|
|
191
|
-
course: { dir: '/tmp', fileName: 'course.json', data: undefined },
|
|
192
|
-
config: { dir: '/tmp', fileName: 'config.json', data: undefined },
|
|
193
|
-
contentObject: { dir: '/tmp', fileName: 'contentObjects.json', data: [] },
|
|
194
|
-
article: { dir: '/tmp', fileName: 'articles.json', data: [] },
|
|
195
|
-
block: { dir: '/tmp', fileName: 'blocks.json', data: [] },
|
|
196
|
-
component: { dir: '/tmp', fileName: 'components.json', data: [] }
|
|
197
|
-
}
|
|
210
|
+
const build = createBuild()
|
|
211
|
+
build.courseData = createEmptyCourseData()
|
|
198
212
|
const items = [
|
|
199
213
|
{ _id: 'course1', _type: 'course' },
|
|
200
214
|
{ _id: 'page2', _type: 'page', _parentId: 'course1', _sortOrder: 2 },
|
|
@@ -209,15 +223,8 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
209
223
|
})
|
|
210
224
|
|
|
211
225
|
it('should categorise "menu" type as contentObject', () => {
|
|
212
|
-
const build =
|
|
213
|
-
build.courseData =
|
|
214
|
-
course: { dir: '/tmp', fileName: 'course.json', data: undefined },
|
|
215
|
-
config: { dir: '/tmp', fileName: 'config.json', data: undefined },
|
|
216
|
-
contentObject: { dir: '/tmp', fileName: 'contentObjects.json', data: [] },
|
|
217
|
-
article: { dir: '/tmp', fileName: 'articles.json', data: [] },
|
|
218
|
-
block: { dir: '/tmp', fileName: 'blocks.json', data: [] },
|
|
219
|
-
component: { dir: '/tmp', fileName: 'components.json', data: [] }
|
|
220
|
-
}
|
|
226
|
+
const build = createBuild()
|
|
227
|
+
build.courseData = createEmptyCourseData()
|
|
221
228
|
const items = [
|
|
222
229
|
{ _id: 'course1', _type: 'course' },
|
|
223
230
|
{ _id: 'menu1', _type: 'menu', _parentId: 'course1', _sortOrder: 1 }
|
|
@@ -229,15 +236,8 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
229
236
|
})
|
|
230
237
|
|
|
231
238
|
it('should sort deeply nested content in global order', () => {
|
|
232
|
-
const build =
|
|
233
|
-
build.courseData =
|
|
234
|
-
course: { dir: '/tmp', fileName: 'course.json', data: undefined },
|
|
235
|
-
config: { dir: '/tmp', fileName: 'config.json', data: undefined },
|
|
236
|
-
contentObject: { dir: '/tmp', fileName: 'contentObjects.json', data: [] },
|
|
237
|
-
article: { dir: '/tmp', fileName: 'articles.json', data: [] },
|
|
238
|
-
block: { dir: '/tmp', fileName: 'blocks.json', data: [] },
|
|
239
|
-
component: { dir: '/tmp', fileName: 'components.json', data: [] }
|
|
240
|
-
}
|
|
239
|
+
const build = createBuild()
|
|
240
|
+
build.courseData = createEmptyCourseData()
|
|
241
241
|
const items = [
|
|
242
242
|
{ _id: 'course1', _type: 'course' },
|
|
243
243
|
{ _id: 'page1', _type: 'page', _parentId: 'course1', _sortOrder: 1 },
|
|
@@ -257,101 +257,69 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
257
257
|
|
|
258
258
|
describe('#transformContentItems()', () => {
|
|
259
259
|
it('should replace _courseId and _parentId with friendlyIds from idMap', () => {
|
|
260
|
-
const build =
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
265
|
-
|
|
266
|
-
const items = [
|
|
267
|
-
{ _courseId: 'course1', _parentId: 'page1' }
|
|
268
|
-
]
|
|
260
|
+
const build = createTransformBuild({
|
|
261
|
+
idMap: { course1: 'co-friendly', page1: 'page-friendly' }
|
|
262
|
+
})
|
|
263
|
+
const items = [{ _courseId: 'course1', _parentId: 'page1' }]
|
|
269
264
|
transformContentItemsSync(build, items)
|
|
270
265
|
assert.equal(items[0]._courseId, 'co-friendly')
|
|
271
266
|
assert.equal(items[0]._parentId, 'page-friendly')
|
|
272
267
|
})
|
|
273
268
|
|
|
274
269
|
it('should replace _id with _friendlyId when present', () => {
|
|
275
|
-
const build =
|
|
276
|
-
build.idMap = {}
|
|
277
|
-
build.assetData = { idMap: {} }
|
|
278
|
-
build.enabledPlugins = []
|
|
279
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
280
|
-
|
|
270
|
+
const build = createTransformBuild()
|
|
281
271
|
const items = [{ _id: 'abc', _friendlyId: 'my-friendly' }]
|
|
282
272
|
transformContentItemsSync(build, items)
|
|
283
273
|
assert.equal(items[0]._id, 'my-friendly')
|
|
284
274
|
})
|
|
285
275
|
|
|
286
276
|
it('should not replace _id when _friendlyId is absent', () => {
|
|
287
|
-
const build =
|
|
288
|
-
build.idMap = {}
|
|
289
|
-
build.assetData = { idMap: {} }
|
|
290
|
-
build.enabledPlugins = []
|
|
291
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
292
|
-
|
|
277
|
+
const build = createTransformBuild()
|
|
293
278
|
const items = [{ _id: 'abc' }]
|
|
294
279
|
transformContentItemsSync(build, items)
|
|
295
280
|
assert.equal(items[0]._id, 'abc')
|
|
296
281
|
})
|
|
297
282
|
|
|
298
283
|
it('should replace asset _ids with relative paths', () => {
|
|
299
|
-
const build =
|
|
300
|
-
|
|
284
|
+
const build = createTransformBuild({
|
|
285
|
+
assetData: { idMap: { asset123: '/build/course/assets/image.png' } }
|
|
286
|
+
})
|
|
301
287
|
build.courseDir = '/build/course'
|
|
302
|
-
build.assetData = { idMap: { asset123: '/build/course/assets/image.png' } }
|
|
303
|
-
build.enabledPlugins = []
|
|
304
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
305
|
-
|
|
306
288
|
const items = [{ graphic: 'asset123' }]
|
|
307
289
|
transformContentItemsSync(build, items)
|
|
308
290
|
assert.equal(items[0].graphic, 'course/assets/image.png')
|
|
309
291
|
})
|
|
310
292
|
|
|
311
293
|
it('should resolve _component to targetAttribute', () => {
|
|
312
|
-
const build =
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
]
|
|
318
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
319
|
-
|
|
294
|
+
const build = createTransformBuild({
|
|
295
|
+
enabledPlugins: [
|
|
296
|
+
{ name: 'adapt-contrib-text', targetAttribute: '_text', type: 'component' }
|
|
297
|
+
]
|
|
298
|
+
})
|
|
320
299
|
const items = [{ _component: 'adapt-contrib-text' }]
|
|
321
300
|
transformContentItemsSync(build, items)
|
|
322
301
|
assert.equal(items[0]._component, 'text')
|
|
323
302
|
})
|
|
324
303
|
|
|
325
304
|
it('should keep _component as-is when plugin not found', () => {
|
|
326
|
-
const build =
|
|
327
|
-
build.idMap = {}
|
|
328
|
-
build.assetData = { idMap: {} }
|
|
329
|
-
build.enabledPlugins = []
|
|
330
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
331
|
-
|
|
305
|
+
const build = createTransformBuild()
|
|
332
306
|
const items = [{ _component: 'unknown-plugin' }]
|
|
333
307
|
transformContentItemsSync(build, items)
|
|
334
308
|
assert.equal(items[0]._component, 'unknown-plugin')
|
|
335
309
|
})
|
|
336
310
|
|
|
337
311
|
it('should move globals into nested _extensions object', () => {
|
|
338
|
-
const build =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
dir: '/tmp',
|
|
347
|
-
data: {
|
|
348
|
-
_globals: {
|
|
349
|
-
_trickle: { label: 'Trickle' }
|
|
350
|
-
}
|
|
312
|
+
const build = createTransformBuild({
|
|
313
|
+
enabledPlugins: [
|
|
314
|
+
{ name: 'adapt-contrib-trickle', targetAttribute: '_trickle', type: 'extension' }
|
|
315
|
+
],
|
|
316
|
+
courseData: {
|
|
317
|
+
course: {
|
|
318
|
+
dir: '/tmp',
|
|
319
|
+
data: { _globals: { _trickle: { label: 'Trickle' } } }
|
|
351
320
|
}
|
|
352
321
|
}
|
|
353
|
-
}
|
|
354
|
-
|
|
322
|
+
})
|
|
355
323
|
transformContentItemsSync(build, [])
|
|
356
324
|
const globals = build.courseData.course.data._globals
|
|
357
325
|
assert.equal(globals._extensions._trickle.label, 'Trickle')
|
|
@@ -359,35 +327,24 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
359
327
|
})
|
|
360
328
|
|
|
361
329
|
it('should use _components key for component type globals', () => {
|
|
362
|
-
const build =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
dir: '/tmp',
|
|
371
|
-
data: {
|
|
372
|
-
_globals: {
|
|
373
|
-
_text: { ariaRegion: 'Text' }
|
|
374
|
-
}
|
|
330
|
+
const build = createTransformBuild({
|
|
331
|
+
enabledPlugins: [
|
|
332
|
+
{ name: 'adapt-contrib-text', targetAttribute: '_text', type: 'component' }
|
|
333
|
+
],
|
|
334
|
+
courseData: {
|
|
335
|
+
course: {
|
|
336
|
+
dir: '/tmp',
|
|
337
|
+
data: { _globals: { _text: { ariaRegion: 'Text' } } }
|
|
375
338
|
}
|
|
376
339
|
}
|
|
377
|
-
}
|
|
378
|
-
|
|
340
|
+
})
|
|
379
341
|
transformContentItemsSync(build, [])
|
|
380
342
|
const globals = build.courseData.course.data._globals
|
|
381
343
|
assert.equal(globals._components._text.ariaRegion, 'Text')
|
|
382
344
|
})
|
|
383
345
|
|
|
384
346
|
it('should keep _courseId as-is when not in idMap', () => {
|
|
385
|
-
const build =
|
|
386
|
-
build.idMap = {}
|
|
387
|
-
build.assetData = { idMap: {} }
|
|
388
|
-
build.enabledPlugins = []
|
|
389
|
-
build.courseData = { course: { dir: '/tmp', data: {} } }
|
|
390
|
-
|
|
347
|
+
const build = createTransformBuild()
|
|
391
348
|
const items = [{ _courseId: 'unmapped' }]
|
|
392
349
|
transformContentItemsSync(build, items)
|
|
393
350
|
assert.equal(items[0]._courseId, 'unmapped')
|
|
@@ -396,7 +353,7 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
396
353
|
|
|
397
354
|
describe('#writeContentJson() asset mapping', () => {
|
|
398
355
|
it('should map asset data to export format for export builds', () => {
|
|
399
|
-
const build =
|
|
356
|
+
const build = createBuild({ action: 'export' })
|
|
400
357
|
build.assetData = {
|
|
401
358
|
data: [
|
|
402
359
|
{ title: 'Logo', description: 'A logo', path: 'assets/logo.png', tags: ['branding'], _id: '123', url: '' },
|
|
@@ -418,7 +375,7 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
418
375
|
})
|
|
419
376
|
|
|
420
377
|
it('should not include assets for export when assetData is empty', () => {
|
|
421
|
-
const build =
|
|
378
|
+
const build = createBuild({ action: 'export' })
|
|
422
379
|
build.assetData = { data: [] }
|
|
423
380
|
build.courseData = {
|
|
424
381
|
course: { dir: '/tmp', fileName: 'course.json', data: {} }
|
|
@@ -431,7 +388,7 @@ describe('AdaptFrameworkBuild', () => {
|
|
|
431
388
|
})
|
|
432
389
|
|
|
433
390
|
it('should include asset data entry for non-empty export', () => {
|
|
434
|
-
const build =
|
|
391
|
+
const build = createBuild({ action: 'export' })
|
|
435
392
|
build.assetData = { data: [{ title: 'img', path: 'a.png' }] }
|
|
436
393
|
build.courseData = {
|
|
437
394
|
course: { dir: '/tmp', fileName: 'course.json', data: {} }
|