Fix document generation, eliminate template support from snippets tool. (#147893)
## Description This fixes the API doc generation that I broke when I moved the snippets tool into the framework. It removes the last of the template support (properly this time), and makes sure all of the tests pass. ## Related Issues - https://github.com/flutter/flutter/issues/144408 - https://github.com/flutter/flutter/issues/147609 - https://github.com/flutter/flutter/pull/147645 ## Tests - Fixed tests, including smoke test of doc generation.
This commit is contained in:
parent
91f2350a07
commit
8a7c18c12a
@ -113,33 +113,6 @@ demonstrate the API's functionality in a sample application, or used with the
|
||||
`dartpad` samples are embedded into the API docs web page and are live
|
||||
applications in the API documentation.
|
||||
|
||||
```dart
|
||||
/// {@tool sample --template=stateless_widget_material}
|
||||
/// This example shows how to make a simple [FloatingActionButton] in a
|
||||
/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon].
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Scaffold(
|
||||
/// appBar: AppBar(
|
||||
/// title: Text('Floating Action Button Sample'),
|
||||
/// ),
|
||||
/// body: Center(
|
||||
/// child: Text('Press the button below!')
|
||||
/// ),
|
||||
/// floatingActionButton: FloatingActionButton(
|
||||
/// onPressed: () {
|
||||
/// // Add your onPressed code here!
|
||||
/// },
|
||||
/// child: Icon(Icons.thumb_up),
|
||||
/// backgroundColor: Colors.pink,
|
||||
/// ),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
```
|
||||
|
||||
This uses the skeleton for [application](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/sample.html)
|
||||
snippets in the Flutter repo.
|
||||
|
||||
|
@ -21,7 +21,6 @@ const String _kOutputDirectoryOption = 'output-directory';
|
||||
const String _kOutputOption = 'output';
|
||||
const String _kPackageOption = 'package';
|
||||
const String _kSerialOption = 'serial';
|
||||
const String _kTemplateOption = 'template';
|
||||
const String _kTypeOption = 'type';
|
||||
|
||||
class GitStatusFailed implements Exception {
|
||||
@ -126,22 +125,14 @@ void main(List<String> argList) {
|
||||
allowed: sampleTypes,
|
||||
allowedHelp: <String, String>{
|
||||
'dartpad':
|
||||
'Produce a code sample application complete with embedding the sample in an '
|
||||
'application template for using in Dartpad.',
|
||||
'Produce a code sample application for using in Dartpad.',
|
||||
'sample':
|
||||
'Produce a code sample application complete with embedding the sample in an '
|
||||
'application template.',
|
||||
'Produce a code sample application.',
|
||||
'snippet':
|
||||
'Produce a nicely formatted piece of sample code. Does not embed the '
|
||||
'sample into an application template.',
|
||||
'Produce a nicely formatted piece of sample code.',
|
||||
},
|
||||
help: 'The type of snippet to produce.',
|
||||
);
|
||||
// TODO(goderbauer): Remove template support, this is no longer used.
|
||||
parser.addOption(
|
||||
_kTemplateOption,
|
||||
help: 'The name of the template to inject the code into.',
|
||||
);
|
||||
parser.addOption(
|
||||
_kOutputOption,
|
||||
help: 'The output name for the generated sample application. Overrides '
|
||||
|
@ -11,7 +11,6 @@ class SnippetConfiguration {
|
||||
const SnippetConfiguration({
|
||||
required this.configDirectory,
|
||||
required this.skeletonsDirectory,
|
||||
required this.templatesDirectory,
|
||||
this.filesystem = const LocalFileSystem(),
|
||||
});
|
||||
|
||||
@ -25,10 +24,6 @@ class SnippetConfiguration {
|
||||
/// and returned to dartdoc for insertion in the output.
|
||||
final Directory skeletonsDirectory;
|
||||
|
||||
/// The directory containing the code templates that can be referenced by the
|
||||
/// dartdoc.
|
||||
final Directory templatesDirectory;
|
||||
|
||||
/// Gets the skeleton file to use for the given [SampleType] and DartPad
|
||||
/// preference.
|
||||
File getHtmlSkeletonFile(String type) {
|
||||
@ -47,11 +42,6 @@ class FlutterRepoSnippetConfiguration extends SnippetConfiguration {
|
||||
const <String>['dev', 'snippets', 'config']),
|
||||
skeletonsDirectory: _underRoot(filesystem, flutterRoot,
|
||||
const <String>['dev', 'snippets', 'config', 'skeletons']),
|
||||
templatesDirectory: _underRoot(
|
||||
filesystem,
|
||||
flutterRoot,
|
||||
const <String>['dev', 'snippets', 'config', 'templates'],
|
||||
),
|
||||
);
|
||||
|
||||
final Directory flutterRoot;
|
||||
|
@ -58,8 +58,8 @@ class SourceLine {
|
||||
|
||||
/// A class containing the name and contents associated with a code block inside of a
|
||||
/// code sample, for named injection into a template.
|
||||
class TemplateInjection {
|
||||
TemplateInjection(this.name, this.contents, {this.language = ''});
|
||||
class SkeletonInjection {
|
||||
SkeletonInjection(this.name, this.contents, {this.language = ''});
|
||||
final String name;
|
||||
final List<SourceLine> contents;
|
||||
final String language;
|
||||
@ -137,7 +137,7 @@ abstract class CodeSample {
|
||||
String get element => start.element ?? '';
|
||||
String output = '';
|
||||
Map<String, Object?> metadata = <String, Object?>{};
|
||||
List<TemplateInjection> parts = <TemplateInjection>[];
|
||||
List<SkeletonInjection> parts = <SkeletonInjection>[];
|
||||
SourceLine get start => input.isEmpty ? _lineProto : input.first;
|
||||
|
||||
String get template {
|
||||
|
@ -356,7 +356,7 @@ class _DirectiveInfo implements Comparable<_DirectiveInfo> {
|
||||
if (priority == other.priority) {
|
||||
return _compareUri(uri, other.uri);
|
||||
}
|
||||
return priority.ordinal - other.priority.ordinal;
|
||||
return priority.index - other.priority.index;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -387,23 +387,15 @@ class _DirectiveInfo implements Comparable<_DirectiveInfo> {
|
||||
}
|
||||
|
||||
enum _DirectivePriority {
|
||||
IMPORT_SDK('IMPORT_SDK', 0),
|
||||
IMPORT_PKG('IMPORT_PKG', 1),
|
||||
IMPORT_OTHER('IMPORT_OTHER', 2),
|
||||
IMPORT_REL('IMPORT_REL', 3),
|
||||
EXPORT_SDK('EXPORT_SDK', 4),
|
||||
EXPORT_PKG('EXPORT_PKG', 5),
|
||||
EXPORT_OTHER('EXPORT_OTHER', 6),
|
||||
EXPORT_REL('EXPORT_REL', 7),
|
||||
PART('PART', 8);
|
||||
|
||||
const _DirectivePriority(this.name, this.ordinal);
|
||||
|
||||
final String name;
|
||||
final int ordinal;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
IMPORT_SDK,
|
||||
IMPORT_PKG,
|
||||
IMPORT_OTHER,
|
||||
IMPORT_REL,
|
||||
EXPORT_SDK,
|
||||
EXPORT_PKG,
|
||||
EXPORT_OTHER,
|
||||
EXPORT_REL,
|
||||
PART
|
||||
}
|
||||
|
||||
/// SourceEdit
|
||||
|
@ -43,30 +43,9 @@ class SnippetGenerator {
|
||||
static DartFormatter formatter =
|
||||
DartFormatter(pageWidth: 80, fixes: StyleFix.all);
|
||||
|
||||
/// Gets the path to the template file requested.
|
||||
File? getTemplatePath(String templateName, {Directory? templatesDir}) {
|
||||
final Directory templateDir =
|
||||
templatesDir ?? configuration.templatesDirectory;
|
||||
final File templateFile = configuration.filesystem
|
||||
.file(path.join(templateDir.path, '$templateName.tmpl'));
|
||||
return templateFile.existsSync() ? templateFile : null;
|
||||
}
|
||||
|
||||
/// Returns an iterable over the template files available in the templates
|
||||
/// directory in the configuration.
|
||||
Iterable<File> getAvailableTemplates() sync* {
|
||||
final Directory templatesDir = configuration.templatesDirectory;
|
||||
for (final File file in templatesDir.listSync().whereType<File>()) {
|
||||
if (file.basename.endsWith('.tmpl')) {
|
||||
yield file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpolates the [injections] into an HTML skeleton file.
|
||||
///
|
||||
/// Similar to interpolateTemplate, but we are only looking for `code-`
|
||||
/// components, and we care about the order of the injections.
|
||||
/// The order of the injections is important.
|
||||
///
|
||||
/// Takes into account the [type] and doesn't substitute in the id and the app
|
||||
/// if not a [SnippetType.sample] snippet.
|
||||
@ -77,7 +56,7 @@ class SnippetGenerator {
|
||||
final List<String> codeParts = <String>[];
|
||||
const HtmlEscape htmlEscape = HtmlEscape();
|
||||
String? language;
|
||||
for (final TemplateInjection injection in sample.parts) {
|
||||
for (final SkeletonInjection injection in sample.parts) {
|
||||
if (!injection.name.startsWith('code')) {
|
||||
continue;
|
||||
}
|
||||
@ -165,7 +144,7 @@ class SnippetGenerator {
|
||||
/// into valid Dart code.
|
||||
List<SourceLine> _processBlocks(CodeSample sample) {
|
||||
final List<SourceLine> block = sample.parts
|
||||
.expand<SourceLine>((TemplateInjection injection) => injection.contents)
|
||||
.expand<SourceLine>((SkeletonInjection injection) => injection.contents)
|
||||
.toList();
|
||||
if (block.isEmpty) {
|
||||
return <SourceLine>[];
|
||||
@ -233,11 +212,11 @@ class SnippetGenerator {
|
||||
}
|
||||
|
||||
/// Parses the input for the various code and description segments, and
|
||||
/// returns a set of template injections in the order found.
|
||||
List<TemplateInjection> parseInput(CodeSample sample) {
|
||||
/// returns a set of skeleton injections in the order found.
|
||||
List<SkeletonInjection> parseInput(CodeSample sample) {
|
||||
bool inCodeBlock = false;
|
||||
final List<SourceLine> description = <SourceLine>[];
|
||||
final List<TemplateInjection> components = <TemplateInjection>[];
|
||||
final List<SkeletonInjection> components = <SkeletonInjection>[];
|
||||
String? language;
|
||||
final RegExp codeStartEnd =
|
||||
RegExp(r'^\s*```(?<language>[-\w]+|[-\w]+ (?<section>[-\w]+))?\s*$');
|
||||
@ -249,12 +228,12 @@ class SnippetGenerator {
|
||||
if (match.namedGroup('language') != null) {
|
||||
language = match[1];
|
||||
if (match.namedGroup('section') != null) {
|
||||
components.add(TemplateInjection(
|
||||
components.add(SkeletonInjection(
|
||||
'code-${match.namedGroup('section')}', <SourceLine>[],
|
||||
language: language!));
|
||||
} else {
|
||||
components.add(
|
||||
TemplateInjection('code', <SourceLine>[], language: language!));
|
||||
SkeletonInjection('code', <SourceLine>[], language: language!));
|
||||
}
|
||||
} else {
|
||||
language = null;
|
||||
@ -286,9 +265,9 @@ class SnippetGenerator {
|
||||
lastWasWhitespace = onlyWhitespace;
|
||||
}
|
||||
sample.description = descriptionLines.join('\n').trimRight();
|
||||
sample.parts = <TemplateInjection>[
|
||||
sample.parts = <SkeletonInjection>[
|
||||
if (sample is SnippetSample)
|
||||
TemplateInjection('#assumptions', sample.assumptions),
|
||||
SkeletonInjection('#assumptions', sample.assumptions),
|
||||
...components,
|
||||
];
|
||||
return sample.parts;
|
||||
@ -327,10 +306,6 @@ class SnippetGenerator {
|
||||
///
|
||||
/// The optional `output` is the file to write the generated sample code to.
|
||||
///
|
||||
/// If `addSectionMarkers` is true, then markers will be added before and
|
||||
/// after each template section in the output. This is intended to facilitate
|
||||
/// editing of the sample during the authoring process.
|
||||
///
|
||||
/// If `includeAssumptions` is true, then the block in the "Examples can
|
||||
/// assume:" block will also be included in the output.
|
||||
///
|
||||
@ -341,58 +316,16 @@ class SnippetGenerator {
|
||||
String? copyright,
|
||||
String? description,
|
||||
bool formatOutput = true,
|
||||
bool addSectionMarkers = false,
|
||||
bool includeAssumptions = false,
|
||||
}) {
|
||||
sample.metadata['copyright'] ??= copyright;
|
||||
final List<TemplateInjection> snippetData = parseInput(sample);
|
||||
final List<SkeletonInjection> snippetData = parseInput(sample);
|
||||
sample.description = description ?? sample.description;
|
||||
sample.metadata['description'] = _getDescription(sample);
|
||||
switch (sample.runtimeType) {
|
||||
switch (sample) {
|
||||
case DartpadSample _:
|
||||
case ApplicationSample _:
|
||||
String app;
|
||||
if (sample.sourceFile == null) {
|
||||
final String templateName = sample.template;
|
||||
if (templateName.isEmpty) {
|
||||
io.stderr
|
||||
.writeln('Non-linked samples must have a --template argument.');
|
||||
io.exit(1);
|
||||
}
|
||||
final Directory templatesDir = configuration.templatesDirectory;
|
||||
File? templateFile;
|
||||
templateFile =
|
||||
getTemplatePath(templateName, templatesDir: templatesDir);
|
||||
if (templateFile == null) {
|
||||
io.stderr.writeln(
|
||||
'The template $templateName was not found in the templates '
|
||||
'directory ${templatesDir.path}');
|
||||
io.exit(1);
|
||||
}
|
||||
final String templateContents = _loadFileAsUtf8(templateFile);
|
||||
final String templateRelativePath =
|
||||
templateFile.absolute.path.contains(flutterRoot.absolute.path)
|
||||
? path.relative(templateFile.absolute.path,
|
||||
from: flutterRoot.absolute.path)
|
||||
: templateFile.absolute.path;
|
||||
final String templateHeader = '''
|
||||
// Template: $templateRelativePath
|
||||
//
|
||||
// Comment lines marked with "▼▼▼" and "▲▲▲" are used for authoring
|
||||
// of samples, and may be ignored if you are just exploring the sample.
|
||||
''';
|
||||
app = interpolateTemplate(
|
||||
snippetData,
|
||||
addSectionMarkers
|
||||
? '$templateHeader\n$templateContents'
|
||||
: templateContents,
|
||||
sample.metadata,
|
||||
addSectionMarkers: addSectionMarkers,
|
||||
addCopyright: copyright != null,
|
||||
);
|
||||
} else {
|
||||
app = sample.sourceFileContents;
|
||||
}
|
||||
final String app = sample.sourceFileContents;
|
||||
sample.output = app;
|
||||
if (formatOutput) {
|
||||
final DartFormatter formatter =
|
||||
@ -421,13 +354,14 @@ class SnippetGenerator {
|
||||
metadataFile.writeAsStringSync(jsonEncoder.convert(metadata));
|
||||
}
|
||||
case SnippetSample _:
|
||||
if (sample is SnippetSample) {
|
||||
String app;
|
||||
if (sample.sourceFile == null) {
|
||||
String templateContents;
|
||||
if (includeAssumptions) {
|
||||
templateContents =
|
||||
'${headers.map<String>((SourceLine line) => line.text).join('\n')}\n{{#assumptions}}\n{{description}}\n{{code}}';
|
||||
'${headers.map<String>((SourceLine line) {
|
||||
return line.text;
|
||||
}).join('\n')}\n{{#assumptions}}\n{{description}}\n{{code}}';
|
||||
} else {
|
||||
templateContents = '{{description}}\n{{code}}';
|
||||
}
|
||||
@ -435,14 +369,12 @@ class SnippetGenerator {
|
||||
snippetData,
|
||||
templateContents,
|
||||
sample.metadata,
|
||||
addSectionMarkers: addSectionMarkers,
|
||||
addCopyright: copyright != null,
|
||||
);
|
||||
} else {
|
||||
app = sample.inputAsString;
|
||||
}
|
||||
sample.output = app;
|
||||
}
|
||||
}
|
||||
return sample.output;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ class SnippetDartdocParser {
|
||||
}
|
||||
parseComment(element);
|
||||
for (final CodeSample sample in element.samples) {
|
||||
switch (sample.runtimeType) {
|
||||
switch (sample) {
|
||||
case DartpadSample _:
|
||||
dartpadCount++;
|
||||
case ApplicationSample _:
|
||||
|
@ -140,28 +140,12 @@ class FlutterInformation {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a marker with section arrows surrounding the given string.
|
||||
///
|
||||
/// Specifying `start` as false returns an ending marker instead of a starting
|
||||
/// marker.
|
||||
String sectionArrows(String name, {bool start = true}) {
|
||||
const int markerArrows = 8;
|
||||
final String arrows =
|
||||
(start ? '\u25bc' /* ▼ */ : '\u25b2' /* ▲ */) * markerArrows;
|
||||
final String marker =
|
||||
'//* $arrows $name $arrows (do not modify or remove section marker)';
|
||||
return '${start ? '\n//*${'*' * marker.length}\n' : '\n'}'
|
||||
'$marker'
|
||||
'${!start ? '\n//*${'*' * marker.length}\n' : '\n'}';
|
||||
}
|
||||
|
||||
/// Injects the [injections] into the [template], while turning the
|
||||
/// "description" injection into a comment.
|
||||
String interpolateTemplate(
|
||||
List<TemplateInjection> injections,
|
||||
List<SkeletonInjection> injections,
|
||||
String template,
|
||||
Map<String, Object?> metadata, {
|
||||
bool addSectionMarkers = false,
|
||||
bool addCopyright = false,
|
||||
}) {
|
||||
String wrapSectionMarker(Iterable<String> contents, {required String name}) {
|
||||
@ -170,13 +154,8 @@ String interpolateTemplate(
|
||||
return '';
|
||||
}
|
||||
// We don't wrap some sections, because otherwise they generate invalid files.
|
||||
const Set<String> skippedSections = <String>{'element', 'copyright'};
|
||||
final bool addMarkers =
|
||||
addSectionMarkers && !skippedSections.contains(name);
|
||||
final String result = <String>[
|
||||
if (addMarkers) sectionArrows(name),
|
||||
...contents,
|
||||
if (addMarkers) sectionArrows(name, start: false),
|
||||
].join('\n');
|
||||
final RegExp wrappingNewlines = RegExp(r'^\n*(.*)\n*$', dotAll: true);
|
||||
return result.replaceAllMapped(
|
||||
@ -187,7 +166,7 @@ String interpolateTemplate(
|
||||
.replaceAllMapped(RegExp(r'{{([^}]+)}}'), (Match match) {
|
||||
final String name = match[1]!;
|
||||
final int componentIndex = injections
|
||||
.indexWhere((TemplateInjection injection) => injection.name == name);
|
||||
.indexWhere((SkeletonInjection injection) => injection.name == name);
|
||||
if (metadata[name] != null && componentIndex == -1) {
|
||||
// If the match isn't found in the injections, then just return the
|
||||
// metadata entry.
|
||||
|
@ -27,12 +27,6 @@ void main() {
|
||||
matches(RegExp(
|
||||
r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons')));
|
||||
});
|
||||
test('templates directory is correct', () async {
|
||||
expect(
|
||||
config.templatesDirectory.path,
|
||||
matches(RegExp(
|
||||
r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]templates')));
|
||||
});
|
||||
test('html skeleton file for sample is correct', () async {
|
||||
expect(
|
||||
config.getHtmlSkeletonFile('snippet').path,
|
||||
|
@ -37,7 +37,6 @@ void main() {
|
||||
late FlutterRepoSnippetConfiguration configuration;
|
||||
late SnippetGenerator generator;
|
||||
late Directory tmpDir;
|
||||
late File template;
|
||||
late Directory flutterRoot;
|
||||
|
||||
void writeSkeleton(String type) {
|
||||
@ -69,25 +68,13 @@ void main() {
|
||||
.directory(path.join(tmpDir.absolute.path, 'flutter'));
|
||||
configuration = FlutterRepoSnippetConfiguration(
|
||||
flutterRoot: flutterRoot, filesystem: memoryFileSystem);
|
||||
configuration.templatesDirectory.createSync(recursive: true);
|
||||
configuration.skeletonsDirectory.createSync(recursive: true);
|
||||
template = memoryFileSystem.file(
|
||||
path.join(configuration.templatesDirectory.path, 'template.tmpl'));
|
||||
template.writeAsStringSync('''
|
||||
// Flutter code sample for {{element}}
|
||||
|
||||
{{description}}
|
||||
|
||||
{{code-my-preamble}}
|
||||
|
||||
{{code}}
|
||||
''');
|
||||
<String>['dartpad', 'sample', 'snippet'].forEach(writeSkeleton);
|
||||
FlutterInformation.instance = FakeFlutterInformation(flutterRoot);
|
||||
generator = SnippetGenerator(
|
||||
configuration: configuration,
|
||||
filesystem: memoryFileSystem,
|
||||
flutterRoot: configuration.templatesDirectory.parent);
|
||||
flutterRoot: configuration.skeletonsDirectory.parent);
|
||||
});
|
||||
|
||||
test('parses from comments', () async {
|
||||
@ -124,35 +111,6 @@ void main() {
|
||||
}
|
||||
expect(sampleCount, equals(8));
|
||||
});
|
||||
test('parses dartpad samples from comments', () async {
|
||||
final File inputFile =
|
||||
_createDartpadSourceFile(tmpDir, memoryFileSystem, flutterRoot);
|
||||
final Iterable<SourceElement> elements = getFileElements(inputFile,
|
||||
resourceProvider: FileSystemResourceProvider(memoryFileSystem));
|
||||
expect(elements, isNotEmpty);
|
||||
final SnippetDartdocParser sampleParser =
|
||||
SnippetDartdocParser(memoryFileSystem);
|
||||
sampleParser.parseFromComments(elements);
|
||||
expect(elements.length, equals(1));
|
||||
int sampleCount = 0;
|
||||
for (final SourceElement element in elements) {
|
||||
expect(element.samples.length, greaterThanOrEqualTo(1));
|
||||
sampleCount += element.samples.length;
|
||||
final String code = generator.generateCode(element.samples.first);
|
||||
expect(code, contains('// Description'));
|
||||
expect(
|
||||
code,
|
||||
contains(RegExp('^void ${element.name}Sample\\(\\) \\{.*\$',
|
||||
multiLine: true)));
|
||||
final String html = generator.generateHtml(element.samples.first);
|
||||
expect(
|
||||
html,
|
||||
contains(RegExp(
|
||||
'''^<iframe class="snippet-dartpad" src="https://dartpad.dev/.*sample_id=${element.name}.0.*></iframe>.*\$''',
|
||||
multiLine: true)));
|
||||
}
|
||||
expect(sampleCount, equals(1));
|
||||
});
|
||||
test('parses dartpad samples from linked file', () async {
|
||||
final File inputFile = _createDartpadSourceFile(
|
||||
tmpDir, memoryFileSystem, flutterRoot,
|
||||
|
@ -41,7 +41,6 @@ void main() {
|
||||
late FlutterRepoSnippetConfiguration configuration;
|
||||
late SnippetGenerator generator;
|
||||
late Directory tmpDir;
|
||||
late File template;
|
||||
|
||||
void writeSkeleton(String type) {
|
||||
switch (type) {
|
||||
@ -72,26 +71,14 @@ void main() {
|
||||
flutterRoot: memoryFileSystem
|
||||
.directory(path.join(tmpDir.absolute.path, 'flutter')),
|
||||
filesystem: memoryFileSystem);
|
||||
configuration.templatesDirectory.createSync(recursive: true);
|
||||
configuration.skeletonsDirectory.createSync(recursive: true);
|
||||
template = memoryFileSystem.file(
|
||||
path.join(configuration.templatesDirectory.path, 'template.tmpl'));
|
||||
template.writeAsStringSync('''
|
||||
// Flutter code sample for {{element}}
|
||||
|
||||
{{description}}
|
||||
|
||||
{{code-my-preamble}}
|
||||
|
||||
{{code}}
|
||||
''');
|
||||
<String>['dartpad', 'sample', 'snippet'].forEach(writeSkeleton);
|
||||
FlutterInformation.instance =
|
||||
FakeFlutterInformation(configuration.flutterRoot);
|
||||
generator = SnippetGenerator(
|
||||
configuration: configuration,
|
||||
filesystem: memoryFileSystem,
|
||||
flutterRoot: configuration.templatesDirectory.parent);
|
||||
flutterRoot: configuration.skeletonsDirectory.parent);
|
||||
});
|
||||
|
||||
test('generates samples', () async {
|
||||
@ -99,20 +86,24 @@ void main() {
|
||||
.file(path.join(tmpDir.absolute.path, 'snippet_in.txt'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(r'''
|
||||
A description of the snippet.
|
||||
A description of the sample.
|
||||
|
||||
On several lines.
|
||||
|
||||
```my-dart_language my-preamble
|
||||
const String name = 'snippet';
|
||||
```
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
print('The actual $name.');
|
||||
}
|
||||
```
|
||||
** See code in examples/api/widgets/foo/foo_example.0.dart **
|
||||
''');
|
||||
final String examplePath = path.join(configuration.flutterRoot.path, 'examples/api/widgets/foo/foo_example.0.dart');
|
||||
memoryFileSystem.file(examplePath)
|
||||
..create(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
// Copyright
|
||||
|
||||
// Flutter code sample for [MyElement].
|
||||
|
||||
void main() {
|
||||
runApp(MaterialApp(title: 'foo'));
|
||||
}\n'''
|
||||
);
|
||||
final File outputFile = memoryFileSystem
|
||||
.file(path.join(tmpDir.absolute.path, 'snippet_out.txt'));
|
||||
final SnippetDartdocParser sampleParser =
|
||||
@ -135,27 +126,23 @@ void main() {
|
||||
element.samples.first,
|
||||
output: outputFile,
|
||||
);
|
||||
expect(code, contains('// Flutter code sample for MyElement'));
|
||||
expect(code, contains("runApp(MaterialApp(title: 'foo'));"));
|
||||
final String html = generator.generateHtml(
|
||||
element.samples.first,
|
||||
);
|
||||
expect(html, contains('<div>HTML Bits</div>'));
|
||||
expect(html, contains('<div>More HTML Bits</div>'));
|
||||
expect(html, contains(r'print('The actual $name.');'));
|
||||
expect(html, contains('A description of the snippet.\n'));
|
||||
expect(html, contains(r'''runApp(MaterialApp(title: 'foo'));'''));
|
||||
expect(html, isNot(contains('sample_channel=stable')));
|
||||
expect(
|
||||
html,
|
||||
contains('A description of the snippet.\n'
|
||||
contains('A description of the sample.\n'
|
||||
'\n'
|
||||
'On several lines.{@inject-html}</div>'));
|
||||
expect(html, contains('void main() {'));
|
||||
|
||||
final String outputContents = outputFile.readAsStringSync();
|
||||
expect(outputContents, contains('// Flutter code sample for MyElement'));
|
||||
expect(outputContents, contains('A description of the snippet.'));
|
||||
expect(outputContents, contains('void main() {'));
|
||||
expect(outputContents, contains("const String name = 'snippet';"));
|
||||
});
|
||||
|
||||
test('generates snippets', () async {
|
||||
@ -212,12 +199,20 @@ A description of the snippet.
|
||||
|
||||
On several lines.
|
||||
|
||||
```code
|
||||
void main() {
|
||||
print('The actual $name.');
|
||||
}
|
||||
```
|
||||
** See code in examples/api/widgets/foo/foo_example.0.dart **
|
||||
''');
|
||||
final String examplePath = path.join(configuration.flutterRoot.path, 'examples/api/widgets/foo/foo_example.0.dart');
|
||||
memoryFileSystem.file(examplePath)
|
||||
..create(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
// Copyright
|
||||
|
||||
// Flutter code sample for [MyElement].
|
||||
|
||||
void main() {
|
||||
runApp(MaterialApp(title: 'foo'));
|
||||
}\n'''
|
||||
);
|
||||
|
||||
final SnippetDartdocParser sampleParser =
|
||||
SnippetDartdocParser(memoryFileSystem);
|
||||
@ -235,7 +230,7 @@ void main() {
|
||||
'channel': 'stable',
|
||||
});
|
||||
final String code = generator.generateCode(element.samples.first);
|
||||
expect(code, contains('// Flutter code sample for MyElement'));
|
||||
expect(code, contains("runApp(MaterialApp(title: 'foo'));"));
|
||||
final String html = generator.generateHtml(element.samples.first);
|
||||
expect(html, contains('<div>HTML Bits (DartPad-style)</div>'));
|
||||
expect(html, contains('<div>More HTML Bits</div>'));
|
||||
@ -350,7 +345,7 @@ void main() {
|
||||
.file(tmpDir.childFile('input.snippet'))
|
||||
..writeAsString('/// Test file');
|
||||
snippets_main.main(
|
||||
<String>['--input=${input.absolute.path}', '--template=template']);
|
||||
<String>['--input=${input.absolute.path}']);
|
||||
|
||||
final Map<String, dynamic> metadata =
|
||||
mockSnippetGenerator.sample.metadata;
|
||||
@ -393,7 +388,7 @@ void main() {
|
||||
|
||||
input.deleteSync();
|
||||
snippets_main.main(
|
||||
<String>['--input=${input.absolute.path}', '--template=template']);
|
||||
<String>['--input=${input.absolute.path}']);
|
||||
expect(errorMessage,
|
||||
equals('The input file ${input.absolute.path} does not exist.'));
|
||||
errorMessage = '';
|
||||
|
Loading…
x
Reference in New Issue
Block a user