Unverified Commit f9c6f305 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Allow users to create samples using flutter create. (#23584)

This adds flutter create --sample which allows users to execute a command which will create a working sample app from samples embedded in the API docs.

The command looks something like this:

flutter create --sample=chip.DeletableChipAttributes.onDeleted mysample
parent 4559ae1a
......@@ -20,7 +20,7 @@
</div>
<div class="snippet" id="longSnippet" hidden>
<div class="snippet-description">To create a sample project with this code snippet, run:<br/>
<span class="snippet-create-command">flutter create --snippet={{id}} mysample</span>
<span class="snippet-create-command">flutter create --sample={{id}} mysample</span>
</div>
<div class="copyable-container">
<button class="copy-button-overlay copy-button" title="Copy to clipboard"
......
......@@ -19,7 +19,7 @@ import 'globals.dart';
class FlutterVersion {
@visibleForTesting
FlutterVersion(this._clock) {
FlutterVersion([this._clock = const Clock()]) {
_channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}');
final String branch = _runGit('git rev-parse --abbrev-ref HEAD');
_branch = branch == 'HEAD' ? _channel : branch;
......
......@@ -8,6 +8,7 @@ import 'dart:convert';
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/create.dart';
import 'package:flutter_tools/src/dart/sdk.dart';
......@@ -56,6 +57,7 @@ void main() {
'ios/Flutter/AppFrameworkInfo.plist',
'ios/Runner/AppDelegate.m',
'ios/Runner/GeneratedPluginRegistrant.h',
'lib/main.dart',
],
);
return _runFlutterTest(projectDir);
......@@ -719,18 +721,52 @@ void main() {
);
});
// Verify that we fail with an error code when the file exists.
testUsingContext('fails when file exists', () async {
testUsingContext('fails when file exists where output directory should be', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final File existingFile = fs.file('${projectDir.path.toString()}/bad');
final File existingFile = fs.file(fs.path.join(projectDir.path, 'bad'));
if (!existingFile.existsSync()) {
existingFile.createSync(recursive: true);
}
expect(
runner.run(<String>['create', existingFile.path]),
throwsToolExit(message: 'file exists'),
throwsToolExit(message: 'existing file'),
);
});
testUsingContext('fails overwrite when file exists where output directory should be', () async {
Cache.flutterRoot = '../..';
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
final File existingFile = fs.file(fs.path.join(projectDir.path, 'bad'));
if (!existingFile.existsSync()) {
existingFile.createSync(recursive: true);
}
expect(
runner.run(<String>['create', '--overwrite', existingFile.path]),
throwsToolExit(message: 'existing file'),
);
});
testUsingContext('overwrites existing directory when requested', () async {
Cache.flutterRoot = '../..';
final Directory existingDirectory = fs.directory(fs.path.join(projectDir.path, 'bad'));
if (!existingDirectory.existsSync()) {
existingDirectory.createSync(recursive: true);
}
final File existingFile = fs.file(fs.path.join(existingDirectory.path, 'lib', 'main.dart'));
existingFile.createSync(recursive: true);
await _createProject(
fs.directory(existingDirectory.path),
<String>['--overwrite'],
<String>[
'android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
'lib/main.dart',
'ios/Flutter/AppFrameworkInfo.plist',
'ios/Runner/AppDelegate.m',
'ios/Runner/GeneratedPluginRegistrant.h',
],
);
});
......@@ -779,6 +815,24 @@ void main() {
ProcessManager: () => loggingProcessManager,
},
);
testUsingContext('can create a sample-based project', () async {
await _createAndAnalyzeProject(
projectDir,
<String>['--no-pub', '--sample=foo.bar.Baz'],
<String>[
'lib/main.dart',
'flutter_project.iml',
'android/app/src/main/AndroidManifest.xml',
'ios/Flutter/AppFrameworkInfo.plist',
],
unexpectedPaths: <String>['test'],
);
expect(projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(),
contains('void main() {}'));
}, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() {}'),
});
}
Future<void> _createProject(
......@@ -901,3 +955,62 @@ class LoggingProcessManager extends LocalProcessManager {
);
}
}
class MockHttpClient implements HttpClient {
MockHttpClient(this.statusCode, {this.result});
final int statusCode;
final String result;
@override
Future<HttpClientRequest> getUrl(Uri url) async {
return MockHttpClientRequest(statusCode, result: result);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClient - $invocation';
}
}
class MockHttpClientRequest implements HttpClientRequest {
MockHttpClientRequest(this.statusCode, {this.result});
final int statusCode;
final String result;
@override
Future<HttpClientResponse> close() async {
return MockHttpClientResponse(statusCode, result: result);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClientRequest - $invocation';
}
}
class MockHttpClientResponse extends Stream<List<int>> implements HttpClientResponse {
MockHttpClientResponse(this.statusCode, {this.result});
@override
final int statusCode;
final String result;
@override
String get reasonPhrase => '<reason phrase>';
@override
StreamSubscription<List<int>> listen(void onData(List<int> event), {
Function onError, void onDone(), bool cancelOnError
}) {
return Stream<List<int>>.fromIterable(<List<int>>[result.codeUnits])
.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClientResponse - $invocation';
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment