| // Copyright (c) 2023, 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. |
| |
| import 'package:analyzer/src/test_utilities/test_code_format.dart'; |
| import 'package:collection/collection.dart'; |
| import 'package:linter/src/rules.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../utils/test_code_extensions.dart'; |
| import 'server_abstract.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(DocumentLinkTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class DocumentLinkTest extends AbstractLspAnalysisServerTest { |
| static const _pubBase = 'https://pub.dev/packages/'; |
| static const _lintBase = 'https://dart.dev/tools/linter-rules/'; |
| |
| @override |
| void setUp() { |
| registerLintRules(); |
| super.setUp(); |
| } |
| |
| Future<void> test_analysisOptions_empty() async { |
| var content = ''; |
| |
| await _test_analysisOptions_links(content, []); |
| } |
| |
| Future<void> test_analysisOptions_lint_links_list() async { |
| var content = ''' |
| linter: |
| rules: |
| - /*[0*/prefer_relative_imports/*0]*/ |
| - /*[1*/prefer_single_quotes/*1]*/ |
| - /*[2*/prefer_int_literals/*2]*/ |
| '''; |
| |
| var expectedLinks = [ |
| '${_lintBase}prefer_relative_imports', |
| '${_lintBase}prefer_single_quotes', |
| '${_lintBase}prefer_int_literals', |
| ]; |
| |
| await _test_analysisOptions_links(content, expectedLinks); |
| } |
| |
| Future<void> test_analysisOptions_lint_links_map() async { |
| var content = ''' |
| linter: |
| rules: |
| /*[0*/prefer_relative_imports/*0]*/: true |
| /*[1*/prefer_single_quotes/*1]*/: true |
| /*[2*/prefer_int_literals/*2]*/: false |
| '''; |
| |
| var expectedLinks = [ |
| '${_lintBase}prefer_relative_imports', |
| '${_lintBase}prefer_single_quotes', |
| '${_lintBase}prefer_int_literals', |
| ]; |
| |
| await _test_analysisOptions_links(content, expectedLinks); |
| } |
| |
| Future<void> test_analysisOptions_linterRules_empty() async { |
| var content = ''' |
| linter: |
| rules: |
| '''; |
| |
| await _test_pubspec_links(content, []); |
| } |
| |
| Future<void> test_analysisOptions_undefinedLint() async { |
| var content = ''' |
| linter: |
| rules: |
| - undefined |
| - /*[0*/prefer_single_quotes/*0]*/ |
| '''; |
| |
| var expectedLinks = ['${_lintBase}prefer_single_quotes']; |
| |
| await _test_analysisOptions_links(content, expectedLinks); |
| } |
| |
| Future<void> test_exampleLink() async { |
| var exampleFolderPath = join(projectFolderPath, 'examples', 'api'); |
| var exampleFileUri = Uri.file(join(exampleFolderPath, 'foo.dart')); |
| |
| var code = TestCode.parse(''' |
| /// {@tool dartpad} |
| /// ** See code in [!examples/api/foo.dart!] ** |
| /// {@end-tool} |
| class A {} |
| '''); |
| |
| newFolder(exampleFolderPath); |
| newFile(mainFilePath, code.code); |
| |
| await initialize(); |
| var links = await getDocumentLinks(mainFileUri); |
| |
| var link = links!.single; |
| expect(link.range, code.range.range); |
| expect(link.target, exampleFileUri); |
| } |
| |
| Future<void> test_pubspec_empty() async { |
| var content = ''; |
| |
| await _test_pubspec_links(content, []); |
| } |
| |
| Future<void> test_pubspec_packages_empty() async { |
| var content = ''' |
| dependencies: |
| '''; |
| |
| await _test_pubspec_links(content, []); |
| } |
| |
| Future<void> test_pubspec_packages_git() async { |
| var content = ''' |
| dependencies: |
| /*[0*/github_package_1/*0]*/: |
| git: https://github.com/dart-lang/sdk.git |
| /*[1*/github_package_2/*1]*/: |
| git: git@github.com:dart-lang/sdk.git |
| /*[2*/github_package_3/*2]*/: |
| git: |
| url: https://github.com/dart-lang/sdk.git |
| '''; |
| |
| var expectedLinks = [ |
| 'https://github.com/dart-lang/sdk.git', |
| 'https://github.com/dart-lang/sdk.git', |
| 'https://github.com/dart-lang/sdk.git', |
| ]; |
| |
| await _test_pubspec_links(content, expectedLinks); |
| } |
| |
| Future<void> test_pubspec_packages_hosted() async { |
| var content = ''' |
| dependencies: |
| /*[0*/hosted_package_1/*0]*/: |
| hosted: https://custom.dart.dev/ |
| /*[1*/hosted_package_2/*1]*/: |
| hosted: |
| url: https://custom.dart.dev/ |
| '''; |
| |
| var expectedLinks = [ |
| 'https://custom.dart.dev/packages/hosted_package_1', |
| 'https://custom.dart.dev/packages/hosted_package_2', |
| ]; |
| |
| await _test_pubspec_links(content, expectedLinks); |
| } |
| |
| Future<void> test_pubspec_packages_pub() async { |
| var content = ''' |
| dependencies: |
| /*[0*/pub_package_1/*0]*/: 1.2.3 |
| /*[1*/pub_package_2/*1]*/: ^1.2.3 |
| /*[2*/pub_package_3/*2]*/: |
| /*[3*/pub_package_4/*3]*/: any |
| '''; |
| |
| var expectedLinks = [ |
| '${_pubBase}pub_package_1', |
| '${_pubBase}pub_package_2', |
| '${_pubBase}pub_package_3', |
| '${_pubBase}pub_package_4', |
| ]; |
| |
| await _test_pubspec_links(content, expectedLinks); |
| } |
| |
| Future<void> test_pubspec_packages_unknown() async { |
| var content = ''' |
| dependencies: |
| flutter: |
| sdk: flutter |
| foo: |
| path: foo/ |
| bar: |
| future_unknown_kind: |
| '''; |
| |
| await _test_pubspec_links(content, []); |
| } |
| |
| Future<void> test_pubspec_packages_withDependencyOverrides() async { |
| var content = ''' |
| dependencies: |
| /*[0*/dep_package/*0]*/: 1.0.0 |
| |
| dependency_overrides: |
| /*[1*/dep_package/*1]*/: 1.2.3 |
| '''; |
| |
| var expectedLinks = ['${_pubBase}dep_package', '${_pubBase}dep_package']; |
| |
| await _test_pubspec_links(content, expectedLinks); |
| } |
| |
| Future<void> test_pubspec_packages_withDevDependencies() async { |
| var content = ''' |
| dependencies: |
| /*[0*/dep_package/*0]*/: 1.2.3 |
| |
| dev_dependencies: |
| /*[1*/dev_dep_package/*1]*/: |
| '''; |
| |
| var expectedLinks = [ |
| '${_pubBase}dep_package', |
| '${_pubBase}dev_dep_package', |
| ]; |
| |
| await _test_pubspec_links(content, expectedLinks); |
| } |
| |
| Future<void> _test_analysisOptions_links( |
| String content, |
| List<String> expected, |
| ) async { |
| await _test_file_links( |
| analysisOptionsUri, |
| analysisOptionsPath, |
| content, |
| expected, |
| ); |
| } |
| |
| Future<void> _test_file_links( |
| Uri fileUri, |
| String filePath, |
| String content, |
| List<String> expectedLinks, |
| ) async { |
| var code = TestCode.parse(content); |
| |
| // Combine expectedLinks with the ranges from the markers in the content |
| // so we can verify both ranges and links. |
| expect(expectedLinks.length, code.ranges.length); |
| var expectedLinksWithRanges = expectedLinks.mapIndexed( |
| (i, link) => (code.ranges[i].range, link), |
| ); |
| |
| newFile(filePath, code.code); |
| |
| await initialize(); |
| var links = await getDocumentLinks(fileUri); |
| |
| // Convert the results into the same format as expectedLinksWithRanges. |
| var linkData = |
| links!.map((link) => (link.range, link.target?.toString())).toList(); |
| |
| expect(linkData, equals(expectedLinksWithRanges)); |
| } |
| |
| Future<void> _test_pubspec_links( |
| String content, |
| List<String> expected, |
| ) async { |
| await _test_file_links(pubspecFileUri, pubspecFilePath, content, expected); |
| } |
| } |