diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart index 223ed301da..409f87617f 100644 --- a/lib/src/dartdoc_options.dart +++ b/lib/src/dartdoc_options.dart @@ -1422,6 +1422,9 @@ class DartdocOptionContext extends DartdocOptionContextBase excludePackages.any((pattern) => name == pattern); String get templatesDir => optionSet['templatesDir'].valueAt(context); + + // TODO(jdkoren): temporary while we confirm href base behavior doesn't break important clients + bool get useBaseHref => optionSet['useBaseHref'].valueAt(context); } /// Instantiate dartdoc's configuration file and options parser with the @@ -1635,6 +1638,14 @@ Future> createDartdocOptions() async { 'property, top_level_constant, top_level_property, typedef. Partial templates are ' 'supported; they must begin with an underscore, and references to them must omit the ' 'leading underscore (e.g. use {{>foo}} to reference the partial template _foo.html).'), + DartdocOptionArgOnly('useBaseHref', false, + help: + 'Use in generated files (legacy behavior). This option ' + 'is temporary and support will be removed in the future. Use only ' + 'if the default behavior breaks links between your documentation ' + 'pages, and please file an issue on Github.', + negatable: false, + hide: true), // TODO(jcollins-g): refactor so there is a single static "create" for // each DartdocOptionContext that traverses the inheritance tree itself. ] diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index ca9b0c6f0a..8cac333a0a 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -142,13 +142,17 @@ class HtmlGeneratorOptions implements HtmlOptions { @override final String toolVersion; + @override + final bool useBaseHref; + HtmlGeneratorOptions( {this.url, this.relCanonicalPrefix, this.faviconPath, String toolVersion, this.prettyIndexJson = false, - this.templatesDir}) + this.templatesDir, + this.useBaseHref = false}) : this.toolVersion = toolVersion ?? 'unknown'; } @@ -166,7 +170,8 @@ Future> initGenerators(GeneratorContext config) async { toolVersion: dartdocVersion, faviconPath: config.favicon, prettyIndexJson: config.prettyIndexJson, - templatesDir: config.templatesDir); + templatesDir: config.templatesDir, + useBaseHref: config.useBaseHref); return [ await HtmlGenerator.create( diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index 0ac5d7fda5..b82bbc4782 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -76,6 +76,9 @@ class HtmlGeneratorInstance { }); String json = encoder.convert(indexItems); + if (!_options.useBaseHref) { + json = json.replaceAll(HTMLBASE_PLACEHOLDER, ''); + } _writer(path.join('categories.json'), '${json}\n'); } @@ -118,6 +121,9 @@ class HtmlGeneratorInstance { }); String json = encoder.convert(indexItems); + if (!_options.useBaseHref) { + json = json.replaceAll(HTMLBASE_PLACEHOLDER, ''); + } _writer(path.join('index.json'), '${json}\n'); } @@ -412,6 +418,10 @@ class HtmlGeneratorInstance { // Replaces '/' separators with proper separators for the platform. String outFile = path.joinAll(filename.split('/')); String content = template.renderString(data); + + if (!_options.useBaseHref) { + content = content.replaceAll(HTMLBASE_PLACEHOLDER, data.htmlBase); + } _writer(outFile, content, element: data.self is Warnable ? data.self : null); if (data.self is Indexable) _indexedElements.add(data.self as Indexable); diff --git a/lib/src/html/template_data.dart b/lib/src/html/template_data.dart index 2127c290dd..0bcef5e61e 100644 --- a/lib/src/html/template_data.dart +++ b/lib/src/html/template_data.dart @@ -8,6 +8,7 @@ import 'package:dartdoc/src/model/model.dart'; abstract class HtmlOptions { String get relCanonicalPrefix; String get toolVersion; + bool get useBaseHref; } abstract class TemplateData { @@ -38,6 +39,7 @@ abstract class TemplateData { T get self; String get version => htmlOptions.toolVersion; String get relCanonicalPrefix => htmlOptions.relCanonicalPrefix; + bool get useBaseHref => htmlOptions.useBaseHref; String _layoutTitle(String name, String kind, bool isDeprecated) => _renderHelper.composeLayoutTitle(name, kind, isDeprecated); @@ -67,9 +69,9 @@ class PackageTemplateData extends TemplateData { bool get hasHomepage => package.hasHomepage; String get homepage => package.homepage; - /// `null` for packages because they are at the root – not needed + /// empty for packages because they are at the root – not needed @override - String get htmlBase => null; + String get htmlBase => ''; } class CategoryTemplateData extends TemplateData { @@ -83,7 +85,7 @@ class CategoryTemplateData extends TemplateData { String get title => '${category.name} ${category.kind} - Dart API'; @override - String get htmlBase => '..'; + String get htmlBase => '../'; @override String get layoutTitle => _layoutTitle(category.name, category.kind, false); @@ -109,7 +111,7 @@ class LibraryTemplateData extends TemplateData { @override String get title => '${library.name} library - Dart API'; @override - String get htmlBase => '..'; + String get htmlBase => '../'; @override String get metaDescription => '${library.name} library API docs, for the Dart programming language.'; @@ -164,7 +166,7 @@ class ClassTemplateData extends TemplateData { @override List get navLinks => [packageGraph.defaultPackage, library]; @override - String get htmlBase => '..'; + String get htmlBase => '../'; Class get objectType { if (_objectType != null) { @@ -207,7 +209,7 @@ class ExtensionTemplateData extends TemplateData { @override List get navLinks => [packageGraph.defaultPackage, library]; @override - String get htmlBase => '..'; + String get htmlBase => '../'; } class ConstructorTemplateData extends TemplateData { @@ -235,7 +237,7 @@ class ConstructorTemplateData extends TemplateData { List get navLinksWithGenerics => [clazz]; @override @override - String get htmlBase => '../..'; + String get htmlBase => '../../'; @override String get title => '${constructor.name} constructor - ${clazz.name} class - ' '${library.name} library - Dart API'; @@ -279,7 +281,7 @@ class FunctionTemplateData extends TemplateData { @override List get navLinks => [packageGraph.defaultPackage, library]; @override - String get htmlBase => '..'; + String get htmlBase => '../'; } class MethodTemplateData extends TemplateData { @@ -317,7 +319,7 @@ class MethodTemplateData extends TemplateData { @override List get navLinksWithGenerics => [container]; @override - String get htmlBase => '../..'; + String get htmlBase => '../../'; } class PropertyTemplateData extends TemplateData { @@ -356,7 +358,7 @@ class PropertyTemplateData extends TemplateData { @override List get navLinksWithGenerics => [container]; @override - String get htmlBase => '../..'; + String get htmlBase => '../../'; String get type => 'property'; } @@ -400,7 +402,7 @@ class TypedefTemplateData extends TemplateData { @override List get navLinks => [packageGraph.defaultPackage, library]; @override - String get htmlBase => '..'; + String get htmlBase => '../'; } class TopLevelPropertyTemplateData extends TemplateData { @@ -431,7 +433,7 @@ class TopLevelPropertyTemplateData extends TemplateData { @override List get navLinks => [packageGraph.defaultPackage, library]; @override - String get htmlBase => '..'; + String get htmlBase => '../'; String get _type => 'property'; } diff --git a/lib/src/model/package.dart b/lib/src/model/package.dart index 7771d3ea12..b712e92450 100644 --- a/lib/src/model/package.dart +++ b/lib/src/model/package.dart @@ -14,6 +14,17 @@ import 'package:pub_semver/pub_semver.dart'; final RegExp substituteNameVersion = RegExp(r'%([bnv])%'); +// All hrefs are emitted as relative paths from the output root. We are unable +// to compute them from the page we are generating, and many properties computed +// using hrefs are memoized anyway. To build complete relative hrefs, we emit +// the href with this placeholder, and then replace it with the current page's +// base href afterwards. +// See https://github.com/dart-lang/dartdoc/issues/2090 for further context. +// TODO: Find an approach that doesn't require doing this. +// Unlikely to be mistaken for an identifier, html tag, or something else that +// might reasonably exist normally. +final String HTMLBASE_PLACEHOLDER = '\%\%__HTMLBASE_dartdoc_internal__\%\%'; + /// A [LibraryContainer] that contains [Library] objects related to a particular /// package. class Package extends LibraryContainer @@ -206,7 +217,7 @@ class Package extends LibraryContainer }); if (!_baseHref.endsWith('/')) _baseHref = '${_baseHref}/'; } else { - _baseHref = ''; + _baseHref = config.useBaseHref ? '' : HTMLBASE_PLACEHOLDER; } } return _baseHref; diff --git a/lib/templates/_footer.html b/lib/templates/_footer.html index c20bd74241..bac8322bab 100644 --- a/lib/templates/_footer.html +++ b/lib/templates/_footer.html @@ -11,11 +11,12 @@ +{{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} - - - - + + + + diff --git a/lib/templates/_head.html b/lib/templates/_head.html index 5fffa11b60..15580a9e62 100644 --- a/lib/templates/_head.html +++ b/lib/templates/_head.html @@ -12,16 +12,20 @@ {{ #relCanonicalPrefix }} {{ /relCanonicalPrefix}} + + {{#useBaseHref}}{{! TODO(jdkoren): remove when the useBaseHref option is removed.}} {{#htmlBase}} {{/htmlBase}} + {{/useBaseHref}} + {{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} - - - + + + diff --git a/test/model_special_cases_test.dart b/test/model_special_cases_test.dart index 8abf5af3ed..7866edc784 100644 --- a/test/model_special_cases_test.dart +++ b/test/model_special_cases_test.dart @@ -356,7 +356,8 @@ void main() { hashCode.inheritance.any((c) => c.name == 'Interceptor'), isTrue); // If EventTarget really does start implementing hashCode, this will // fail. - expect(hashCode.href, equals('dart-core/Object/hashCode.html')); + expect(hashCode.href, + equals('${HTMLBASE_PLACEHOLDER}dart-core/Object/hashCode.html')); expect( hashCode.canonicalEnclosingContainer, equals(objectModelElement)); expect( @@ -387,7 +388,8 @@ void main() { test('sdk library have formatted names', () { expect(dartAsyncLib.name, 'dart:async'); expect(dartAsyncLib.dirName, 'dart-async'); - expect(dartAsyncLib.href, 'dart-async/dart-async-library.html'); + expect(dartAsyncLib.href, + '${HTMLBASE_PLACEHOLDER}dart-async/dart-async-library.html'); }); }); diff --git a/test/model_test.dart b/test/model_test.dart index edae9ae3bc..c044f0366d 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -251,18 +251,24 @@ void main() { test('can invoke a tool and add a reference link', () { expect(invokeTool.documentation, contains('Yes it is a [Dog]! Is not a [ToolUser].')); - expect(invokeTool.documentationAsHtml, - contains('ToolUser')); - expect(invokeTool.documentationAsHtml, - contains('Dog')); + expect( + invokeTool.documentationAsHtml, + contains( + 'ToolUser')); + expect( + invokeTool.documentationAsHtml, + contains( + 'Dog')); }); test(r'can invoke a tool with no $INPUT or args', () { expect(invokeToolNoInput.documentation, contains('Args: []')); expect(invokeToolNoInput.documentation, isNot(contains('This text should not appear in the output'))); expect(invokeToolNoInput.documentation, isNot(contains('[Dog]'))); - expect(invokeToolNoInput.documentationAsHtml, - isNot(contains('Dog'))); + expect( + invokeToolNoInput.documentationAsHtml, + isNot(contains( + 'Dog'))); }); test('can invoke a tool multiple times in one comment block', () { RegExp envLine = RegExp(r'^Env: \{', multiLine: true); @@ -372,14 +378,14 @@ void main() { expect( renderer.renderCategoryLabel(category), '' - 'Superb'); + 'Superb'); }); test('CategoryRendererHtml renders linkedName', () { Category category = packageGraph.publicPackages.first.categories.first; CategoryRendererHtml renderer = CategoryRendererHtml(); expect(renderer.renderLinkedName(category), - 'Superb'); + 'Superb'); }); }); @@ -963,14 +969,14 @@ void main() { test('Verify links inside of table headers', () { expect( docsAsHtml.contains( - 'Annotation'), + 'Annotation'), isTrue); }); test('Verify links inside of table body', () { expect( docsAsHtml.contains( - 'foo'), + 'foo'), isTrue); }); @@ -995,49 +1001,49 @@ void main() { expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to library: renamedLib')); + 'Link to library: renamedLib')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to constructor (implied): new renamedLib.YetAnotherHelper()')); + 'Link to constructor (implied): new renamedLib.YetAnotherHelper()')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to constructor (implied, no new): renamedLib.YetAnotherHelper()')); + 'Link to constructor (implied, no new): renamedLib.YetAnotherHelper()')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to class: renamedLib.YetAnotherHelper')); + 'Link to class: renamedLib.YetAnotherHelper')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to constructor (direct): renamedLib.YetAnotherHelper.YetAnotherHelper')); + 'Link to constructor (direct): renamedLib.YetAnotherHelper.YetAnotherHelper')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to class member: renamedLib.YetAnotherHelper.getMoreContents')); + 'Link to class member: renamedLib.YetAnotherHelper.getMoreContents')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to function: renamedLib.helperFunction')); + 'Link to function: renamedLib.helperFunction')); expect( aFunctionUsingRenamedLib.documentationAsHtml, contains( - 'Link to overlapping prefix: renamedLib2.theOnlyThingInTheLibrary')); + 'Link to overlapping prefix: renamedLib2.theOnlyThingInTheLibrary')); }); test('operator [] reference within a class works', () { expect( docsAsHtml, contains( - 'operator [] ')); + 'operator [] ')); }); test('operator [] reference outside of a class works', () { expect( docsAsHtml, contains( - 'SpecialList.operator [] ')); + 'SpecialList.operator [] ')); }, skip: 'https://github.com/dart-lang/dartdoc/issues/1285'); test('codeifies a class from the SDK', () { @@ -1052,7 +1058,7 @@ void main() { expect( docsAsHtml, contains( - 'BaseForDocComments')); + 'BaseForDocComments')); }); test( @@ -1061,7 +1067,7 @@ void main() { expect( docsAsHtml, contains( - 'doesStuff')); + 'doesStuff')); }); test( @@ -1069,10 +1075,14 @@ void main() { () { final Class helperClass = exLibrary.classes.firstWhere((c) => c.name == 'Helper'); - expect(helperClass.documentationAsHtml, - contains('Apple')); - expect(helperClass.documentationAsHtml, - contains('ex.B')); + expect( + helperClass.documentationAsHtml, + contains( + 'Apple')); + expect( + helperClass.documentationAsHtml, + contains( + 'ex.B')); }); test('link to override method in implementer from base class', () { @@ -1081,7 +1091,7 @@ void main() { expect( helperClass.documentationAsHtml, contains( - 'BoxConstraints.debugAssertIsValid')); + 'BoxConstraints.debugAssertIsValid')); }); test( @@ -1090,7 +1100,7 @@ void main() { expect( docsAsHtml, contains( - 'BaseClass')); + 'BaseClass')); }); test( @@ -1099,30 +1109,35 @@ void main() { expect( docsAsHtml, contains( - 'NAME_WITH_TWO_UNDERSCORES')); + 'NAME_WITH_TWO_UNDERSCORES')); }); test('links to a method in this class', () { expect( docsAsHtml, contains( - 'anotherMethod')); + 'anotherMethod')); }); test('links to a top-level function in this library', () { expect( docsAsHtml, contains( - 'topLevelFunction')); + 'topLevelFunction')); }); test('links to top-level function from an imported library', () { expect( - docsAsHtml, contains('function1')); + docsAsHtml, + contains( + 'function1')); }); test('links to a class from an imported lib', () { - expect(docsAsHtml, contains('Apple')); + expect( + docsAsHtml, + contains( + 'Apple')); }); test( @@ -1131,14 +1146,14 @@ void main() { expect( docsAsHtml, contains( - 'incorrectDocReference')); + 'incorrectDocReference')); }); test('links to a top-level const from an imported lib', () { expect( docsAsHtml, contains( - 'incorrectDocReferenceFromEx')); + 'incorrectDocReferenceFromEx')); }); test('links to a top-level variable with a prefix from an imported lib', @@ -1146,27 +1161,42 @@ void main() { expect( docsAsHtml, contains( - 'css.theOnlyThingInTheLibrary')); + 'css.theOnlyThingInTheLibrary')); }); test('links to a name with a single underscore', () { expect( docsAsHtml, contains( - 'NAME_SINGLEUNDERSCORE')); + 'NAME_SINGLEUNDERSCORE')); }); test('correctly escapes type parameters in links', () { expect( docsAsHtml, contains( - 'ExtraSpecialList<Object>')); + 'ExtraSpecialList<Object>')); }); test('correctly escapes type parameters in broken links', () { expect(docsAsHtml, contains('ThisIsNotHereNoWay<MyType>')); }); + + test('leaves relative href resulting in a broken link', () { + // Dartdoc does emit a brokenLink warning for this. + expect(docsAsHtml, + contains('link')); + }); + + test('leaves relative href resulting in a working link', () { + // Ideally doc comments should not make assumptions about Dartdoc output + // files, but unfortunately some do... + expect( + docsAsHtml, + contains( + 'link')); + }); }); test('multi-underscore names in brackets do not become italicized', () { @@ -1174,7 +1204,7 @@ void main() { expect( short.documentationAsHtml, contains( - 'NAME_WITH_TWO_UNDERSCORES')); + 'NAME_WITH_TWO_UNDERSCORES')); }); test('still has brackets inside code blocks', () { @@ -1227,7 +1257,7 @@ void main() { test('single ref to class', () { expect( B.documentationAsHtml.contains( - '

Extends class Apple, use new Apple or new Apple.fromString

'), + '

Extends class Apple, use new Apple or new Apple.fromString

'), isTrue); }); @@ -1243,27 +1273,38 @@ void main() { expect(extendedClass, isNotNull); String resolved = extendedClass.documentationAsHtml; expect(resolved, isNotNull); - expect(resolved, - contains('BaseClass')); - expect(resolved, - contains('Linking over to Apple')); + expect( + resolved, + contains( + 'BaseClass')); + expect( + resolved, + contains( + 'Linking over to Apple')); }); test('references to class and constructors', () { String comment = B.documentationAsHtml; - expect(comment, - contains('Extends class Apple')); expect( - comment, contains('use new Apple')); + comment, + contains( + 'Extends class Apple')); expect( comment, contains( - 'new Apple.fromString')); + 'use new Apple')); + expect( + comment, + contains( + 'new Apple.fromString')); }); test('reference to class from another library', () { String comment = superAwesomeClass.documentationAsHtml; - expect(comment, contains('Apple')); + expect( + comment, + contains( + 'Apple')); }); test('reference to method', () { @@ -1271,7 +1312,7 @@ void main() { expect( comment, equals( - '

link to method from class Apple.m

')); + '

link to method from class Apple.m

')); }); test( @@ -1284,11 +1325,11 @@ void main() { expect( notAMethodFromPrivateClass.documentationAsHtml, contains( - 'fake.InheritingClassOne.aMethod')); + 'fake.InheritingClassOne.aMethod')); expect( notAMethodFromPrivateClass.documentationAsHtml, contains( - 'fake.InheritingClassTwo.aMethod')); + 'fake.InheritingClassTwo.aMethod')); }); test('legacy code blocks render correctly', () { @@ -1451,35 +1492,35 @@ void main() { expect( overrideByModifierClass.oneLineDoc, contains( - 'ModifierClass')); + 'ModifierClass')); expect( overrideByModifierClass.canonicalModelElement.documentationAsHtml, contains( - 'ModifierClass')); + 'ModifierClass')); expect( overrideByGenericMixin.oneLineDoc, contains( - 'GenericMixin')); + 'GenericMixin')); expect( overrideByGenericMixin.canonicalModelElement.documentationAsHtml, contains( - 'GenericMixin')); + 'GenericMixin')); expect( overrideByBoth.oneLineDoc, contains( - 'ModifierClass')); + 'ModifierClass')); expect( overrideByBoth.oneLineDoc, contains( - 'GenericMixin')); + 'GenericMixin')); expect( overrideByBoth.canonicalModelElement.documentationAsHtml, contains( - 'ModifierClass')); + 'ModifierClass')); expect( overrideByBoth.canonicalModelElement.documentationAsHtml, contains( - 'GenericMixin')); + 'GenericMixin')); }); }); @@ -1629,11 +1670,12 @@ void main() { }); test('exported class should have hrefs from the current library', () { - expect(Dep.href, equals('ex/Deprecated-class.html')); expect( - Dep.instanceMethods[0].href, equals('ex/Deprecated/toString.html')); - expect( - Dep.instanceProperties[0].href, equals('ex/Deprecated/expires.html')); + Dep.href, equals('${HTMLBASE_PLACEHOLDER}ex/Deprecated-class.html')); + expect(Dep.instanceMethods[0].href, + equals('${HTMLBASE_PLACEHOLDER}ex/Deprecated/toString.html')); + expect(Dep.instanceProperties[0].href, + equals('${HTMLBASE_PLACEHOLDER}ex/Deprecated/expires.html')); }); test('exported class should have linkedReturnType for the current library', @@ -1641,8 +1683,10 @@ void main() { Method returnCool = Cool.instanceMethods .firstWhere((m) => m.name == 'returnCool', orElse: () => null); expect(returnCool, isNotNull); - expect(returnCool.linkedReturnType, - equals('Cool')); + expect( + returnCool.linkedReturnType, + equals( + 'Cool')); }); test('F has a single instance method', () { @@ -1885,14 +1929,18 @@ void main() { () { expect(extensionReferencer.documentationAsHtml, contains('_Shhh')); - expect(extensionReferencer.documentationAsHtml, - contains('FancyList')); - expect(extensionReferencer.documentationAsHtml, - contains('AnExtension.call')); expect( extensionReferencer.documentationAsHtml, contains( - 'DocumentThisExtensionOnce')); + 'FancyList')); + expect( + extensionReferencer.documentationAsHtml, + contains( + 'AnExtension.call')); + expect( + extensionReferencer.documentationAsHtml, + contains( + 'DocumentThisExtensionOnce')); }); test('has a fully qualified name', () { @@ -1909,7 +1957,7 @@ void main() { test('member method has href', () { s = ext.instanceMethods.firstWhere((m) => m.name == 's'); - expect(s.href, 'ex/AppleExtension/s.html'); + expect(s.href, '${HTMLBASE_PLACEHOLDER}ex/AppleExtension/s.html'); }); test('has extended type', () { @@ -2163,7 +2211,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( params, '' - 'VoidCallback ' + 'VoidCallback ' 'callback'); function = @@ -2172,7 +2220,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( params, '' - 'Callback2 ' + 'Callback2 ' 'callback'); }); @@ -2205,21 +2253,21 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, // TODO(jcollins-g): really, these shouldn't be called "parameters" in // the span class. expect(explicitSetter.linkedReturnType, - 'dynamic Function(int, Cool, List<int>)'); + 'dynamic Function(int, Cool, List<int>)'); }); test('parameterized type from field is correctly displayed', () { Field aField = TemplatedInterface.instanceProperties .singleWhere((f) => f.name == 'aField'); expect(aField.linkedReturnType, - 'AnotherParameterizedClass<Stream<List<int>>>'); + 'AnotherParameterizedClass<Stream<List<int>>>'); }); test('parameterized type from inherited field is correctly displayed', () { Field aInheritedField = TemplatedInterface.inheritedProperties .singleWhere((f) => f.name == 'aInheritedField'); expect(aInheritedField.linkedReturnType, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); }); test( @@ -2229,7 +2277,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .singleWhere((f) => f.name == 'aGetter') .getter; expect(aGetter.linkedReturnType, - 'AnotherParameterizedClass<Map<A, List<String>>>'); + 'AnotherParameterizedClass<Map<A, List<String>>>'); }); test( @@ -2239,7 +2287,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .singleWhere((f) => f.name == 'aInheritedGetter') .getter; expect(aInheritedGetter.linkedReturnType, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); }); test( @@ -2249,11 +2297,11 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .singleWhere((f) => f.name == 'aInheritedSetter') .setter; expect(aInheritedSetter.allParameters.first.modelType.linkedName, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); // TODO(jcollins-g): really, these shouldn't be called "parameters" in // the span class. expect(aInheritedSetter.enclosingCombo.linkedReturnType, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); }); test( @@ -2262,7 +2310,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, Method aMethodInterface = TemplatedInterface.allInstanceMethods .singleWhere((m) => m.name == 'aMethodInterface'); expect(aMethodInterface.linkedReturnType, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); }); test( @@ -2271,7 +2319,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, Method aInheritedMethod = TemplatedInterface.allInstanceMethods .singleWhere((m) => m.name == 'aInheritedMethod'); expect(aInheritedMethod.linkedReturnType, - 'AnotherParameterizedClass<List<int>>'); + 'AnotherParameterizedClass<List<int>>'); }); test( @@ -2281,7 +2329,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .allInstanceMethods .singleWhere((m) => m.name == 'aTypedefReturningMethodInterface'); expect(aTypedefReturningMethodInterface.linkedReturnType, - 'ParameterizedTypedef<List<String>>'); + 'ParameterizedTypedef<List<String>>'); }); test( @@ -2291,7 +2339,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .allInstanceMethods .singleWhere((m) => m.name == 'aInheritedTypedefReturningMethod'); expect(aInheritedTypedefReturningMethod.linkedReturnType, - 'ParameterizedTypedef<List<int>>'); + 'ParameterizedTypedef<List<int>>'); }); test('parameterized types for inherited operator is correctly displayed', @@ -2300,11 +2348,11 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .inheritedOperators .singleWhere((m) => m.name == 'operator +'); expect(aInheritedAdditionOperator.linkedReturnType, - 'ParameterizedClass<List<int>>'); + 'ParameterizedClass<List<int>>'); expect( ParameterRendererHtml() .renderLinkedParams(aInheritedAdditionOperator.parameters), - 'ParameterizedClass<List<int>> other'); + 'ParameterizedClass<List<int>> other'); }); test('', () {}); @@ -2409,14 +2457,17 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test( 'an inherited method from the core SDK has a href relative to the package class', () { - expect(inheritedClear.href, equals('ex/CatString/clear.html')); + expect(inheritedClear.href, + equals('${HTMLBASE_PLACEHOLDER}ex/CatString/clear.html')); }); test( 'an inherited method has linked to enclosed class name when superclass not in package', () { - expect(inheritedClear.linkedName, - equals('clear')); + expect( + inheritedClear.linkedName, + equals( + 'clear')); }); test('has enclosing element', () { @@ -2519,7 +2570,10 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('if inherited, has a href relative to enclosed class', () { - expect(plus.href, equals('ex/SpecializedDuration/operator_plus.html')); + expect( + plus.href, + equals( + '${HTMLBASE_PLACEHOLDER}ex/SpecializedDuration/operator_plus.html')); }); test('if inherited and superclass not in package, link to enclosed class', @@ -2527,7 +2581,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( plus.linkedName, equals( - 'operator +')); + 'operator +')); }); }); @@ -2764,8 +2818,10 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect(lengthX.oneLineDoc, equals('Returns a length.')); // TODO(jdkoren): This is left here to have at least one literal matching // test for extendedDocLink. Move this when extracting renderer tests. - expect(lengthX.extendedDocLink, - equals('[...]')); + expect( + lengthX.extendedDocLink, + equals( + '[...]')); expect(lengthX.documentation, contains('the fourth dimension')); expect(lengthX.documentation, isNot(contains('[...]'))); }); @@ -2776,14 +2832,17 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('inherited property has a linked name to superclass in package', () { - expect(mInB.linkedName, equals('m')); + expect(mInB.linkedName, + equals('m')); }); test( 'inherited property has linked name to enclosed class, if superclass is not in package', () { - expect(isEmpty.linkedName, - equals('isEmpty')); + expect( + isEmpty.linkedName, + equals( + 'isEmpty')); }); test('inherited property has the enclosing class', () { @@ -2917,7 +2976,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( fieldWithTypedef.linkedReturnType, equals( - 'ParameterizedTypedef<bool>')); + 'ParameterizedTypedef<bool>')); }); }); @@ -2988,7 +3047,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( complicatedReturn.linkedReturnType, equals( - 'ATypeTakingClass<String Function(int)>')); + 'ATypeTakingClass<String Function(int)>')); }); test('@nodoc on simple property works', () { @@ -3098,7 +3157,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('substrings of the constant values type are not linked (#1535)', () { expect(aName.constantValue, - 'const ExtendedShortName("hello there")'); + 'const ExtendedShortName("hello there")'); }); test('constant field values are escaped', () { @@ -3142,7 +3201,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('MY_CAT is linked', () { expect(cat.constantValue, - 'const ConstantCat('tabby')'); + 'const ConstantCat('tabby')'); }); test('exported property', () { @@ -3186,11 +3245,11 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( referToADefaultConstructor.documentationAsHtml, contains( - 'ReferToADefaultConstructor')); + 'ReferToADefaultConstructor')); expect( referToADefaultConstructor.documentationAsHtml, contains( - 'ReferToADefaultConstructor.ReferToADefaultConstructor')); + 'ReferToADefaultConstructor.ReferToADefaultConstructor')); }); test('displays generic parameters correctly', () { @@ -3280,7 +3339,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( ExtendsFutureVoid.linkedName, equals( - 'ExtendsFutureVoid')); + 'ExtendsFutureVoid')); DefinedElementType FutureVoid = ExtendsFutureVoid.publicSuperChain .firstWhere((c) => c.name == 'Future'); expect( @@ -3293,7 +3352,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( ImplementsFutureVoid.linkedName, equals( - 'ImplementsFutureVoid')); + 'ImplementsFutureVoid')); DefinedElementType FutureVoid = ImplementsFutureVoid.publicInterfaces .firstWhere((c) => c.name == 'Future'); expect( @@ -3306,13 +3365,13 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( ATypeTakingClassMixedIn.linkedName, equals( - 'ATypeTakingClassMixedIn')); + 'ATypeTakingClassMixedIn')); DefinedElementType ATypeTakingClassVoid = ATypeTakingClassMixedIn.mixins .firstWhere((c) => c.name == 'ATypeTakingClass'); expect( ATypeTakingClassVoid.linkedName, equals( - 'ATypeTakingClass<void>')); + 'ATypeTakingClass<void>')); }); }); @@ -3355,7 +3414,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( ParameterRendererHtml().renderLinkedParams(theConstructor.parameters), equals( - 'ParameterizedTypedef<double> x')); + 'ParameterizedTypedef<double> x')); }); test('anonymous nested functions inside typedefs are handled', () { @@ -3536,7 +3595,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( params, equals( - 'processMessage p')); + 'processMessage p')); }); }); @@ -3615,7 +3674,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( forAnnotation.annotations.first, equals( - '@ForAnnotation('my value')')); + '@ForAnnotation('my value')')); }); test('methods has the right annotation', () { @@ -3628,7 +3687,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( ctr.annotations[0], equals( - '@Deprecated("Internal use")')); + '@Deprecated("Internal use")')); }); }); diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart index 94ff87e57b..97a479596d 100644 --- a/testing/test_package/lib/fake.dart +++ b/testing/test_package/lib/fake.dart @@ -887,6 +887,10 @@ class BaseForDocComments { /// Reference containing a type parameter [ExtraSpecialList] /// /// Reference to something that doesn't exist containing a type parameter [ThisIsNotHereNoWay] + /// + /// Link to a nonexistent file (erroneously expects base href): [link](SubForDocComments/localMethod.html) + /// + /// Link to an existing file: [link](../SubForDocComments/localMethod.html) String doAwesomeStuff(int value) => null; void anotherMethod() {}