1
1
import * as content from 'astro:content' ;
2
2
import { describe , expect , test , vi , type TaskContext } from 'vitest' ;
3
3
import { getTutorial , type CollectionEntryTutorial } from './content' ;
4
+ import { logger } from './logger' ;
4
5
5
6
const getCollection = vi . mocked < ( ) => Omit < CollectionEntryTutorial , 'render' > [ ] > ( content . getCollection ) ;
6
7
vi . mock ( 'astro:content' , ( ) => ( { getCollection : vi . fn ( ) } ) ) ;
@@ -11,6 +12,8 @@ vi.mock(import('@tutorialkit/types'), async (importOriginal) => ({
11
12
DEFAULT_LOCALIZATION : { mocked : 'default localization' } as any ,
12
13
} ) ) ;
13
14
15
+ vi . mock ( import ( './logger' ) , async ( importOriginal ) => importOriginal ( ) ) ;
16
+
14
17
expect . addSnapshotSerializer ( {
15
18
serialize : ( val ) => JSON . stringify ( val , null , 2 ) ,
16
19
test : ( value ) => ! ( value instanceof Error ) ,
@@ -95,6 +98,34 @@ test('multiple parts', async (ctx) => {
95
98
await expect ( collection ) . toMatchFileSnapshot ( snapshotName ( ctx ) ) ;
96
99
} ) ;
97
100
101
+ test ( 'lessons with identical names in different chapters' , async ( ) => {
102
+ getCollection . mockReturnValueOnce ( [
103
+ { id : 'meta.md' , ...tutorial } ,
104
+ { id : '1-part/meta.md' , ...part } ,
105
+
106
+ { id : '1-part/1-chapter/meta.md' , ...chapter } ,
107
+ {
108
+ id : '1-part/1-chapter/identical-lesson-name/content.md' ,
109
+ ...lesson ,
110
+ data : { ...lesson . data , focus : '/first.js' } ,
111
+ } ,
112
+
113
+ { id : '1-part/2-chapter/meta.md' , ...chapter } ,
114
+ {
115
+ id : '1-part/2-chapter/identical-lesson-name/content.md' ,
116
+ ...lesson ,
117
+ data : { ...lesson . data , focus : '/second.js' } ,
118
+ } ,
119
+ ] ) ;
120
+
121
+ const collection = await getTutorial ( ) ;
122
+ const chapters = collection . parts [ '1-part' ] . chapters ;
123
+
124
+ // verify that lesson.id is not used to define what makes a lesson unique (part.id + chapter.id too)
125
+ expect ( chapters [ '1-chapter' ] . lessons [ 'identical-lesson-name' ] ) . toBeDefined ( ) ;
126
+ expect ( chapters [ '2-chapter' ] . lessons [ 'identical-lesson-name' ] ) . toBeDefined ( ) ;
127
+ } ) ;
128
+
98
129
describe ( 'metadata inheriting' , ( ) => {
99
130
test ( 'lesson inherits metadata from tutorial' , async ( ) => {
100
131
const data : CollectionEntryTutorial [ 'data' ] = {
@@ -212,6 +243,33 @@ describe('ordering', () => {
212
243
expect ( parts [ '2-part' ] . order ) . toBe ( 2 ) ;
213
244
} ) ;
214
245
246
+ test ( 'parts not mention in order are excluded ' , async ( ) => {
247
+ vi . spyOn ( logger , 'warn' ) . mockImplementationOnce ( vi . fn ( ) ) ;
248
+
249
+ getCollection . mockReturnValueOnce ( [
250
+ {
251
+ id : 'meta.md' ,
252
+ ...tutorial ,
253
+ data : { ...tutorial . data , parts : [ '2-part' , '1-part' ] } ,
254
+ } ,
255
+ { id : '2-part/meta.md' , ...part } ,
256
+ { id : 'excluded-part/meta.md' , ...part } ,
257
+ { id : '1-part/meta.md' , ...part } ,
258
+ ] ) ;
259
+
260
+ const collection = await getTutorial ( ) ;
261
+ const parts = collection . parts ;
262
+
263
+ expect ( Object . keys ( parts ) ) . toHaveLength ( 2 ) ;
264
+ expect ( parts [ 'excluded-part' ] ) . toBeUndefined ( ) ;
265
+ expect ( parts [ '1-part' ] ) . toBeDefined ( ) ;
266
+ expect ( parts [ '2-part' ] ) . toBeDefined ( ) ;
267
+
268
+ expect ( vi . mocked ( logger . warn ) . mock . calls [ 0 ] [ 0 ] ) . toMatchInlineSnapshot (
269
+ `"An order was specified for the parts of the tutorial but 'excluded-part' is not included so it won't be visible."` ,
270
+ ) ;
271
+ } ) ;
272
+
215
273
test ( 'chapters are ordered by default' , async ( ) => {
216
274
getCollection . mockReturnValueOnce ( [
217
275
{ id : 'meta.md' , ...tutorial } ,
@@ -246,6 +304,30 @@ describe('ordering', () => {
246
304
expect ( chapters [ '2-chapter' ] . order ) . toBe ( 2 ) ;
247
305
} ) ;
248
306
307
+ test ( 'chapters not mention in order are excluded ' , async ( ) => {
308
+ vi . spyOn ( logger , 'warn' ) . mockImplementationOnce ( vi . fn ( ) ) ;
309
+
310
+ getCollection . mockReturnValueOnce ( [
311
+ { id : 'meta.md' , ...tutorial } ,
312
+ { id : '1-part/meta.md' , ...part , data : { ...part . data , chapters : [ '2-chapter' , '1-chapter' ] } } ,
313
+ { id : '1-part/2-chapter/meta.md' , ...chapter } ,
314
+ { id : '1-part/excluded-chapter/meta.md' , ...chapter } ,
315
+ { id : '1-part/1-chapter/meta.md' , ...chapter } ,
316
+ ] ) ;
317
+
318
+ const collection = await getTutorial ( ) ;
319
+ const chapters = collection . parts [ '1-part' ] . chapters ;
320
+
321
+ expect ( Object . keys ( chapters ) ) . toHaveLength ( 2 ) ;
322
+ expect ( chapters [ 'excluded-part' ] ) . toBeUndefined ( ) ;
323
+ expect ( chapters [ '1-chapter' ] ) . toBeDefined ( ) ;
324
+ expect ( chapters [ '2-chapter' ] ) . toBeDefined ( ) ;
325
+
326
+ expect ( vi . mocked ( logger . warn ) . mock . calls [ 0 ] [ 0 ] ) . toMatchInlineSnapshot (
327
+ `"An order was specified for part '1-part' but chapter 'excluded-chapter' is not included, so it won't be visible."` ,
328
+ ) ;
329
+ } ) ;
330
+
249
331
test ( 'lessons are ordered by default' , async ( ) => {
250
332
getCollection . mockReturnValueOnce ( [
251
333
{ id : 'meta.md' , ...tutorial } ,
@@ -288,6 +370,34 @@ describe('ordering', () => {
288
370
expect ( lessons [ '1-lesson' ] . order ) . toBe ( 1 ) ;
289
371
expect ( lessons [ '2-lesson' ] . order ) . toBe ( 2 ) ;
290
372
} ) ;
373
+
374
+ test ( 'lessons not mention in order are excluded ' , async ( ) => {
375
+ vi . spyOn ( logger , 'warn' ) . mockImplementationOnce ( vi . fn ( ) ) ;
376
+
377
+ getCollection . mockReturnValueOnce ( [
378
+ { id : 'meta.md' , ...tutorial } ,
379
+ { id : '1-part/meta.md' , ...part } ,
380
+ {
381
+ id : '1-part/1-chapter/meta.md' ,
382
+ ...chapter ,
383
+ data : { ...chapter . data , lessons : [ '2-lesson' , '1-lesson' ] } ,
384
+ } ,
385
+ { id : '1-part/1-chapter/excluded-lesson/meta.md' , ...lesson } ,
386
+ { id : '1-part/1-chapter/1-lesson/meta.md' , ...lesson } ,
387
+ { id : '1-part/1-chapter/2-lesson/meta.md' , ...lesson } ,
388
+ ] ) ;
389
+
390
+ const collection = await getTutorial ( ) ;
391
+ const lessons = collection . parts [ '1-part' ] . chapters [ '1-chapter' ] . lessons ;
392
+
393
+ expect ( Object . keys ( lessons ) ) . toHaveLength ( 2 ) ;
394
+ expect ( lessons [ '1-lesson' ] ) . toBeDefined ( ) ;
395
+ expect ( lessons [ '2-lesson' ] ) . toBeDefined ( ) ;
396
+
397
+ expect ( vi . mocked ( logger . warn ) . mock . calls [ 0 ] [ 0 ] ) . toMatchInlineSnapshot (
398
+ `"An order was specified for chapter '1-chapter' but lesson 'excluded-lesson' is not included, so it won't be visible."` ,
399
+ ) ;
400
+ } ) ;
291
401
} ) ;
292
402
293
403
describe ( 'missing parts' , ( ) => {
0 commit comments