@@ -65,6 +65,42 @@ class ExternalRenderNodeTests: XCTestCase {
65
65
platforms: [ . init( name: " macOS " , introduced: nil , isBeta: false ) ]
66
66
)
67
67
)
68
+ externalResolver. entitiesToReturn [ " /path/to/external/navigatorTitleSwiftSymbol " ] = . success(
69
+ . init(
70
+ referencePath: " /path/to/external/navigatorTitleSwiftSymbol " ,
71
+ title: " NavigatorTitleSwiftSymbol (title) " ,
72
+ kind: . class,
73
+ language: . swift,
74
+ declarationFragments: . init( declarationFragments: [
75
+ . init( kind: . keyword, spelling: " class " , preciseIdentifier: nil ) ,
76
+ . init( kind: . text, spelling: " " , preciseIdentifier: nil ) ,
77
+ . init( kind: . identifier, spelling: " NavigatorTitleSwiftSymbol " , preciseIdentifier: nil )
78
+ ] ) ,
79
+ navigatorTitle: . init( declarationFragments: [
80
+ . init( kind: . identifier, spelling: " NavigatorTitleSwiftSymbol (navigator title) " , preciseIdentifier: nil )
81
+ ] ) ,
82
+ platforms: [ . init( name: " iOS " , introduced: nil , isBeta: true ) ]
83
+ )
84
+ )
85
+ externalResolver. entitiesToReturn [ " /path/to/external/navigatorTitleObjCSymbol " ] = . success(
86
+ . init(
87
+ referencePath: " /path/to/external/navigatorTitleObjCSymbol " ,
88
+ title: " NavigatorTitleObjCSymbol (title) " ,
89
+ kind: . function,
90
+ language: . objectiveC,
91
+ declarationFragments: . init( declarationFragments: [
92
+ . init( kind: . text, spelling: " - " , preciseIdentifier: nil ) ,
93
+ . init( kind: . text, spelling: " ( " , preciseIdentifier: nil ) ,
94
+ . init( kind: . typeIdentifier, spelling: " void " , preciseIdentifier: nil ) ,
95
+ . init( kind: . text, spelling: " ) " , preciseIdentifier: nil ) ,
96
+ . init( kind: . identifier, spelling: " ObjCSymbol " , preciseIdentifier: nil )
97
+ ] ) ,
98
+ navigatorTitle: . init( declarationFragments: [
99
+ . init( kind: . identifier, spelling: " NavigatorTitleObjCSymbol (navigator title) " , preciseIdentifier: nil )
100
+ ] ) ,
101
+ platforms: [ . init( name: " macOS " , introduced: nil , isBeta: false ) ]
102
+ )
103
+ )
68
104
return externalResolver
69
105
}
70
106
@@ -266,6 +302,80 @@ class ExternalRenderNodeTests: XCTestCase {
266
302
XCTAssertEqual ( objcSymbolExternalNode. type, " func " )
267
303
}
268
304
305
+ func testNavigatorWithExternalNodesWithNavigatorTitle( ) async throws {
306
+ let catalog = Folder ( name: " ModuleName.docc " , content: [
307
+ Folder ( name: " swift " , content: [
308
+ JSONFile ( name: " ModuleName.symbols.json " , content: makeSymbolGraph ( moduleName: " ModuleName " , symbols: [
309
+ makeSymbol ( id: " some-symbol-id " , language: . swift, kind: . class, pathComponents: [ " SomeClass " ] )
310
+ ] ) )
311
+ ] ) ,
312
+ Folder ( name: " clang " , content: [
313
+ JSONFile ( name: " ModuleName.symbols.json " , content: makeSymbolGraph ( moduleName: " ModuleName " , symbols: [
314
+ makeSymbol ( id: " some-symbol-id " , language: . objectiveC, kind: . class, pathComponents: [ " TLASomeClass " ] )
315
+ ] ) )
316
+ ] ) ,
317
+
318
+ InfoPlist ( identifier: " some.custom.identifier " ) ,
319
+
320
+ TextFile ( name: " ModuleName.md " , utf8Content: """
321
+ # ``ModuleName``
322
+
323
+ Curate a few external language-specific symbols and articles
324
+
325
+ ## Topics
326
+
327
+ ### External Reference
328
+
329
+ - <doc://com.test.external/path/to/external/navigatorTitleSwiftSymbol>
330
+ - <doc://com.test.external/path/to/external/navigatorTitleObjCSymbol>
331
+ """ ) ,
332
+ ] )
333
+
334
+ var configuration = DocumentationContext . Configuration ( )
335
+ let externalResolver = generateExternalResolver ( )
336
+ configuration. externalDocumentationConfiguration. sources [ externalResolver. bundleID] = externalResolver
337
+ let ( bundle, context) = try await loadBundle ( catalog: catalog, configuration: configuration)
338
+ XCTAssert ( context. problems. isEmpty, " Encountered unexpected problems: \( context. problems. map ( \. diagnostic. summary) ) " )
339
+
340
+ let renderContext = RenderContext ( documentationContext: context, bundle: bundle)
341
+ let converter = DocumentationContextConverter ( bundle: bundle, context: context, renderContext: renderContext)
342
+ let targetURL = try createTemporaryDirectory ( )
343
+ let builder = NavigatorIndex . Builder ( outputURL: targetURL, bundleIdentifier: bundle. id. rawValue, sortRootChildrenByName: true , groupByLanguage: true )
344
+ builder. setup ( )
345
+ for externalLink in context. externalCache {
346
+ let externalRenderNode = ExternalRenderNode ( externalEntity: externalLink. value, bundleIdentifier: bundle. id)
347
+ try builder. index ( renderNode: externalRenderNode)
348
+ }
349
+ for identifier in context. knownPages {
350
+ let entity = try context. entity ( with: identifier)
351
+ let renderNode = try XCTUnwrap ( converter. renderNode ( for: entity) )
352
+ try builder. index ( renderNode: renderNode)
353
+ }
354
+ builder. finalize ( )
355
+ let renderIndex = try RenderIndex . fromURL ( targetURL. appendingPathComponent ( " index.json " ) )
356
+
357
+ // Verify that there are no uncurated external links at the top level
358
+ XCTAssertEqual ( renderIndex. interfaceLanguages [ SourceLanguage . swift. id] ? . count ( where: \. isExternal) , 0 )
359
+ XCTAssertEqual ( renderIndex. interfaceLanguages [ SourceLanguage . objectiveC. id] ? . count ( where: \. isExternal) , 0 )
360
+
361
+ func externalNodes( by language: SourceLanguage ) -> [ RenderIndex . Node ] ? {
362
+ renderIndex. interfaceLanguages [ language. id] ? . first? . children? . filter ( \. isExternal)
363
+ }
364
+
365
+ // Verify that the curated external links are part of the index.
366
+ let swiftExternalNodes = try XCTUnwrap ( externalNodes ( by: . swift) )
367
+ let objcExternalNodes = try XCTUnwrap ( externalNodes ( by: . objectiveC) )
368
+
369
+ XCTAssertEqual ( swiftExternalNodes. count, 1 )
370
+ XCTAssertEqual ( objcExternalNodes. count, 1 )
371
+
372
+ let swiftSymbolExternalNode = try XCTUnwrap ( swiftExternalNodes. first)
373
+ let objcSymbolExternalNode = try XCTUnwrap ( objcExternalNodes. first)
374
+
375
+ XCTAssertEqual ( swiftSymbolExternalNode. title, " NavigatorTitleSwiftSymbol (title) " ) // Swift types prefer not using the navigator title where possible
376
+ XCTAssertEqual ( objcSymbolExternalNode. title, " NavigatorTitleObjCSymbol (navigator title) " ) // Objective C types prefer using the navigator title where possible
377
+ }
378
+
269
379
func testNavigatorWithExternalNodesOnlyAddsCuratedNodesToNavigator( ) async throws {
270
380
let catalog = Folder ( name: " ModuleName.docc " , content: [
271
381
Folder ( name: " swift " , content: [
0 commit comments