// Copyright 2013 The Flutter Authors. 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:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:flutter_plugin_tools/src/readme_check_command.dart'; import 'package:test/test.dart'; import 'mocks.dart'; import 'util.dart'; void main() { late CommandRunner runner; late RecordingProcessRunner processRunner; late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; setUp(() { fileSystem = MemoryFileSystem(); mockPlatform = MockPlatform(); packagesDir = fileSystem.currentDirectory.childDirectory('packages'); createPackagesDirectory(parentDir: packagesDir.parent); processRunner = RecordingProcessRunner(); final ReadmeCheckCommand command = ReadmeCheckCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, ); runner = CommandRunner( 'readme_check_command', 'Test for readme_check_command'); runner.addCommand(command); }); test('fails when README is missing', () async { createFakePackage('a_package', packagesDir); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('Missing README.md'), ]), ); }); group('plugin OS support', () { test( 'does not check support table for anything other than app-facing plugin packages', () async { const String federatedPluginName = 'a_federated_plugin'; final Directory federatedDir = packagesDir.childDirectory(federatedPluginName); final List packages = [ // A non-plugin package. createFakePackage('a_package', packagesDir), // Non-app-facing parts of a federated plugin. createFakePlugin( '${federatedPluginName}_platform_interface', federatedDir), createFakePlugin('${federatedPluginName}_android', federatedDir), ]; for (final RepositoryPackage package in packages) { package.readmeFile.writeAsStringSync(''' A very useful package. '''); } final List output = await runCapturingPrint(runner, [ 'readme-check', ]); expect( output, containsAll([ contains('Running for a_package...'), contains('Running for a_federated_plugin_platform_interface...'), contains('Running for a_federated_plugin_android...'), contains('No issues found!'), ]), ); }); test('fails when non-federated plugin is missing an OS support table', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('No OS support table found'), ]), ); }); test( 'fails when app-facing part of a federated plugin is missing an OS support table', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('No OS support table found'), ]), ); }); test('fails the OS support table is missing the header', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('OS support table does not have the expected header format'), ]), ); }); test('fails if the OS support table is missing a supported OS', () async { final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | |----------------|---------|----------| | **Support** | SDK 21+ | iOS 10+* | '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains(' OS support table does not match supported platforms:\n' ' Actual: android, ios, web\n' ' Documented: android, ios'), contains('Incorrect OS support table'), ]), ); }); test('fails if the OS support table lists an extra OS', () async { final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | Web | |----------------|---------|----------|------------------------| | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains(' OS support table does not match supported platforms:\n' ' Actual: android, ios\n' ' Documented: android, ios, web'), contains('Incorrect OS support table'), ]), ); }); test('fails if the OS support table has unexpected OS formatting', () async { final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | android | ios | MacOS | web | |----------------|---------|----------|-------|------------------------| | **Support** | SDK 21+ | iOS 10+* | 10.11 | [See `camera_web `][1] | '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains(' Incorrect OS capitalization: android, ios, MacOS, web\n' ' Please use standard capitalizations: Android, iOS, macOS, Web\n'), contains('Incorrect OS support formatting'), ]), ); }); }); group('code blocks', () { test('fails on missing info string', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ``` void main() { // ... } ``` '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('Code block at line 3 is missing a language identifier.'), contains('Missing language identifier for code block'), ]), ); }); test('allows unknown info strings', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```someunknowninfotag A B C ``` '''); final List output = await runCapturingPrint(runner, [ 'readme-check', ]); expect( output, containsAll([ contains('Running for a_package...'), contains('No issues found!'), ]), ); }); test('allows space around info strings', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ``` dart A B C ``` '''); final List output = await runCapturingPrint(runner, [ 'readme-check', ]); expect( output, containsAll([ contains('Running for a_package...'), contains('No issues found!'), ]), ); }); test('passes when excerpt requirement is met', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```dart A B C ``` '''); final List output = await runCapturingPrint( runner, ['readme-check', '--require-excerpts']); expect( output, containsAll([ contains('Running for a_package...'), contains('No issues found!'), ]), ); }); test('fails on missing excerpt tag when requested', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```dart A B C ``` '''); Error? commandError; final List output = await runCapturingPrint( runner, ['readme-check', '--require-excerpts'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('Dart code block at line 3 is not managed by code-excerpt.'), contains('Missing code-excerpt management for code block'), ]), ); }); }); }