From 0aed0b61a1789ed06c9fa05a6c5fcfbd398c4811 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 3 Jan 2020 17:53:54 -0800 Subject: [PATCH] Add newlines between plugin names in GitHub template (#46937) --- .../lib/src/reporting/github_template.dart | 67 +++++---- .../general.shard/github_template_test.dart | 136 ++++++++++++++---- 2 files changed, 141 insertions(+), 62 deletions(-) diff --git a/packages/flutter_tools/lib/src/reporting/github_template.dart b/packages/flutter_tools/lib/src/reporting/github_template.dart index f2ff7ef988..922f2602e9 100644 --- a/packages/flutter_tools/lib/src/reporting/github_template.dart +++ b/packages/flutter_tools/lib/src/reporting/github_template.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:file/file.dart'; import '../base/context.dart'; +import '../base/file_system.dart'; import '../base/io.dart'; import '../base/net.dart'; import '../convert.dart'; @@ -40,27 +41,27 @@ class GitHubTemplateCreator { ) async { final String title = '[tool_crash] $errorString'; final String body = '''## Command - ``` - $command - ``` +``` +$command +``` - ## Steps to Reproduce - 1. ... - 2. ... - 3. ... +## Steps to Reproduce +1. ... +2. ... +3. ... - ## Logs - $exception - ``` - ${LineSplitter.split(stackTrace.toString()).take(20).join('\n')} - ``` - ``` - $doctorText - ``` +## Logs +$exception +``` +${LineSplitter.split(stackTrace.toString()).take(20).join('\n')} +``` +``` +$doctorText +``` - ## Flutter Application Metadata - ${_projectMetadataInformation()} - '''; +## Flutter Application Metadata +${_projectMetadataInformation()} +'''; final String fullURL = 'https://github.com/flutter/flutter/issues/new?' 'title=${Uri.encodeQueryComponent(title)}' @@ -84,29 +85,33 @@ class GitHubTemplateCreator { if (project == null || manifest == null || manifest.isEmpty) { return 'No pubspec in working directory.'; } - String description = ''; - description += ''' -**Version**: ${manifest.appVersion} -**Material**: ${manifest.usesMaterialDesign} -**Android X**: ${manifest.usesAndroidX} -**Module**: ${manifest.isModule} -**Plugin**: ${manifest.isPlugin} -**Android package**: ${manifest.androidPackage} -**iOS bundle identifier**: ${manifest.iosBundleIdentifier} -'''; + final StringBuffer description = StringBuffer() + ..writeln('**Version**: ${manifest.appVersion}') + ..writeln('**Material**: ${manifest.usesMaterialDesign}') + ..writeln('**Android X**: ${manifest.usesAndroidX}') + ..writeln('**Module**: ${manifest.isModule}') + ..writeln('**Plugin**: ${manifest.isPlugin}') + ..writeln('**Android package**: ${manifest.androidPackage}') + ..writeln('**iOS bundle identifier**: ${manifest.iosBundleIdentifier}'); + final File file = project.flutterPluginsFile; if (file.existsSync()) { - description += '### Plugins\n'; + description.writeln('### Plugins'); + // Format is: + // camera=/path/to/.pub-cache/hosted/pub.dartlang.org/camera-0.5.7+2/ for (String plugin in project.flutterPluginsFile.readAsLinesSync()) { final List pluginParts = plugin.split('='); if (pluginParts.length != 2) { continue; } - description += pluginParts.first; + // Write the last part of the path, which includes the plugin name and version. + // Example: camera-0.5.7+2 + final List pathParts = fs.path.split(pluginParts[1]); + description.writeln(pathParts.isEmpty ? pluginParts.first : pathParts.last); } } - return description; + return description.toString(); } on Exception catch (exception) { return exception.toString(); } diff --git a/packages/flutter_tools/test/general.shard/github_template_test.dart b/packages/flutter_tools/test/general.shard/github_template_test.dart index 44d406f212..8d5efde877 100644 --- a/packages/flutter_tools/test/general.shard/github_template_test.dart +++ b/packages/flutter_tools/test/general.shard/github_template_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:file/file.dart'; +import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/net.dart'; import 'package:flutter_tools/src/reporting/github_template.dart'; @@ -14,7 +16,6 @@ const String _kShortURL = 'https://www.example.com/short'; void main() { group('GitHub template creator', () { - testUsingContext('similar issues URL', () async { final GitHubTemplateCreator creator = GitHubTemplateCreator(); expect( @@ -23,6 +24,8 @@ void main() { ); }, overrides: { HttpClientFactory: () => () => SuccessShortenURLFakeHttpClient(), + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), }); testUsingContext('similar issues URL with network failure', () async { @@ -33,45 +36,116 @@ void main() { ); }, overrides: { HttpClientFactory: () => () => FakeHttpClient(), + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), }); - testUsingContext('new issue template URL', () async { - final GitHubTemplateCreator creator = GitHubTemplateCreator(); + group('new issue template URL', () { + StackTrace stackTrace; const String command = 'flutter test'; const String errorString = 'this is a 100% error'; const String exception = 'failing to succeed!!!'; - final StackTrace stackTrace = StackTrace.fromString('trace'); const String doctorText = ' [✓] Flutter (Channel report'; + FileSystem fs; - expect( - await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText), - _kShortURL - ); - }, overrides: { - HttpClientFactory: () => () => SuccessShortenURLFakeHttpClient(), - }); + setUp(() async { + stackTrace = StackTrace.fromString('trace'); + fs = MemoryFileSystem(); + }); - testUsingContext('new issue template URL with network failure', () async { - final GitHubTemplateCreator creator = GitHubTemplateCreator(); - const String command = 'flutter test'; - const String errorString = 'this is a 100% error'; - const String exception = 'failing to succeed!!!'; - final StackTrace stackTrace = StackTrace.fromString('trace'); - const String doctorText = ' [✓] Flutter (Channel report'; + testUsingContext('shortened', () async { + final GitHubTemplateCreator creator = GitHubTemplateCreator(); + expect( + await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText), + _kShortURL + ); + }, overrides: { + HttpClientFactory: () => () => SuccessShortenURLFakeHttpClient(), + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), + }); - expect( - await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText), - 'https://github.com/flutter/flutter/issues/new?title=%5Btool_crash%5D+this+is+a+100%25+error&body=%23%' - '23+Command%0A++%60%60%60%0A++flutter+test%0A++%60%60%60%0A%0A++%23%23+Steps+to+Reproduce%0A++1.+...' - '%0A++2.+...%0A++3.+...%0A%0A++%23%23+Logs%0A++failing+to+succeed%21%21%21%0A++%60%60%60%0A++trace%0A' - '++%60%60%60%0A++%60%60%60%0A+++%5B%E2%9C%93%5D+Flutter+%28Channel+report%0A++%60%60%60%0A%0A++%23%23' - '+Flutter+Application+Metadata%0A++%2A%2AVersion%2A%2A%3A+null%0A%2A%2AMaterial%2A%2A%3A+false%0A%2A' - '%2AAndroid+X%2A%2A%3A+false%0A%2A%2AModule%2A%2A%3A+false%0A%2A%2APlugin%2A%2A%3A+false%0A%2A%2AAndr' - 'oid+package%2A%2A%3A+null%0A%2A%2AiOS+bundle+identifier%2A%2A%3A+null%0A%0A++&labels=tool%2Csevere%3' - 'A+crash' - ); - }, overrides: { - HttpClientFactory: () => () => FakeHttpClient(), + testUsingContext('with network failure', () async { + final GitHubTemplateCreator creator = GitHubTemplateCreator(); + expect( + await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText), + 'https://github.com/flutter/flutter/issues/new?title=%5Btool_crash%5D+this+is+a+100%25+error&body=%23%' + '23+Command%0A%60%60%60%0Aflutter+test%0A%60%60%60%0A%0A%23%23+Steps+to+Reproduce%0A1.+...' + '%0A2.+...%0A3.+...%0A%0A%23%23+Logs%0Afailing+to+succeed%21%21%21%0A%60%60%60%0Atrace%0A' + '%60%60%60%0A%60%60%60%0A+%5B%E2%9C%93%5D+Flutter+%28Channel+report%0A%60%60%60%0A%0A%23%23' + '+Flutter+Application+Metadata%0ANo+pubspec+in+working+directory.%0A&labels=tool%2Csevere%3A+crash' + ); + }, overrides: { + HttpClientFactory: () => () => FakeHttpClient(), + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('app metadata', () async { + final GitHubTemplateCreator creator = GitHubTemplateCreator(); + final Directory projectDirectory = fs.currentDirectory; + final File pluginsFile = projectDirectory.childFile('.flutter-plugins'); + + projectDirectory + .childFile('pubspec.yaml') + .writeAsStringSync(''' +name: failing_app +version: 2.0.1+100 +flutter: + uses-material-design: true + module: + androidX: true + androidPackage: com.example.failing.android + iosBundleIdentifier: com.example.failing.ios +'''); + + pluginsFile + .writeAsStringSync(''' +camera=/fake/pub.dartlang.org/camera-0.5.7+2/ +device_info=/fake/pub.dartlang.org/pub.dartlang.org/device_info-0.4.1+4/ + '''); + + final String actualURL = await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText); + final String actualBody = Uri.parse(actualURL).queryParameters['body']; + const String expectedBody = '''## Command +``` +flutter test +``` + +## Steps to Reproduce +1. ... +2. ... +3. ... + +## Logs +failing to succeed!!! +``` +trace +``` +``` + [✓] Flutter (Channel report +``` + +## Flutter Application Metadata +**Version**: 2.0.1+100 +**Material**: true +**Android X**: true +**Module**: true +**Plugin**: false +**Android package**: com.example.failing.android +**iOS bundle identifier**: com.example.failing.ios +### Plugins +camera-0.5.7+2 +device_info-0.4.1+4 + +'''; + + expect(actualBody, expectedBody); + }, overrides: { + HttpClientFactory: () => () => FakeHttpClient(), + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + }); }); }); }