@@ -30,7 +30,7 @@ class DocumentationCuratorTests: XCTestCase {
30
30
func testCrawl( ) async throws {
31
31
let ( bundle, context) = try await testBundleAndContext ( named: " LegacyBundle_DoNotUseInNewTests " )
32
32
33
- var crawler = DocumentationCurator . init ( in: context, bundle: bundle)
33
+ var crawler = DocumentationCurator ( in: context, bundle: bundle)
34
34
let mykit = try context. entity ( with: ResolvedTopicReference ( bundleID: " org.swift.docc.example " , path: " /documentation/MyKit " , sourceLanguage: . swift) )
35
35
36
36
var symbolsWithCustomCuration = [ ResolvedTopicReference] ( )
@@ -303,7 +303,7 @@ class DocumentationCuratorTests: XCTestCase {
303
303
""" . write ( to: url. appendingPathComponent ( " Root.md " ) , atomically: true , encoding: . utf8)
304
304
}
305
305
306
- let crawler = DocumentationCurator . init ( in: context, bundle: bundle)
306
+ let crawler = DocumentationCurator ( in: context, bundle: bundle)
307
307
XCTAssert ( context. problems. isEmpty, " Expected no problems. Found: \( context. problems. map ( \. diagnostic. summary) ) " )
308
308
309
309
guard let moduleNode = context. documentationCache [ " SourceLocations " ] ,
@@ -316,11 +316,109 @@ class DocumentationCuratorTests: XCTestCase {
316
316
317
317
XCTAssertEqual ( root. path, " /documentation/Root " )
318
318
XCTAssertEqual ( crawler. problems. count, 0 )
319
-
319
+ }
320
+
321
+ func testCuratorDoesNotRelateNodesWhenArticleLinksContainExtraPathComponents( ) throws {
322
+ let ( bundle, context) = try loadBundle ( catalog:
323
+ Folder ( name: " CatalogName.docc " , content: [
324
+ TextFile ( name: " Root.md " , utf8Content: """
325
+ # Root
326
+
327
+ @Metadata {
328
+ @TechnologyRoot
329
+ }
330
+
331
+ Add an API Collection of indirection to more easily detect the failed curation.
332
+
333
+ ## Topics
334
+ - <doc:API-Collection>
335
+ """ ) ,
336
+
337
+ TextFile ( name: " API-Collection.md " , utf8Content: """
338
+ # Some API Collection
339
+
340
+ Fail to curate all 4 articles because of extra incorrect path components.
341
+
342
+ ## Topics
343
+
344
+ ### No links will resolve in this section
345
+
346
+ - <doc:WrongModuleName/First>
347
+ - <doc:documentation/WrongModuleName/Second>
348
+ - <doc:documentation/CatalogName/ExtraPathComponent/Third>
349
+ - <doc:CatalogName/ExtraPathComponent/Forth>
350
+ """ ) ,
351
+
352
+ TextFile ( name: " First.md " , utf8Content: " # First " ) ,
353
+ TextFile ( name: " Second.md " , utf8Content: " # Second " ) ,
354
+ TextFile ( name: " Third.md " , utf8Content: " # Third " ) ,
355
+ TextFile ( name: " Forth.md " , utf8Content: " # Forth " ) ,
356
+ ] )
357
+ )
358
+ let ( linkResolutionProblems, otherProblems) = context. problems. categorize ( where: { $0. diagnostic. identifier == " org.swift.docc.unresolvedTopicReference " } )
359
+ XCTAssert ( otherProblems. isEmpty, " Unexpected problems: \( otherProblems. map ( \. diagnostic. summary) . sorted ( ) ) " )
360
+
361
+ XCTAssertEqual (
362
+ linkResolutionProblems. map ( \. diagnostic. source? . lastPathComponent) ,
363
+ [ " API-Collection.md " , " API-Collection.md " , " API-Collection.md " , " API-Collection.md " ] ,
364
+ " Every unresolved link is in the API collection "
365
+ )
366
+ XCTAssertEqual (
367
+ linkResolutionProblems. map ( { $0. diagnostic. range? . lowerBound. line } ) , [ 9 , 10 , 11 , 12 ] ,
368
+ " There should be one warning about an unresolved reference for each link in the API collection's top "
369
+ )
370
+
371
+ let rootReference = try XCTUnwrap ( context. soleRootModuleReference)
372
+
373
+ for articleName in [ " First " , " Second " , " Third " , " Forth " ] {
374
+ let reference = try XCTUnwrap ( context. documentationCache. allReferences. first ( where: { $0. lastPathComponent == articleName } ) )
375
+ XCTAssertEqual (
376
+ context. topicGraph. nodeWithReference ( reference) ? . shouldAutoCurateInCanonicalLocation, true ,
377
+ " Article ' \( articleName) ' isn't (successfully) manually curated and should therefore automatically curate. "
378
+ )
379
+ XCTAssertEqual (
380
+ context. topicGraph. reverseEdges [ reference] ? . map ( \. path) , [ rootReference. path] ,
381
+ " Article ' \( articleName) ' should only have a reverse edge to the root page where it will be automatically curated. "
382
+ )
383
+ }
384
+
385
+ let apiCollectionReference = try XCTUnwrap ( context. documentationCache. allReferences. first ( where: { $0. lastPathComponent == " API-Collection " } ) )
386
+ let apiCollectionSemantic = try XCTUnwrap ( try context. entity ( with: apiCollectionReference) . semantic as? Article )
387
+ XCTAssertEqual ( apiCollectionSemantic. topics? . taskGroups. count, 1 , " The API Collection has one topic section " )
388
+ let topicSection = try XCTUnwrap ( apiCollectionSemantic. topics? . taskGroups. first)
389
+ XCTAssertEqual ( topicSection. links. map ( \. destination) , [
390
+ // All these links are the same as they were authored which means that they didn't resolve.
391
+ " doc:WrongModuleName/First " ,
392
+ " doc:documentation/WrongModuleName/Second " ,
393
+ " doc:documentation/CatalogName/ExtraPathComponent/Third " ,
394
+ " doc:CatalogName/ExtraPathComponent/Forth " ,
395
+ ] )
396
+
397
+ let rootPage = try context. entity ( with: rootReference)
398
+ let renderer = DocumentationNodeConverter ( bundle: bundle, context: context)
399
+ let renderNode = renderer. convert ( rootPage)
400
+
401
+ XCTAssertEqual ( renderNode. topicSections. map ( \. title) , [
402
+ nil , // An unnamed topic section
403
+ " Articles " , // The automatic topic section
404
+ ] )
405
+ XCTAssertEqual ( renderNode. topicSections. map { $0. identifiers. sorted ( ) } , [
406
+ // The unnamed topic section curates the API collection
407
+ [
408
+ " doc://CatalogName/documentation/CatalogName/API-Collection "
409
+ ] ,
410
+ // The automatic "Articles" section curates all 4 articles
411
+ [
412
+ " doc://CatalogName/documentation/CatalogName/First " ,
413
+ " doc://CatalogName/documentation/CatalogName/Forth " ,
414
+ " doc://CatalogName/documentation/CatalogName/Second " ,
415
+ " doc://CatalogName/documentation/CatalogName/Third " ,
416
+ ] ,
417
+ ] )
320
418
}
321
419
322
420
func testModuleUnderAncestorOfTechnologyRoot( ) async throws {
323
- let ( _, bundle , context) = try await testBundleAndContext ( copying: " SourceLocations " ) { url in
421
+ let ( _, _ , context) = try await testBundleAndContext ( copying: " SourceLocations " ) { url in
324
422
try """
325
423
# Root with ancestor curating a module
326
424
@@ -347,7 +445,6 @@ class DocumentationCuratorTests: XCTestCase {
347
445
""" . write ( to: url. appendingPathComponent ( " Ancestor.md " ) , atomically: true , encoding: . utf8)
348
446
}
349
447
350
- let _ = DocumentationCurator . init ( in: context, bundle: bundle)
351
448
XCTAssert ( context. problems. isEmpty, " Expected no problems. Found: \( context. problems. map ( \. diagnostic. summary) ) " )
352
449
353
450
guard let moduleNode = context. documentationCache [ " SourceLocations " ] ,
@@ -364,7 +461,7 @@ class DocumentationCuratorTests: XCTestCase {
364
461
func testSymbolLinkResolving( ) async throws {
365
462
let ( bundle, context) = try await testBundleAndContext ( named: " LegacyBundle_DoNotUseInNewTests " )
366
463
367
- let crawler = DocumentationCurator . init ( in: context, bundle: bundle)
464
+ let crawler = DocumentationCurator ( in: context, bundle: bundle)
368
465
369
466
// Resolve top-level symbol in module parent
370
467
do {
@@ -417,7 +514,7 @@ class DocumentationCuratorTests: XCTestCase {
417
514
func testLinkResolving( ) async throws {
418
515
let ( sourceRoot, bundle, context) = try await testBundleAndContext ( named: " LegacyBundle_DoNotUseInNewTests " )
419
516
420
- var crawler = DocumentationCurator . init ( in: context, bundle: bundle)
517
+ var crawler = DocumentationCurator ( in: context, bundle: bundle)
421
518
422
519
// Resolve and curate an article in module root (absolute link)
423
520
do {
@@ -510,7 +607,7 @@ class DocumentationCuratorTests: XCTestCase {
510
607
""" . write ( to: root. appendingPathComponent ( " documentation " ) . appendingPathComponent ( " api-collection.md " ) , atomically: true , encoding: . utf8)
511
608
}
512
609
513
- var crawler = DocumentationCurator . init ( in: context, bundle: bundle)
610
+ var crawler = DocumentationCurator ( in: context, bundle: bundle)
514
611
let reference = ResolvedTopicReference ( bundleID: " org.swift.docc.example " , path: " /documentation/SideKit " , sourceLanguage: . swift)
515
612
516
613
try crawler. crawlChildren ( of: reference, prepareForCuration: { _ in } ) { ( _, _) in }
0 commit comments