| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// This test library handles checks against the model for configurations |
| /// that require different PacakgeGraph configurations. Since those |
| /// take a long time to initialize, isolate them here to keep model_test |
| /// fast. |
| library dartdoc.model_special_cases_test; |
| |
| import 'dart:io'; |
| |
| import 'package:dartdoc/dartdoc.dart'; |
| import 'package:dartdoc/src/model/model.dart'; |
| import 'package:dartdoc/src/special_elements.dart'; |
| import 'package:dartdoc/src/warnings.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'src/utils.dart' as utils; |
| |
| void main() { |
| Directory sdkDir = defaultSdkDir; |
| |
| if (sdkDir == null) { |
| print("Warning: unable to locate the Dart SDK."); |
| exit(1); |
| } |
| |
| // Experimental features not yet enabled by default. Move tests out of this block |
| // when the feature is enabled by default. |
| group('Experiments', () { |
| Library lateFinalWithoutInitializer, nnbdClassMemberDeclarations; |
| Class b; |
| setUpAll(() async { |
| lateFinalWithoutInitializer = (await utils.testPackageGraphExperiments) |
| .libraries |
| .firstWhere((lib) => lib.name == 'late_final_without_initializer'); |
| nnbdClassMemberDeclarations = (await utils.testPackageGraphExperiments) |
| .libraries |
| .firstWhere((lib) => lib.name == 'nnbd_class_member_declarations'); |
| b = nnbdClassMemberDeclarations.allClasses |
| .firstWhere((c) => c.name == 'B'); |
| }); |
| |
| test('method parameters with required', () { |
| Method m1 = b.allInstanceMethods.firstWhere((m) => m.name == 'm1'); |
| Parameter p1 = m1.allParameters.firstWhere((p) => p.name == 'p1'); |
| Parameter p2 = m1.allParameters.firstWhere((p) => p.name == 'p2'); |
| expect(p1.isRequiredNamed, isTrue); |
| expect(p2.isRequiredNamed, isFalse); |
| expect(p2.isNamed, isTrue); |
| |
| expect( |
| m1.linkedParamsLines, |
| equals( |
| '<ol class="parameter-list"><li><span class="parameter" id="m1-param-some"><span class="type-annotation">int</span> <span class="parameter-name">some</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-regular"><span class="type-annotation">dynamic</span> <span class="parameter-name">regular</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-parameters"><span>covariant</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">parameters</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-p1">{<span>required</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">p1</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-p2"><span class="type-annotation">int</span> <span class="parameter-name">p2</span>: <span class="default-value">3</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-p3"><span>required</span> <span>covariant</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">p3</span>, </span></li>\n' |
| '<li><span class="parameter" id="m1-param-p4"><span>required</span> <span>covariant</span> <span class="type-annotation">int</span> <span class="parameter-name">p4</span>}</span></li>\n' |
| '</ol>')); |
| }); |
| |
| test('verify no regression on ordinary optionals', () { |
| Method m2 = b.allInstanceMethods.firstWhere((m) => m.name == 'm2'); |
| Parameter sometimes = |
| m2.allParameters.firstWhere((p) => p.name == 'sometimes'); |
| Parameter optionals = |
| m2.allParameters.firstWhere((p) => p.name == 'optionals'); |
| expect(sometimes.isRequiredNamed, isFalse); |
| expect(sometimes.isRequiredPositional, isTrue); |
| expect(sometimes.isOptionalPositional, isFalse); |
| expect(optionals.isRequiredNamed, isFalse); |
| expect(optionals.isRequiredPositional, isFalse); |
| expect(optionals.isOptionalPositional, isTrue); |
| |
| expect( |
| m2.linkedParamsLines, |
| equals( |
| '<ol class="parameter-list"><li><span class="parameter" id="m2-param-sometimes"><span class="type-annotation">int</span> <span class="parameter-name">sometimes</span>, </span></li>\n' |
| '<li><span class="parameter" id="m2-param-we"><span class="type-annotation">dynamic</span> <span class="parameter-name">we</span>, </span></li>\n' |
| '<li><span class="parameter" id="m2-param-have">[<span class="type-annotation">String</span> <span class="parameter-name">have</span>, </span></li>\n' |
| '<li><span class="parameter" id="m2-param-optionals"><span class="type-annotation">double</span> <span class="parameter-name">optionals</span>]</span></li>\n' |
| '</ol>')); |
| }); |
| |
| test('Late final class member test', () { |
| Class c = lateFinalWithoutInitializer.allClasses |
| .firstWhere((c) => c.name == 'C'); |
| Field a = c.allFields.firstWhere((f) => f.name == 'a'); |
| Field b = c.allFields.firstWhere((f) => f.name == 'b'); |
| Field cField = c.allFields.firstWhere((f) => f.name == 'cField'); |
| Field dField = c.allFields.firstWhere((f) => f.name == 'dField'); |
| |
| // If nnbd isn't enabled, fields named 'late' come back from the analyzer |
| // instead of setting up 'isLate'. |
| expect(c.allFields.any((f) => f.name == 'late'), isFalse); |
| |
| expect(a.modelType.returnType.name, equals('dynamic')); |
| expect(a.isLate, isTrue); |
| expect(a.features, contains('late')); |
| |
| expect(b.modelType.returnType.name, equals('int')); |
| expect(b.isLate, isTrue); |
| expect(b.features, contains('late')); |
| |
| expect(cField.modelType.returnType.name, equals('dynamic')); |
| expect(cField.isLate, isTrue); |
| expect(cField.features, contains('late')); |
| |
| expect(dField.modelType.returnType.name, equals('double')); |
| expect(dField.isLate, isTrue); |
| expect(dField.features, contains('late')); |
| }); |
| |
| test('Late final top level variables', () { |
| TopLevelVariable initializeMe = lateFinalWithoutInitializer |
| .publicProperties |
| .firstWhere((v) => v.name == 'initializeMe'); |
| expect(initializeMe.modelType.returnType.name, equals('String')); |
| expect(initializeMe.isLate, isTrue); |
| expect(initializeMe.features, contains('late')); |
| }); |
| }); |
| |
| group('HTML Injection when allowed', () { |
| Class htmlInjection; |
| Method injectSimpleHtml; |
| Method injectHtmlFromTool; |
| |
| PackageGraph injectionPackageGraph; |
| Library injectionExLibrary; |
| |
| setUpAll(() async { |
| injectionPackageGraph = await utils.bootBasicPackage( |
| 'testing/test_package', ['css', 'code_in_comments', 'excluded'], |
| additionalArguments: ['--inject-html']); |
| |
| injectionExLibrary = |
| injectionPackageGraph.libraries.firstWhere((lib) => lib.name == 'ex'); |
| |
| htmlInjection = injectionExLibrary.classes |
| .firstWhere((c) => c.name == 'HtmlInjection'); |
| injectSimpleHtml = htmlInjection.allInstanceMethods |
| .firstWhere((m) => m.name == 'injectSimpleHtml'); |
| injectHtmlFromTool = htmlInjection.allInstanceMethods |
| .firstWhere((m) => m.name == 'injectHtmlFromTool'); |
| injectionPackageGraph.allLocalModelElements |
| .forEach((m) => m.documentation); |
| }); |
| |
| test("can inject HTML", () { |
| expect( |
| injectSimpleHtml.documentation, |
| contains( |
| '\n<dartdoc-html>bad2bbdd4a5cf9efb3212afff4449904756851aa</dartdoc-html>\n')); |
| expect(injectSimpleHtml.documentation, |
| isNot(contains('\n{@inject-html}\n'))); |
| expect(injectSimpleHtml.documentation, |
| isNot(contains('\n{@end-inject-html}\n'))); |
| expect(injectSimpleHtml.documentationAsHtml, |
| contains(' <div style="opacity: 0.5;">[HtmlInjection]</div>')); |
| }); |
| test("can inject HTML from tool", () { |
| RegExp envLine = RegExp(r'^Env: \{', multiLine: true); |
| expect(envLine.allMatches(injectHtmlFromTool.documentation).length, |
| equals(2)); |
| RegExp argLine = RegExp(r'^Args: \[', multiLine: true); |
| expect(argLine.allMatches(injectHtmlFromTool.documentation).length, |
| equals(2)); |
| expect( |
| injectHtmlFromTool.documentation, |
| contains( |
| 'Invokes more than one tool in the same comment block, and injects HTML.')); |
| expect(injectHtmlFromTool.documentationAsHtml, |
| contains('<div class="title">Title</div>')); |
| expect(injectHtmlFromTool.documentationAsHtml, |
| isNot(contains('{@inject-html}'))); |
| expect(injectHtmlFromTool.documentationAsHtml, |
| isNot(contains('{@end-inject-html}'))); |
| }); |
| }); |
| |
| group('Missing and Remote', () { |
| PackageGraph ginormousPackageGraph; |
| |
| setUpAll(() async { |
| ginormousPackageGraph = await utils.testPackageGraphGinormous; |
| }); |
| |
| test('Verify that SDK libraries are not canonical when missing', () { |
| expect(ginormousPackageGraph.publicPackages, isNotEmpty); |
| }); |
| |
| test( |
| 'Verify that autoIncludeDependencies makes everything document locally', |
| () { |
| expect(ginormousPackageGraph.packages.map((p) => p.documentedWhere), |
| everyElement((x) => x == DocumentLocation.local)); |
| }); |
| |
| test('Verify that ginormousPackageGraph takes in the SDK', () { |
| expect( |
| ginormousPackageGraph.packages |
| .firstWhere((p) => p.isSdk) |
| .libraries |
| .length, |
| greaterThan(1)); |
| expect( |
| ginormousPackageGraph.packages |
| .firstWhere((p) => p.isSdk) |
| .documentedWhere, |
| equals(DocumentLocation.local)); |
| }); |
| }); |
| |
| group('Category', () { |
| PackageGraph ginormousPackageGraph; |
| |
| setUpAll(() async { |
| ginormousPackageGraph = await utils.testPackageGraphGinormous; |
| }); |
| |
| test( |
| 'Verify auto-included dependencies do not use default package category definitions', |
| () { |
| Class IAmAClassWithCategories = ginormousPackageGraph.localPackages |
| .firstWhere((Package p) => p.name == 'test_package_imported') |
| .publicLibraries |
| .firstWhere((Library l) => l.name == 'categoriesExported') |
| .publicClasses |
| .firstWhere((Class c) => c.name == 'IAmAClassWithCategories'); |
| expect(IAmAClassWithCategories.hasCategoryNames, isTrue); |
| expect(IAmAClassWithCategories.categories.length, equals(1)); |
| expect( |
| IAmAClassWithCategories.categories.first.name, equals('Excellent')); |
| expect(IAmAClassWithCategories.displayedCategories, isEmpty); |
| }); |
| |
| // For flutter, we allow reexports to pick up categories from the package |
| // they are exposed in. |
| test('Verify that reexported classes pick up categories', () { |
| Class IAmAClassWithCategoriesReexport = ginormousPackageGraph |
| .localPackages |
| .firstWhere((Package p) => p.name == 'test_package') |
| .publicLibraries |
| .firstWhere((Library l) => l.name == 'fake') |
| .publicClasses |
| .firstWhere((Class c) => c.name == 'IAmAClassWithCategories'); |
| expect(IAmAClassWithCategoriesReexport.hasCategoryNames, isTrue); |
| expect(IAmAClassWithCategoriesReexport.categories.length, equals(1)); |
| expect(IAmAClassWithCategoriesReexport.categories.first.name, |
| equals('Superb')); |
| expect(IAmAClassWithCategoriesReexport.displayedCategories, isNotEmpty); |
| Category category = |
| IAmAClassWithCategoriesReexport.displayedCategories.first; |
| expect(category.categoryIndex, equals(0)); |
| expect(category.isDocumented, isTrue); |
| }); |
| |
| test('Verify that multiple categories work correctly', () { |
| Library fakeLibrary = ginormousPackageGraph.localPackages |
| .firstWhere((Package p) => p.name == 'test_package') |
| .publicLibraries |
| .firstWhere((Library l) => l.name == 'fake'); |
| Class BaseForDocComments = fakeLibrary.publicClasses |
| .firstWhere((Class c) => c.name == 'BaseForDocComments'); |
| Class SubForDocComments = fakeLibrary.publicClasses |
| .firstWhere((Class c) => c.name == 'SubForDocComments'); |
| expect(BaseForDocComments.hasCategoryNames, isTrue); |
| // Display both, with the correct order and display name. |
| expect(BaseForDocComments.displayedCategories.length, equals(2)); |
| expect( |
| BaseForDocComments.displayedCategories.first.name, equals('Superb')); |
| expect( |
| BaseForDocComments.displayedCategories.last.name, equals('Unreal')); |
| // Subclasses do not inherit category information. |
| expect(SubForDocComments.hasCategoryNames, isTrue); |
| expect(SubForDocComments.categories, hasLength(1)); |
| expect(SubForDocComments.categories.first.isDocumented, isFalse); |
| expect(SubForDocComments.displayedCategories, isEmpty); |
| }); |
| |
| test('Verify that packages without categories get handled', () async { |
| PackageGraph packageGraphSmall = await utils.testPackageGraphSmall; |
| expect(packageGraphSmall.localPackages.length, equals(1)); |
| expect(packageGraphSmall.localPackages.first.hasCategories, isFalse); |
| List<Category> packageCategories = |
| packageGraphSmall.localPackages.first.categories; |
| expect(packageCategories.length, equals(0)); |
| }); |
| }); |
| |
| group('Package', () { |
| PackageGraph ginormousPackageGraph, sdkAsPackageGraph; |
| |
| setUpAll(() async { |
| ginormousPackageGraph = await utils.testPackageGraphGinormous; |
| sdkAsPackageGraph = await utils.testPackageGraphSdk; |
| }); |
| |
| group('test package', () { |
| test('multiple packages, sorted default', () { |
| expect(ginormousPackageGraph.localPackages, hasLength(5)); |
| expect(ginormousPackageGraph.localPackages.first.name, |
| equals('test_package')); |
| }); |
| |
| test('sdk name', () { |
| expect(sdkAsPackageGraph.defaultPackage.name, equals('Dart')); |
| expect(sdkAsPackageGraph.defaultPackage.kind, equals('SDK')); |
| }); |
| |
| test('sdk homepage', () { |
| expect(sdkAsPackageGraph.defaultPackage.hasHomepage, isTrue); |
| expect(sdkAsPackageGraph.defaultPackage.homepage, |
| equals('https://github.com/dart-lang/sdk')); |
| }); |
| |
| test('sdk version', () { |
| expect(sdkAsPackageGraph.defaultPackage.version, isNotNull); |
| }); |
| |
| test('sdk description', () { |
| expect(sdkAsPackageGraph.defaultPackage.documentation, |
| startsWith('Welcome to the Dart API reference doc')); |
| }); |
| }); |
| |
| group('test small package', () { |
| test('does not have documentation', () async { |
| PackageGraph packageGraphSmall = await utils.testPackageGraphSmall; |
| expect(packageGraphSmall.defaultPackage.hasDocumentation, isFalse); |
| expect(packageGraphSmall.defaultPackage.hasDocumentationFile, isFalse); |
| expect(packageGraphSmall.defaultPackage.documentationFile, isNull); |
| expect(packageGraphSmall.defaultPackage.documentation, isNull); |
| }); |
| }); |
| |
| group('SDK-specific cases', () { |
| test('Verify Interceptor is hidden from inheritance in docs', () { |
| Library htmlLibrary = sdkAsPackageGraph.libraries |
| .singleWhere((l) => l.name == 'dart:html'); |
| Class EventTarget = |
| htmlLibrary.allClasses.singleWhere((c) => c.name == 'EventTarget'); |
| Field hashCode = EventTarget.allPublicInstanceProperties |
| .singleWhere((f) => f.name == 'hashCode'); |
| Class objectModelElement = |
| sdkAsPackageGraph.specialClasses[SpecialClass.object]; |
| // If this fails, EventTarget might have been changed to no longer |
| // inherit from Interceptor. If that's true, adjust test case to |
| // another class that does. |
| expect( |
| hashCode.inheritance.any((c) => c.name == 'Interceptor'), isTrue); |
| // If EventTarget really does start implementing hashCode, this will |
| // fail. |
| expect(hashCode.href, |
| equals('${HTMLBASE_PLACEHOLDER}dart-core/Object/hashCode.html')); |
| expect( |
| hashCode.canonicalEnclosingContainer, equals(objectModelElement)); |
| expect( |
| EventTarget.publicSuperChainReversed |
| .any((et) => et.name == 'Interceptor'), |
| isFalse); |
| }); |
| |
| test('Verify pragma is hidden in SDK docs', () { |
| Class pragmaModelElement = |
| sdkAsPackageGraph.specialClasses[SpecialClass.pragma]; |
| expect(pragmaModelElement.name, equals('pragma')); |
| }); |
| }); |
| }); |
| |
| group('Library', () { |
| Library dartAsyncLib; |
| |
| setUpAll(() async { |
| dartAsyncLib = (await utils.testPackageGraphSdk) |
| .libraries |
| .firstWhere((l) => l.name == 'dart:async'); |
| // Make sure the first library is dart:async |
| expect(dartAsyncLib.name, 'dart:async'); |
| }); |
| |
| test('sdk library have formatted names', () { |
| expect(dartAsyncLib.name, 'dart:async'); |
| expect(dartAsyncLib.dirName, 'dart-async'); |
| expect(dartAsyncLib.href, |
| '${HTMLBASE_PLACEHOLDER}dart-async/dart-async-library.html'); |
| }); |
| }); |
| |
| group('YouTube Errors', () { |
| PackageGraph packageGraphErrors; |
| Class documentationErrors; |
| Method withYouTubeWrongParams; |
| Method withYouTubeBadWidth; |
| Method withYouTubeBadHeight; |
| Method withYouTubeInvalidUrl; |
| Method withYouTubeUrlWithAdditionalParameters; |
| |
| setUpAll(() async { |
| packageGraphErrors = await utils.testPackageGraphErrors; |
| documentationErrors = packageGraphErrors.libraries |
| .firstWhere((lib) => lib.name == 'doc_errors') |
| .classes |
| .firstWhere((c) => c.name == 'DocumentationErrors') |
| ..documentation; |
| withYouTubeWrongParams = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withYouTubeWrongParams') |
| ..documentation; |
| withYouTubeBadWidth = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withYouTubeBadWidth') |
| ..documentation; |
| withYouTubeBadHeight = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withYouTubeBadHeight') |
| ..documentation; |
| withYouTubeInvalidUrl = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withYouTubeInvalidUrl') |
| ..documentation; |
| withYouTubeUrlWithAdditionalParameters = documentationErrors |
| .allInstanceMethods |
| .firstWhere((m) => m.name == 'withYouTubeUrlWithAdditionalParameters') |
| ..documentation; |
| }); |
| |
| test("warns on youtube video with missing parameters", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withYouTubeWrongParams, |
| PackageWarning.invalidParameter, |
| 'Invalid @youtube directive, "{@youtube https://youtu.be/oHg5SJYRHA0}"\n' |
| 'YouTube directives must be of the form "{@youtube WIDTH HEIGHT URL}"'), |
| isTrue); |
| }); |
| test("warns on youtube video with non-integer width", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withYouTubeBadWidth, |
| PackageWarning.invalidParameter, |
| 'A @youtube directive has an invalid width, "100px". The width ' |
| 'must be a positive integer.'), |
| isTrue); |
| }); |
| test("warns on youtube video with non-integer height", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withYouTubeBadHeight, |
| PackageWarning.invalidParameter, |
| 'A @youtube directive has an invalid height, "100px". The height ' |
| 'must be a positive integer.'), |
| isTrue); |
| }); |
| test("warns on youtube video with invalid video URL", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withYouTubeInvalidUrl, |
| PackageWarning.invalidParameter, |
| 'A @youtube directive has an invalid URL: ' |
| '"http://host/path/to/video.mp4". Supported YouTube URLs have ' |
| 'the following format: ' |
| 'https://www.youtube.com/watch?v=oHg5SJYRHA0.'), |
| isTrue); |
| }); |
| test("warns on youtube video with extra parameters in URL", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withYouTubeUrlWithAdditionalParameters, |
| PackageWarning.invalidParameter, |
| 'A @youtube directive has an invalid URL: ' |
| '"https://www.youtube.com/watch?v=yI-8QHpGIP4&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&index=5". ' |
| 'Supported YouTube URLs have the following format: ' |
| 'https://www.youtube.com/watch?v=oHg5SJYRHA0.'), |
| isTrue); |
| }); |
| }); |
| |
| group('Animation Errors', () { |
| PackageGraph packageGraphErrors; |
| Class documentationErrors; |
| Method withInvalidNamedAnimation; |
| Method withAnimationNonUnique; |
| Method withAnimationNonUniqueDeprecated; |
| Method withAnimationWrongParams; |
| Method withAnimationBadWidth; |
| Method withAnimationBadHeight; |
| Method withAnimationUnknownArg; |
| |
| setUpAll(() async { |
| packageGraphErrors = await utils.testPackageGraphErrors; |
| documentationErrors = packageGraphErrors.libraries |
| .firstWhere((lib) => lib.name == 'doc_errors') |
| .classes |
| .firstWhere((c) => c.name == 'DocumentationErrors') |
| ..documentation; |
| withInvalidNamedAnimation = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withInvalidNamedAnimation') |
| ..documentation; |
| withAnimationNonUnique = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationNonUnique') |
| ..documentation; |
| withAnimationNonUniqueDeprecated = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationNonUniqueDeprecated') |
| ..documentation; |
| withAnimationWrongParams = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationWrongParams') |
| ..documentation; |
| withAnimationBadWidth = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationBadWidth') |
| ..documentation; |
| withAnimationBadHeight = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationBadHeight') |
| ..documentation; |
| withAnimationUnknownArg = documentationErrors.allInstanceMethods |
| .firstWhere((m) => m.name == 'withAnimationUnknownArg') |
| ..documentation; |
| }); |
| |
| test("warns with invalidly-named animation within the method documentation", |
| () async { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withInvalidNamedAnimation, |
| PackageWarning.invalidParameter, |
| 'An animation has an invalid identifier, "2isNot-A-ValidName". ' |
| 'The identifier can only contain letters, numbers and ' |
| 'underscores, and must not begin with a number.'), |
| isTrue); |
| }); |
| test("warns on a non-unique animation name within a method", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationNonUnique, |
| PackageWarning.invalidParameter, |
| 'An animation has a non-unique identifier, "barHerderAnimation". ' |
| 'Animation identifiers must be unique.'), |
| isTrue); |
| }); |
| test("warns on a non-unique animation name within a deprecated-form method", |
| () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationNonUniqueDeprecated, |
| PackageWarning.invalidParameter, |
| 'An animation has a non-unique identifier, "fooHerderAnimation". ' |
| 'Animation identifiers must be unique.'), |
| isTrue); |
| }); |
| test("warns on animation with missing parameters", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationWrongParams, |
| PackageWarning.invalidParameter, |
| 'Invalid @animation directive, "{@animation http://host/path/to/video.mp4}"\n' |
| 'Animation directives must be of the form "{@animation WIDTH ' |
| 'HEIGHT URL [id=ID]}"'), |
| isTrue); |
| }); |
| test("warns on animation with non-integer width", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationBadWidth, |
| PackageWarning.invalidParameter, |
| 'An animation has an invalid width (badWidthAnimation), "100px". ' |
| 'The width must be an integer.'), |
| isTrue); |
| }); |
| test("warns on animation with non-integer height", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationBadHeight, |
| PackageWarning.invalidParameter, |
| 'An animation has an invalid height (badHeightAnimation), ' |
| '"100px". The height must be an integer.'), |
| isTrue); |
| }); |
| test("Unknown arguments generate an error.", () { |
| expect( |
| packageGraphErrors.packageWarningCounter.hasWarning( |
| withAnimationUnknownArg, |
| PackageWarning.invalidParameter, |
| 'The {@animation ...} directive was called with invalid ' |
| 'parameters. FormatException: Could not find an option named "name".'), |
| isTrue); |
| }); |
| }); |
| } |