// Copyright 2014 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: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'; import '../src/common.dart'; import '../src/context.dart'; import '../src/testbed.dart'; const String _kShortURL = 'https://www.example.com/short'; void main() { group('GitHub template creator', () { testUsingContext('similar issues URL', () async { final GitHubTemplateCreator creator = GitHubTemplateCreator(); expect( await creator.toolCrashSimilarIssuesGitHubURL('this is a 100% error'), _kShortURL ); }, overrides: <Type, Generator>{ HttpClientFactory: () => () => SuccessShortenURLFakeHttpClient(), FileSystem: () => MemoryFileSystem(), ProcessManager: () => FakeProcessManager.any(), }); testUsingContext('similar issues URL with network failure', () async { final GitHubTemplateCreator creator = GitHubTemplateCreator(); expect( await creator.toolCrashSimilarIssuesGitHubURL('this is a 100% error'), 'https://github.com/flutter/flutter/issues?q=is%3Aissue+this+is+a+100%25+error' ); }, overrides: <Type, Generator>{ HttpClientFactory: () => () => FakeHttpClient(), FileSystem: () => MemoryFileSystem(), ProcessManager: () => FakeProcessManager.any(), }); 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!!!'; const String doctorText = ' [✓] Flutter (Channel report'; FileSystem fs; setUp(() async { stackTrace = StackTrace.fromString('trace'); fs = MemoryFileSystem(); }); testUsingContext('shortened', () async { final GitHubTemplateCreator creator = GitHubTemplateCreator(); expect( await creator.toolCrashIssueTemplateGitHubURL(command, errorString, exception, stackTrace, doctorText), _kShortURL ); }, overrides: <Type, Generator>{ HttpClientFactory: () => () => SuccessShortenURLFakeHttpClient(), FileSystem: () => MemoryFileSystem(), ProcessManager: () => FakeProcessManager.any(), }); 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: <Type, Generator>{ 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: <Type, Generator>{ HttpClientFactory: () => () => FakeHttpClient(), FileSystem: () => fs, ProcessManager: () => FakeProcessManager.any(), }); }); }); } class SuccessFakeHttpHeaders extends FakeHttpHeaders { @override List<String> operator [](String name) => <String>[_kShortURL]; } class SuccessFakeHttpClientResponse extends FakeHttpClientResponse { @override int get statusCode => 201; @override HttpHeaders get headers { return SuccessFakeHttpHeaders(); } } class SuccessFakeHttpClientRequest extends FakeHttpClientRequest { @override Future<HttpClientResponse> close() async { return SuccessFakeHttpClientResponse(); } } class SuccessShortenURLFakeHttpClient extends FakeHttpClient { @override Future<HttpClientRequest> postUrl(Uri url) async { return SuccessFakeHttpClientRequest(); } }