// 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/update_excerpts_command.dart'; import 'package:test/test.dart'; import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; void runAllTests(MockPlatform platform) { late FileSystem fileSystem; late Directory packagesDir; late CommandRunner runner; setUp(() { fileSystem = MemoryFileSystem( style: platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix); packagesDir = createPackagesDirectory(fileSystem: fileSystem); runner = CommandRunner('', '') ..addCommand(UpdateExcerptsCommand( packagesDir, platform: platform, processRunner: RecordingProcessRunner(), gitDir: MockGitDir(), )); }); Future testInjection( {required String before, required String source, required String after, required String filename, bool failOnChange = false}) async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(before); package.directory.childFile(filename).writeAsStringSync(source); Object? errorObject; final List output = await runCapturingPrint( runner, [ 'update-excerpts', if (failOnChange) '--fail-on-change', ], errorHandler: (Object error) { errorObject = error; }, ); if (errorObject != null) { fail('Failed: $errorObject\n\nOutput from excerpt command:\n$output'); } expect(package.readmeFile.readAsStringSync(), after); } test('succeeds when nothing has changed', () async { const String filename = 'main.dart'; const String readme = ''' Example: ```dart A B C ``` '''; const String source = ''' FAIL // #docregion SomeSection A B C // #enddocregion SomeSection FAIL '''; await testInjection( before: readme, source: source, after: readme, filename: filename); }); test('fails if example injection fails', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```dart A B C ``` '''); package.directory.childFile('main.dart').writeAsStringSync(''' FAIL // #docregion SomeSection A B C // #enddocregion SomeSection FAIL '''); Error? commandError; final List output = await runCapturingPrint( runner, ['update-excerpts'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('Injecting excerpts failed:'), contains( 'main.dart: did not find a "// #docregion UnknownSection" pragma'), ]), ); }); test('updates files', () async { const String filename = 'main.dart'; const String before = ''' Example: ```dart X Y Z ``` '''; const String source = ''' FAIL // #docregion SomeSection A B C // #enddocregion SomeSection FAIL '''; const String after = ''' Example: ```dart A B C ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); test('fails if READMEs are changed with --fail-on-change', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```dart X Y Z ``` '''); package.directory.childFile('main.dart').writeAsStringSync(''' FAIL // #docregion SomeSection A B C // #enddocregion SomeSection FAIL '''); Error? commandError; final List output = await runCapturingPrint( runner, ['update-excerpts', '--fail-on-change'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output.join('\n'), contains('The following files have out of date excerpts:'), ); }); test('does not fail if READMEs are not changed with --fail-on-change', () async { const String filename = 'main.dart'; const String readme = ''' Example: ```dart A ``` ```dart B ``` '''; const String source = ''' // #docregion aa A // #enddocregion aa // #docregion bb B // #enddocregion bb '''; await testInjection( before: readme, source: source, after: readme, filename: filename, failOnChange: true, ); }); test('indents the plaster', () async { const String filename = 'main.dart'; const String before = ''' Example: ```dart ``` '''; const String source = ''' // #docregion SomeSection A // #enddocregion SomeSection // #docregion SomeSection B // #enddocregion SomeSection '''; const String after = ''' Example: ```dart A // ··· B ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); test('does not unindent blocks if plaster will not unindent', () async { const String filename = 'main.dart'; const String before = ''' Example: ```dart ``` '''; const String source = ''' // #docregion SomeSection A // #enddocregion SomeSection // #docregion SomeSection B // #enddocregion SomeSection '''; const String after = ''' Example: ```dart A // ··· B ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); test('unindents blocks', () async { const String filename = 'main.dart'; const String before = ''' Example: ```dart ``` '''; const String source = ''' // #docregion SomeSection A // #enddocregion SomeSection // #docregion SomeSection B // #enddocregion SomeSection '''; const String after = ''' Example: ```dart A // ··· B ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); test('unindents blocks and plaster', () async { const String filename = 'main.dart'; const String before = ''' Example: ```dart ``` '''; const String source = ''' // #docregion SomeSection A // #enddocregion SomeSection // #docregion SomeSection B // #enddocregion SomeSection '''; const String after = ''' Example: ```dart A // ··· B ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); test('relative path bases', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' ```dart ``` ```dart ``` ```dart ``` ```dart ``` ```dart ``` ```dart ``` ```dart ``` ```dart ``` '''); package.directory.childFile('main.dart').writeAsStringSync(''' // #docregion a X // #enddocregion a '''); package.directory.childDirectory('test').createSync(); package.directory .childDirectory('test') .childFile('main.dart') .writeAsStringSync(''' // #docregion a Y // #enddocregion a '''); package.directory .childDirectory('test') .childDirectory('test') .createSync(); package.directory .childDirectory('test') .childDirectory('test') .childFile('main.dart') .writeAsStringSync(''' // #docregion a Z // #enddocregion a '''); await runCapturingPrint(runner, ['update-excerpts']); expect(package.readmeFile.readAsStringSync(), ''' ```dart X ``` ```dart Y ``` ```dart Z ``` ```dart Y ``` ```dart X ``` ```dart Z ``` ```dart X ``` ```dart Y ``` '''); }); test('logs snippets checked', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.readmeFile.writeAsStringSync(''' Example: ```dart A B C ``` '''); package.directory.childFile('main.dart').writeAsStringSync(''' FAIL // #docregion SomeSection A B C // #enddocregion SomeSection FAIL '''); final List output = await runCapturingPrint(runner, ['update-excerpts']); expect( output, containsAllInOrder([ contains('Checked 1 snippet(s) in README.md.'), ]), ); }); group('File type tests', () { const List> testCases = >[ {'filename': 'main.cc', 'language': 'c++'}, {'filename': 'main.cpp', 'language': 'c++'}, {'filename': 'main.dart'}, {'filename': 'main.js'}, {'filename': 'main.kt', 'language': 'kotlin'}, {'filename': 'main.java'}, {'filename': 'main.gradle', 'language': 'groovy'}, {'filename': 'main.m', 'language': 'objectivec'}, {'filename': 'main.swift'}, { 'filename': 'main.css', 'prefix': '/* ', 'suffix': ' */' }, { 'filename': 'main.html', 'prefix': '' }, { 'filename': 'main.xml', 'prefix': '' }, {'filename': 'main.yaml', 'prefix': '# '}, {'filename': 'main.sh', 'prefix': '# '}, {'filename': 'main', 'language': 'txt', 'prefix': ''}, ]; void runTest(Map testCase) { test('updates ${testCase['filename']} files', () async { final String filename = testCase['filename']!; final String language = testCase['language'] ?? filename.split('.')[1]; final String prefix = testCase['prefix'] ?? '// '; final String suffix = testCase['suffix'] ?? ''; final String before = ''' Example: ```$language X Y Z ``` '''; final String source = ''' FAIL $prefix#docregion SomeSection$suffix A B C $prefix#enddocregion SomeSection$suffix FAIL '''; final String after = ''' Example: ```$language A B C ``` '''; await testInjection( before: before, source: source, after: after, filename: filename); }); } testCases.forEach(runTest); }); } void main() { runAllTests(MockPlatform()); runAllTests(MockPlatform(isWindows: true)); }