update analytics in generate api docs site to use new UA4 (#136497)
Change the analytics instance that the generated api.flutter.dev and master-api.flutter.dev use.
diff --git a/dev/docs/analytics-footer.html b/dev/docs/analytics-footer.html
new file mode 100644
index 0000000..133ecf3
--- /dev/null
+++ b/dev/docs/analytics-footer.html
@@ -0,0 +1,4 @@
+<!-- Google Tag Manager (noscript) -->
+<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-ND4LWWZ"
+height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
+<!-- End Google Tag Manager (noscript) -->
diff --git a/dev/docs/analytics-header.html b/dev/docs/analytics-header.html
new file mode 100644
index 0000000..bed0852
--- /dev/null
+++ b/dev/docs/analytics-header.html
@@ -0,0 +1,7 @@
+<!-- Google Tag Manager -->
+<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
+new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
+j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f)
+})(window,document,'script','dataLayer','GTM-ND4LWWZ');</script>
+<!-- End Google Tag Manager -->
diff --git a/dev/docs/analytics.html b/dev/docs/analytics.html
deleted file mode 100644
index ee11d34..0000000
--- a/dev/docs/analytics.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<script>
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
- })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
-
- ga('create', 'UA-67589403-1', 'auto');
- ga('send', 'pageview');
-</script>
diff --git a/dev/tools/create_api_docs.dart b/dev/tools/create_api_docs.dart
index 87c556e..56f6c32 100644
--- a/dev/tools/create_api_docs.dart
+++ b/dev/tools/create_api_docs.dart
@@ -518,9 +518,9 @@
final Version version = FlutterInformation.instance.getFlutterVersion();
// Verify which version of snippets and dartdoc we're using.
- final ProcessResult snippetsResult = Process.runSync(
- FlutterInformation.instance.getFlutterBinaryPath().path,
+ final ProcessResult snippetsResult = processManager.runSync(
<String>[
+ FlutterInformation.instance.getFlutterBinaryPath().path,
'pub',
'global',
'list',
@@ -574,13 +574,15 @@
'--header',
docsRoot.childFile('styles.html').path,
'--header',
- docsRoot.childFile('analytics.html').path,
+ docsRoot.childFile('analytics-header.html').path,
'--header',
docsRoot.childFile('survey.html').path,
'--header',
docsRoot.childFile('snippets.html').path,
'--header',
docsRoot.childFile('opensearch.html').path,
+ '--footer',
+ docsRoot.childFile('analytics-footer.html').path,
'--footer-text',
packageRoot.childFile('footer.html').path,
'--allow-warnings-in-packages',
@@ -643,6 +645,7 @@
arguments: dartdocArgs,
workingDirectory: packageRoot,
environment: pubEnvironment,
+ processManager: processManager,
));
printStream(
process.stdout,
@@ -669,7 +672,7 @@
}
_sanityCheckDocs();
- checkForUnresolvedDirectives(publishRoot.childDirectory('flutter').path);
+ checkForUnresolvedDirectives(publishRoot.childDirectory('flutter'));
_createIndexAndCleanup();
@@ -690,12 +693,13 @@
}
}
- /// Runs a sanity check by running a test.
- void _sanityCheckDocs([Platform platform = const LocalPlatform()]) {
+ /// A subset of all generated doc files for [_sanityCheckDocs].
+ @visibleForTesting
+ List<File> get canaries {
final Directory flutterDirectory = publishRoot.childDirectory('flutter');
final Directory widgetsDirectory = flutterDirectory.childDirectory('widgets');
- final List<File> canaries = <File>[
+ return <File>[
publishRoot.childDirectory('assets').childFile('overrides.css'),
flutterDirectory.childDirectory('dart-io').childFile('File-class.html'),
flutterDirectory.childDirectory('dart-ui').childFile('Canvas-class.html'),
@@ -710,12 +714,19 @@
widgetsDirectory.childFile('Widget-class.html'),
widgetsDirectory.childFile('Listener-class.html'),
];
+ }
+
+ /// Runs a sanity check by running a test.
+ void _sanityCheckDocs([Platform platform = const LocalPlatform()]) {
for (final File canary in canaries) {
if (!canary.existsSync()) {
throw Exception('Missing "${canary.path}", which probably means the documentation failed to build correctly.');
}
}
// Make sure at least one example of each kind includes source code.
+ final Directory widgetsDirectory = publishRoot
+ .childDirectory('flutter')
+ .childDirectory('widgets');
// Check a "sample" example, any one will do.
_sanityCheckExample(
diff --git a/dev/tools/dartdoc_checker.dart b/dev/tools/dartdoc_checker.dart
index 75fbd2b..cbc47f8 100644
--- a/dev/tools/dartdoc_checker.dart
+++ b/dev/tools/dartdoc_checker.dart
@@ -4,9 +4,31 @@
import 'dart:io';
+import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
-/// Scans the dartdoc HTML output in the provided `htmlOutputPath` for
+/// Makes sure that the path we were given contains some of the expected
+/// libraries.
+@visibleForTesting
+const List<String> dartdocDirectiveCanaryLibraries = <String>[
+ 'animation',
+ 'cupertino',
+ 'material',
+ 'widgets',
+ 'rendering',
+ 'flutter_driver',
+];
+
+/// Makes sure that the path we were given contains some of the expected
+/// HTML files.
+@visibleForTesting
+const List<String> dartdocDirectiveCanaryFiles = <String>[
+ 'Widget-class.html',
+ 'Material-class.html',
+ 'Canvas-class.html',
+];
+
+/// Scans the dartdoc HTML output in the provided `dartDocDir` for
/// unresolved dartdoc directives (`{@foo x y}`).
///
/// Dartdoc usually replaces those directives with other content. However,
@@ -22,27 +44,14 @@
/// ```
/// void foo({@required int bar});
/// ```
-void checkForUnresolvedDirectives(String htmlOutputPath) {
- final Directory dartDocDir = Directory(htmlOutputPath);
+void checkForUnresolvedDirectives(Directory dartDocDir) {
if (!dartDocDir.existsSync()) {
throw Exception('Directory with dartdoc output (${dartDocDir.path}) does not exist.');
}
- // Makes sure that the path we were given contains some of the expected
- // libraries and HTML files.
- final List<String> canaryLibraries = <String>[
- 'animation',
- 'cupertino',
- 'material',
- 'widgets',
- 'rendering',
- 'flutter_driver',
- ];
- final List<String> canaryFiles = <String>[
- 'Widget-class.html',
- 'Material-class.html',
- 'Canvas-class.html',
- ];
+ // Make a copy since this will be mutated
+ final List<String> canaryLibraries = dartdocDirectiveCanaryLibraries.toList();
+ final List<String> canaryFiles = dartdocDirectiveCanaryFiles.toList();
print('Scanning for unresolved dartdoc directives...');
@@ -112,5 +121,5 @@
if (!Directory(args.single).existsSync()) {
throw Exception('The dartdoc HTML output directory ${args.single} does not exist.');
}
- checkForUnresolvedDirectives(args.single);
+ checkForUnresolvedDirectives(Directory(args.single));
}
diff --git a/dev/tools/test/create_api_docs_test.dart b/dev/tools/test/create_api_docs_test.dart
index 6d6b1e8..811885e 100644
--- a/dev/tools/test/create_api_docs_test.dart
+++ b/dev/tools/test/create_api_docs_test.dart
@@ -10,6 +10,7 @@
import '../../../packages/flutter_tools/test/src/fake_process_manager.dart';
import '../create_api_docs.dart' as apidocs;
+import '../dartdoc_checker.dart';
void main() {
group('FlutterInformation', () {
@@ -223,6 +224,235 @@
expect(info['engineRealm'], equals('realm'));
});
});
+
+ group('DartDocGenerator', () {
+ late apidocs.DartdocGenerator generator;
+ late MemoryFileSystem fs;
+ late FakeProcessManager processManager;
+ late Directory publishRoot;
+
+ setUp(() {
+ fs = MemoryFileSystem.test();
+ publishRoot = fs.directory('/path/to/publish');
+ processManager = FakeProcessManager.empty();
+ generator = apidocs.DartdocGenerator(
+ packageRoot: fs.directory('/path/to/package'),
+ publishRoot: publishRoot,
+ docsRoot: fs.directory('/path/to/docs'),
+ filesystem: fs,
+ processManager: processManager,
+ );
+ final Directory repoRoot = fs.directory('/flutter');
+ repoRoot.childDirectory('packages').createSync(recursive: true);
+ apidocs.FlutterInformation.instance = apidocs.FlutterInformation(
+ filesystem: fs,
+ processManager: processManager,
+ platform: FakePlatform(environment: <String, String>{
+ 'FLUTTER_ROOT': repoRoot.path,
+ }),
+ );
+ });
+
+ test('.generateDartDoc() invokes dartdoc with the correct command line arguments', () async {
+ processManager.addCommands(<FakeCommand>[
+ const FakeCommand(command: <String>['/flutter/bin/flutter', 'pub', 'get']),
+ const FakeCommand(
+ command: <String>['/flutter/bin/flutter', '--version', '--machine'],
+ stdout: testVersionInfo,
+ ),
+ const FakeCommand(
+ command: <Pattern>['git', 'status', '-b', '--porcelain'],
+ stdout: '## $branchName',
+ ),
+ const FakeCommand(
+ command: <String>['git', 'rev-parse', 'HEAD'],
+ ),
+ const FakeCommand(
+ command: <String>['/flutter/bin/flutter', 'pub', 'global', 'list'],
+ ),
+ FakeCommand(
+ command: <Pattern>[
+ '/flutter/bin/flutter',
+ 'pub',
+ 'global',
+ 'run',
+ '--enable-asserts',
+ 'dartdoc',
+ '--output',
+ '/path/to/publish/flutter',
+ '--allow-tools',
+ '--json',
+ '--validate-links',
+ '--link-to-source-excludes',
+ '/flutter/bin/cache',
+ '--link-to-source-root',
+ '/flutter',
+ '--link-to-source-uri-template',
+ 'https://github.com/flutter/flutter/blob/master/%f%#L%l%',
+ '--inject-html',
+ '--use-base-href',
+ '--header',
+ '/path/to/docs/styles.html',
+ '--header',
+ '/path/to/docs/analytics-header.html',
+ '--header',
+ '/path/to/docs/survey.html',
+ '--header',
+ '/path/to/docs/snippets.html',
+ '--header',
+ '/path/to/docs/opensearch.html',
+ '--footer',
+ '/path/to/docs/analytics-footer.html',
+ '--footer-text',
+ '/path/to/package/footer.html',
+ '--allow-warnings-in-packages',
+ // match package names
+ RegExp(r'^(\w+,)+(\w+)$'),
+ '--exclude-packages',
+ RegExp(r'^(\w+,)+(\w+)$'),
+ '--exclude',
+ // match dart package URIs
+ RegExp(r'^([\w\/:.]+,)+([\w\/:.]+)$'),
+ '--favicon',
+ '/path/to/docs/favicon.ico',
+ '--package-order',
+ 'flutter,Dart,${apidocs.kPlatformIntegrationPackageName},flutter_test,flutter_driver',
+ '--auto-include-dependencies',
+ ],
+ ),
+ ]);
+
+ // This will throw while sanity checking generated files, which is tested independently
+ await expectLater(
+ () => generator.generateDartdoc(),
+ throwsA(
+ isA<Exception>().having(
+ (Exception e) => e.toString(),
+ 'message',
+ contains(RegExp(r'Missing .* which probably means the documentation failed to build correctly.')),
+ ),
+ ),
+ );
+
+ expect(processManager, hasNoRemainingExpectations);
+ });
+
+ test('sanity checks spot check generated files', () async {
+ processManager.addCommands(<FakeCommand>[
+ const FakeCommand(command: <String>['/flutter/bin/flutter', 'pub', 'get']),
+ const FakeCommand(
+ command: <String>['/flutter/bin/flutter', '--version', '--machine'],
+ stdout: testVersionInfo,
+ ),
+ const FakeCommand(
+ command: <Pattern>['git', 'status', '-b', '--porcelain'],
+ stdout: '## $branchName',
+ ),
+ const FakeCommand(
+ command: <String>['git', 'rev-parse', 'HEAD'],
+ ),
+ const FakeCommand(
+ command: <String>['/flutter/bin/flutter', 'pub', 'global', 'list'],
+ ),
+ FakeCommand(
+ command: <Pattern>[
+ '/flutter/bin/flutter',
+ 'pub',
+ 'global',
+ 'run',
+ '--enable-asserts',
+ 'dartdoc',
+ '--output',
+ '/path/to/publish/flutter',
+ '--allow-tools',
+ '--json',
+ '--validate-links',
+ '--link-to-source-excludes',
+ '/flutter/bin/cache',
+ '--link-to-source-root',
+ '/flutter',
+ '--link-to-source-uri-template',
+ 'https://github.com/flutter/flutter/blob/master/%f%#L%l%',
+ '--inject-html',
+ '--use-base-href',
+ '--header',
+ '/path/to/docs/styles.html',
+ '--header',
+ '/path/to/docs/analytics-header.html',
+ '--header',
+ '/path/to/docs/survey.html',
+ '--header',
+ '/path/to/docs/snippets.html',
+ '--header',
+ '/path/to/docs/opensearch.html',
+ '--footer',
+ '/path/to/docs/analytics-footer.html',
+ '--footer-text',
+ '/path/to/package/footer.html',
+ '--allow-warnings-in-packages',
+ // match package names
+ RegExp(r'^(\w+,)+(\w+)$'),
+ '--exclude-packages',
+ RegExp(r'^(\w+,)+(\w+)$'),
+ '--exclude',
+ // match dart package URIs
+ RegExp(r'^([\w\/:.]+,)+([\w\/:.]+)$'),
+ '--favicon',
+ '/path/to/docs/favicon.ico',
+ '--package-order',
+ 'flutter,Dart,${apidocs.kPlatformIntegrationPackageName},flutter_test,flutter_driver',
+ '--auto-include-dependencies',
+ ],
+ onRun: () {
+ for (final File canary in generator.canaries) {
+ canary.createSync(recursive: true);
+ }
+ for (final String path in dartdocDirectiveCanaryFiles) {
+ publishRoot.childDirectory('flutter').childFile(path).createSync(recursive: true);
+ }
+ for (final String path in dartdocDirectiveCanaryLibraries) {
+ publishRoot.childDirectory('flutter').childDirectory(path).createSync(recursive: true);
+ }
+ publishRoot.childDirectory('flutter').childFile('index.html').createSync();
+
+ final Directory widgetsDir = publishRoot
+ .childDirectory('flutter')
+ .childDirectory('widgets')
+ ..createSync(recursive: true);
+ widgetsDir.childFile('showGeneralDialog.html').writeAsStringSync('''
+<pre id="longSnippet1">
+ <code class="language-dart">
+ import 'package:flutter/material.dart';
+ </code>
+</pre>
+''',
+ );
+ expect(publishRoot.childDirectory('flutter').existsSync(), isTrue);
+ (widgetsDir
+ .childDirectory('ModalRoute')
+ ..createSync(recursive: true))
+ .childFile('barrierColor.html')
+ .writeAsStringSync('''
+<pre id="sample-code">
+ <code class="language-dart">
+ class FooClass {
+ Color get barrierColor => FooColor();
+ }
+ </code>
+</pre>
+''');
+ const String queryParams = 'split=1&run=true&sample_id=widgets.Listener.123&sample_channel=master&channel=master';
+ widgetsDir.childFile('Listener-class.html').writeAsStringSync('''
+<iframe class="snippet-dartpad" src="https://dartpad.dev/embed-flutter.html?$queryParams">
+</iframe>
+''');
+ }
+ ),
+ ]);
+
+ await generator.generateDartdoc();
+ });
+ });
}
const String branchName = 'stable';