github_template_test.dart 5.84 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:file/file.dart';
import 'package:file/memory.dart';
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
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(),
27 28
      FileSystem: () => MemoryFileSystem(),
      ProcessManager: () => FakeProcessManager.any(),
29 30 31 32 33 34 35 36 37 38
    });

    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(),
39 40
      FileSystem: () => MemoryFileSystem(),
      ProcessManager: () => FakeProcessManager.any(),
41 42
    });

43 44
    group('new issue template URL', () {
      StackTrace stackTrace;
45 46 47 48
      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';
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
      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'];
110 111
        const String expectedBody = '''
## Command
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
```
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(),
      });
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    });
  });
}


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();
  }
}