Skip to content

Commit f740dfc

Browse files
authored
Preserve newline following {@endtemplate}. (#2289)
Preserve newline following `{@endtemplate}`. Additionally introduce (some of?) the first unit tests. These live in test/unit/ for now. This includes a new dev dependency on mockito. Fixes #1848
1 parent 3e2dd15 commit f740dfc

7 files changed

+220
-8
lines changed

lib/src/model/comment_processable.dart

+6-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import 'package:dartdoc/src/warnings.dart';
1111
import 'package:path/path.dart' as path;
1212

1313
final _templatePattern = RegExp(
14-
r'[ ]*{@template\s+(.+?)}([\s\S]+?){@endtemplate}[ ]*\n?',
14+
r'[ ]*{@template\s+(.+?)}([\s\S]+?){@endtemplate}[ ]*(\n?)',
1515
multiLine: true);
1616
final _htmlPattern = RegExp(
1717
r'[ ]*{@inject-html\s*}([\s\S]+?){@end-inject-html}[ ]*\n?',
@@ -366,9 +366,6 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
366366
/// The width and height must be integers specifying the dimensions of the
367367
/// video file in pixels.
368368
String _injectAnimations(String rawDocs) {
369-
// Make sure we have a set to keep track of used IDs for this href.
370-
package.usedAnimationIdsByHref[href] ??= {};
371-
372369
String getUniqueId(String base) {
373370
var animationIdCount = 1;
374371
var id = '$base$animationIdCount';
@@ -382,6 +379,9 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
382379
}
383380

384381
return rawDocs.replaceAllMapped(_basicAnimationPattern, (basicMatch) {
382+
// Make sure we have a set to keep track of used IDs for this href.
383+
package.usedAnimationIdsByHref[href] ??= {};
384+
385385
var parser = ArgParser();
386386
parser.addOption('id');
387387
var args = _parseArgs(basicMatch[1], parser, 'animation');
@@ -481,8 +481,9 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
481481
return rawDocs.replaceAllMapped(_templatePattern, (match) {
482482
var name = match[1].trim();
483483
var content = match[2].trim();
484+
var trailingNewline = match[3];
484485
packageGraph.addMacro(name, content);
485-
return '{@macro $name}';
486+
return '{@macro $name}$trailingNewline';
486487
});
487488
}
488489

lib/src/model/model.dart

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export 'canonicalization.dart';
77
export 'categorization.dart';
88
export 'category.dart';
99
export 'class.dart';
10+
export 'comment_processable.dart';
1011
export 'constructor.dart';
1112
export 'container.dart';
1213
export 'container_member.dart';

pubspec.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dev_dependencies:
4040
grinder: ^0.8.2
4141
io: ^0.3.0
4242
http: ^0.12.0
43+
mockito: ^4.1.1
4344
pedantic: ^1.9.0
4445
test: ^1.3.0
4546

test/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Tests
2+
3+
Most of dartdoc's tests are large end-to-end tests which read real files in
4+
real packages, in the `testing/` directory. Unit tests exist in `test/unit/`.
5+
6+
Many of the end-to-end test cases should be rewritten as unit tests.
7+
8+
At some point, the distinction should flip, such that unit tests are generally
9+
located in `test/`, and end-to-end tests are found in a specific directory, or
10+
in files whose names signify the distinction.

test/model_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ void main() {
719719

720720
test("renders a macro within the same comment where it's defined", () {
721721
expect(withMacro.documentation,
722-
equals('Macro method\n\nFoo macro content\nMore docs'));
722+
equals('Macro method\n\nFoo macro content\n\nMore docs'));
723723
});
724724

725725
test("renders a macro in another method, not the same where it's defined",

test/src/utils.dart

+6-2
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,12 @@ class SubprocessLauncher {
384384

385385
var exitCode = await process.exitCode;
386386
if (exitCode != 0) {
387-
throw ProcessException(executable, arguments,
388-
'SubprocessLauncher got non-zero exitCode: $exitCode', exitCode);
387+
throw ProcessException(
388+
executable,
389+
arguments,
390+
'SubprocessLauncher got non-zero exitCode: $exitCode\n\n'
391+
'stdout: ${process.stdout}\n\nstderr: ${process.stderr}',
392+
exitCode);
389393
}
390394
return jsonObjects;
391395
}
+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io' show Directory;
6+
7+
import 'package:dartdoc/src/dartdoc_options.dart';
8+
import 'package:dartdoc/src/model/model.dart';
9+
import 'package:dartdoc/src/package_meta.dart';
10+
import 'package:dartdoc/src/warnings.dart';
11+
import 'package:mockito/mockito.dart';
12+
import 'package:test/test.dart';
13+
14+
void main() {
15+
_Processor processor;
16+
setUp(() {
17+
processor = _Processor(_FakeDartdocOptionContext());
18+
processor.href = '/project/a.dart';
19+
});
20+
21+
test('removes triple slashes', () async {
22+
var doc = await processor.processComment('''
23+
/// Text.
24+
/// More text.
25+
''');
26+
expect(doc, equals('''
27+
Text.
28+
More text.'''));
29+
});
30+
31+
test('removes space after triple slashes', () async {
32+
var doc = await processor.processComment('''
33+
/// Text.
34+
/// More text.
35+
''');
36+
// TODO(srawlins): Actually, the three spaces before 'More' is perhaps not
37+
// the best fit. Should it only be two, to match the indent from the first
38+
// line's "Text"?
39+
expect(doc, equals('''
40+
Text.
41+
More text.'''));
42+
});
43+
44+
test('leaves blank lines', () async {
45+
var doc = await processor.processComment('''
46+
/// Text.
47+
///
48+
/// More text.
49+
''');
50+
expect(doc, equals('''
51+
Text.
52+
53+
More text.'''));
54+
});
55+
56+
test('processes @template', () async {
57+
var doc = await processor.processComment('''
58+
/// Text.
59+
///
60+
/// {@template abc}
61+
/// Template text.
62+
/// {@endtemplate}
63+
///
64+
/// End text.
65+
''');
66+
expect(doc, equals('''
67+
Text.
68+
69+
{@macro abc}
70+
71+
End text.'''));
72+
verify(processor.packageGraph.addMacro('abc', 'Template text.')).called(1);
73+
});
74+
75+
test('processes leading @template', () async {
76+
var doc = await processor.processComment('''
77+
/// {@template abc}
78+
/// Template text.
79+
/// {@endtemplate}
80+
///
81+
/// End text.
82+
''');
83+
expect(doc, equals('''
84+
{@macro abc}
85+
86+
End text.'''));
87+
verify(processor.packageGraph.addMacro('abc', 'Template text.')).called(1);
88+
});
89+
90+
test('processes trailing @template', () async {
91+
var doc = await processor.processComment('''
92+
/// Text.
93+
///
94+
/// {@template abc}
95+
/// Template text.
96+
/// {@endtemplate}
97+
''');
98+
expect(doc, equals('''
99+
Text.
100+
101+
{@macro abc}'''));
102+
verify(processor.packageGraph.addMacro('abc', 'Template text.')).called(1);
103+
});
104+
105+
test('processes @template w/o blank line following', () async {
106+
var doc = await processor.processComment('''
107+
/// Text.
108+
///
109+
/// {@template abc}
110+
/// Template text.
111+
/// {@endtemplate}
112+
/// End text.
113+
''');
114+
expect(doc, equals('''
115+
Text.
116+
117+
{@macro abc}
118+
End text.'''));
119+
verify(processor.packageGraph.addMacro('abc', 'Template text.')).called(1);
120+
});
121+
122+
test('allows whitespace around @template name', () async {
123+
var doc = await processor.processComment('''
124+
/// {@template abc }
125+
/// Template text.
126+
/// {@endtemplate}
127+
''');
128+
expect(doc, equals('''
129+
{@macro abc}'''));
130+
verify(processor.packageGraph.addMacro('abc', 'Template text.')).called(1);
131+
});
132+
133+
// TODO(srawlins): More unit tests: @example, @youtube, @animation,
134+
// @inject-html, @tool.
135+
}
136+
137+
/// In order to mix in [CommentProcessable], we must first implement
138+
/// the super-class constraints.
139+
abstract class __Processor extends Fake
140+
implements Documentable, Warnable, Locatable, SourceCodeMixin {}
141+
142+
/// A simple comment processor for testing [CommentProcessable].
143+
class _Processor extends __Processor with CommentProcessable {
144+
@override
145+
final _FakeDartdocOptionContext config;
146+
147+
@override
148+
final _FakePackage package;
149+
150+
@override
151+
final _MockPackageGraph packageGraph;
152+
153+
@override
154+
String href;
155+
156+
_Processor(this.config)
157+
: package = _FakePackage(),
158+
packageGraph = _MockPackageGraph() {
159+
throwOnMissingStub(packageGraph);
160+
when(packageGraph.addMacro(any, any)).thenReturn(null);
161+
}
162+
}
163+
164+
class _FakeDirectory extends Fake implements Directory {
165+
@override
166+
final String path;
167+
168+
_FakeDirectory() : path = '/project';
169+
}
170+
171+
class _FakePackage extends Fake implements Package {
172+
@override
173+
final PackageMeta packageMeta;
174+
175+
_FakePackage() : packageMeta = _FakePackageMeta();
176+
}
177+
178+
class _FakePackageMeta extends Fake implements PackageMeta {
179+
@override
180+
final Directory dir;
181+
182+
_FakePackageMeta() : dir = _FakeDirectory();
183+
}
184+
185+
class _FakeDartdocOptionContext extends Fake implements DartdocOptionContext {
186+
@override
187+
final bool allowTools;
188+
189+
@override
190+
final bool injectHtml;
191+
192+
_FakeDartdocOptionContext({this.allowTools = false, this.injectHtml = false});
193+
}
194+
195+
class _MockPackageGraph extends Mock implements PackageGraph {}

0 commit comments

Comments
 (0)