Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
32aa3128
Unverified
Commit
32aa3128
authored
Aug 15, 2023
by
Michael Goderbauer
Committed by
GitHub
Aug 15, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Analyze code snippets in flutter_driver docs (#132337)
parent
33b66f64
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
144 additions
and
102 deletions
+144
-102
analyze_snippet_code.dart
dev/bots/analyze_snippet_code.dart
+35
-14
custom_imports_broken.dart
...nalyze-snippet-code-test-input/custom_imports_broken.dart
+21
-0
analyze_snippet_code_test.dart
dev/bots/test/analyze_snippet_code_test.dart
+3
-2
driver.dart
packages/flutter_driver/lib/src/driver/driver.dart
+7
-2
gc_summarizer.dart
packages/flutter_driver/lib/src/driver/gc_summarizer.dart
+1
-1
profiling_summarizer.dart
...s/flutter_driver/lib/src/driver/profiling_summarizer.dart
+1
-1
raster_cache_summarizer.dart
...lutter_driver/lib/src/driver/raster_cache_summarizer.dart
+1
-1
scene_display_lag_summarizer.dart
...r_driver/lib/src/driver/scene_display_lag_summarizer.dart
+1
-1
extension.dart
packages/flutter_driver/lib/src/extension/extension.dart
+74
-80
No files found.
dev/bots/analyze_snippet_code.dart
View file @
32aa3128
...
@@ -55,10 +55,12 @@
...
@@ -55,10 +55,12 @@
//
//
// At the top of a file you can say `// Examples can assume:` and then list some
// At the top of a file you can say `// Examples can assume:` and then list some
// commented-out declarations that will be included in the analysis for snippets
// commented-out declarations that will be included in the analysis for snippets
// in that file.
// in that file.
This section may also contain explicit import statements.
//
//
// Snippets generally import all the main Flutter packages (including material
// For files without an `// Examples can assume:` section or if that section
// and flutter_test), as well as most core Dart packages with the usual prefixes.
// contains no explicit imports, the snippets will implicitly import all the
// main Flutter packages (including material and flutter_test), as well as most
// core Dart packages with the usual prefixes.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
;
...
@@ -72,6 +74,7 @@ import 'package:watcher/watcher.dart';
...
@@ -72,6 +74,7 @@ import 'package:watcher/watcher.dart';
final
String
_flutterRoot
=
path
.
dirname
(
path
.
dirname
(
path
.
dirname
(
path
.
fromUri
(
Platform
.
script
))));
final
String
_flutterRoot
=
path
.
dirname
(
path
.
dirname
(
path
.
dirname
(
path
.
fromUri
(
Platform
.
script
))));
final
String
_packageFlutter
=
path
.
join
(
_flutterRoot
,
'packages'
,
'flutter'
,
'lib'
);
final
String
_packageFlutter
=
path
.
join
(
_flutterRoot
,
'packages'
,
'flutter'
,
'lib'
);
final
String
_packageFlutterTest
=
path
.
join
(
_flutterRoot
,
'packages'
,
'flutter_test'
,
'lib'
);
final
String
_packageFlutterTest
=
path
.
join
(
_flutterRoot
,
'packages'
,
'flutter_test'
,
'lib'
);
final
String
_packageFlutterDriver
=
path
.
join
(
_flutterRoot
,
'packages'
,
'flutter_driver'
,
'lib'
);
final
String
_packageIntegrationTest
=
path
.
join
(
_flutterRoot
,
'packages'
,
'integration_test'
,
'lib'
);
final
String
_packageIntegrationTest
=
path
.
join
(
_flutterRoot
,
'packages'
,
'integration_test'
,
'lib'
);
final
String
_defaultDartUiLocation
=
path
.
join
(
_flutterRoot
,
'bin'
,
'cache'
,
'pkg'
,
'sky_engine'
,
'lib'
,
'ui'
);
final
String
_defaultDartUiLocation
=
path
.
join
(
_flutterRoot
,
'bin'
,
'cache'
,
'pkg'
,
'sky_engine'
,
'lib'
,
'ui'
);
final
String
_flutter
=
path
.
join
(
_flutterRoot
,
'bin'
,
Platform
.
isWindows
?
'flutter.bat'
:
'flutter'
);
final
String
_flutter
=
path
.
join
(
_flutterRoot
,
'bin'
,
Platform
.
isWindows
?
'flutter.bat'
:
'flutter'
);
...
@@ -153,7 +156,8 @@ Future<void> main(List<String> arguments) async {
...
@@ -153,7 +156,8 @@ Future<void> main(List<String> arguments) async {
Directory
(
_packageFlutter
),
Directory
(
_packageFlutter
),
Directory
(
_packageFlutterTest
),
Directory
(
_packageFlutterTest
),
Directory
(
_packageIntegrationTest
),
Directory
(
_packageIntegrationTest
),
// TODO(goderbauer): Add all other packages.
Directory
(
_packageFlutterDriver
),
// TODO(goderbauer): Add all other packages for which we publish docs.
];
];
}
}
...
@@ -574,6 +578,7 @@ class _SnippetChecker {
...
@@ -574,6 +578,7 @@ class _SnippetChecker {
final
List
<
String
>
fileLines
=
file
.
readAsLinesSync
();
final
List
<
String
>
fileLines
=
file
.
readAsLinesSync
();
final
List
<
_Line
>
ignorePreambleLinesOnly
=
<
_Line
>[];
final
List
<
_Line
>
ignorePreambleLinesOnly
=
<
_Line
>[];
final
List
<
_Line
>
preambleLines
=
<
_Line
>[];
final
List
<
_Line
>
preambleLines
=
<
_Line
>[];
final
List
<
_Line
>
customImports
=
<
_Line
>[];
bool
inExamplesCanAssumePreamble
=
false
;
// Whether or not we're in the file-wide preamble section ("Examples can assume").
bool
inExamplesCanAssumePreamble
=
false
;
// Whether or not we're in the file-wide preamble section ("Examples can assume").
bool
inToolSection
=
false
;
// Whether or not we're in a code snippet
bool
inToolSection
=
false
;
// Whether or not we're in a code snippet
bool
inDartSection
=
false
;
// Whether or not we're in a '```dart' segment.
bool
inDartSection
=
false
;
// Whether or not we're in a '```dart' segment.
...
@@ -592,7 +597,11 @@ class _SnippetChecker {
...
@@ -592,7 +597,11 @@ class _SnippetChecker {
throw
_SnippetCheckerException
(
'Unexpected content in snippet code preamble.'
,
file:
relativeFilePath
,
line:
lineNumber
);
throw
_SnippetCheckerException
(
'Unexpected content in snippet code preamble.'
,
file:
relativeFilePath
,
line:
lineNumber
);
}
else
{
}
else
{
final
_Line
newLine
=
_Line
(
line:
lineNumber
,
indent:
3
,
code:
line
.
substring
(
3
));
final
_Line
newLine
=
_Line
(
line:
lineNumber
,
indent:
3
,
code:
line
.
substring
(
3
));
preambleLines
.
add
(
newLine
);
if
(
newLine
.
code
.
startsWith
(
'import '
))
{
customImports
.
add
(
newLine
);
}
else
{
preambleLines
.
add
(
newLine
);
}
if
(
line
.
startsWith
(
'// // ignore_for_file: '
))
{
if
(
line
.
startsWith
(
'// // ignore_for_file: '
))
{
ignorePreambleLinesOnly
.
add
(
newLine
);
ignorePreambleLinesOnly
.
add
(
newLine
);
}
}
...
@@ -612,7 +621,7 @@ class _SnippetChecker {
...
@@ -612,7 +621,7 @@ class _SnippetChecker {
}
}
if
(
trimmedLine
.
startsWith
(
_codeBlockEndRegex
))
{
if
(
trimmedLine
.
startsWith
(
_codeBlockEndRegex
))
{
inDartSection
=
false
;
inDartSection
=
false
;
final
_SnippetFile
snippet
=
_processBlock
(
startLine
,
block
,
preambleLines
,
ignorePreambleLinesOnly
,
relativeFilePath
,
lastExample
);
final
_SnippetFile
snippet
=
_processBlock
(
startLine
,
block
,
preambleLines
,
ignorePreambleLinesOnly
,
relativeFilePath
,
lastExample
,
customImports
);
final
String
path
=
_writeSnippetFile
(
snippet
).
path
;
final
String
path
=
_writeSnippetFile
(
snippet
).
path
;
assert
(!
snippetMap
.
containsKey
(
path
));
assert
(!
snippetMap
.
containsKey
(
path
));
snippetMap
[
path
]
=
snippet
;
snippetMap
[
path
]
=
snippet
;
...
@@ -655,6 +664,7 @@ class _SnippetChecker {
...
@@ -655,6 +664,7 @@ class _SnippetChecker {
line
.
contains
(
'```kotlin'
)
||
line
.
contains
(
'```kotlin'
)
||
line
.
contains
(
'```swift'
)
||
line
.
contains
(
'```swift'
)
||
line
.
contains
(
'```glsl'
)
||
line
.
contains
(
'```glsl'
)
||
line
.
contains
(
'```json'
)
||
line
.
contains
(
'```csv'
))
{
line
.
contains
(
'```csv'
))
{
inOtherBlock
=
true
;
inOtherBlock
=
true
;
}
else
if
(
line
.
startsWith
(
_uncheckedCodeBlockStartRegex
))
{
}
else
if
(
line
.
startsWith
(
_uncheckedCodeBlockStartRegex
))
{
...
@@ -694,7 +704,7 @@ class _SnippetChecker {
...
@@ -694,7 +704,7 @@ class _SnippetChecker {
/// a primitive heuristic to make snippet blocks into valid Dart code.
/// a primitive heuristic to make snippet blocks into valid Dart code.
///
///
/// `block` argument will get mutated, but is copied before this function returns.
/// `block` argument will get mutated, but is copied before this function returns.
_SnippetFile
_processBlock
(
_Line
startingLine
,
List
<
String
>
block
,
List
<
_Line
>
assumptions
,
List
<
_Line
>
ignoreAssumptionsOnly
,
String
filename
,
_SnippetFile
?
lastExample
)
{
_SnippetFile
_processBlock
(
_Line
startingLine
,
List
<
String
>
block
,
List
<
_Line
>
assumptions
,
List
<
_Line
>
ignoreAssumptionsOnly
,
String
filename
,
_SnippetFile
?
lastExample
,
List
<
_Line
>
customImports
)
{
if
(
block
.
isEmpty
)
{
if
(
block
.
isEmpty
)
{
throw
_SnippetCheckerException
(
'
${startingLine.asLocation(filename, 0)}
: Empty ```dart block in snippet code.'
);
throw
_SnippetCheckerException
(
'
${startingLine.asLocation(filename, 0)}
: Empty ```dart block in snippet code.'
);
}
}
...
@@ -755,7 +765,7 @@ class _SnippetChecker {
...
@@ -755,7 +765,7 @@ class _SnippetChecker {
return
_SnippetFile
.
fromStrings
(
return
_SnippetFile
.
fromStrings
(
startingLine
,
startingLine
,
block
.
toList
(),
block
.
toList
(),
importPreviousExample
?
<
_Line
>[]
:
headersWithoutImports
,
headersWithoutImports
,
<
_Line
>[
<
_Line
>[
...
ignoreAssumptionsOnly
,
...
ignoreAssumptionsOnly
,
if
(
hasEllipsis
)
if
(
hasEllipsis
)
...
@@ -764,13 +774,24 @@ class _SnippetChecker {
...
@@ -764,13 +774,24 @@ class _SnippetChecker {
'self-contained program'
,
'self-contained program'
,
filename
,
filename
,
);
);
}
else
if
(
hasStatefulWidgetComment
)
{
}
final
List
<
_Line
>
headers
=
switch
((
importPreviousExample
,
customImports
.
length
))
{
(
true
,
_
)
=>
<
_Line
>[],
(
false
,
0
)
=>
headersWithImports
,
(
false
,
_
)
=>
<
_Line
>[
...
headersWithoutImports
,
const
_Line
.
generated
(
code:
'// ignore_for_file: unused_import'
),
...
customImports
,
]
};
if
(
hasStatefulWidgetComment
)
{
return
_SnippetFile
.
fromStrings
(
return
_SnippetFile
.
fromStrings
(
startingLine
,
startingLine
,
prefix:
'class _State extends State<StatefulWidget> {'
,
prefix:
'class _State extends State<StatefulWidget> {'
,
block
.
toList
(),
block
.
toList
(),
postfix:
'}'
,
postfix:
'}'
,
importPreviousExample
?
<
_Line
>[]
:
headersWithImport
s
,
header
s
,
preamble
,
preamble
,
'stateful widget'
,
'stateful widget'
,
filename
,
filename
,
...
@@ -782,7 +803,7 @@ class _SnippetChecker {
...
@@ -782,7 +803,7 @@ class _SnippetChecker {
return
_SnippetFile
.
fromStrings
(
return
_SnippetFile
.
fromStrings
(
startingLine
,
startingLine
,
block
.
toList
(),
block
.
toList
(),
importPreviousExample
?
<
_Line
>[]
:
headersWithImport
s
,
header
s
,
preamble
,
preamble
,
'top-level declaration'
,
'top-level declaration'
,
filename
,
filename
,
...
@@ -795,7 +816,7 @@ class _SnippetChecker {
...
@@ -795,7 +816,7 @@ class _SnippetChecker {
prefix:
'Future<void> function() async {'
,
prefix:
'Future<void> function() async {'
,
block
.
toList
(),
block
.
toList
(),
postfix:
'}'
,
postfix:
'}'
,
importPreviousExample
?
<
_Line
>[]
:
headersWithImport
s
,
header
s
,
preamble
,
preamble
,
'statement'
,
'statement'
,
filename
,
filename
,
...
@@ -807,7 +828,7 @@ class _SnippetChecker {
...
@@ -807,7 +828,7 @@ class _SnippetChecker {
prefix:
'class Class {'
,
prefix:
'class Class {'
,
block
.
toList
(),
block
.
toList
(),
postfix:
'}'
,
postfix:
'}'
,
importPreviousExample
?
<
_Line
>[]
:
headersWithImport
s
,
header
s
,
<
_Line
>[
<
_Line
>[
...
preamble
,
...
preamble
,
const
_Line
.
generated
(
code:
'// ignore_for_file: avoid_classes_with_only_static_members'
),
const
_Line
.
generated
(
code:
'// ignore_for_file: avoid_classes_with_only_static_members'
),
...
@@ -849,7 +870,7 @@ class _SnippetChecker {
...
@@ -849,7 +870,7 @@ class _SnippetChecker {
prefix:
'dynamic expression = '
,
prefix:
'dynamic expression = '
,
block
.
toList
(),
block
.
toList
(),
postfix:
';'
,
postfix:
';'
,
importPreviousExample
?
<
_Line
>[]
:
headersWithImport
s
,
header
s
,
preamble
,
preamble
,
'expression'
,
'expression'
,
filename
,
filename
,
...
...
dev/bots/test/analyze-snippet-code-test-input/custom_imports_broken.dart
0 → 100644
View file @
32aa3128
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is used by ../analyze_snippet_code_test.dart, which depends on the
// precise contents (including especially the comments) of this file.
// Examples can assume:
// import 'package:flutter/rendering.dart';
/// no error: rendering library was imported.
/// ```dart
/// print(RenderObject);
/// ```
String
?
bar
;
/// error: widgets library was not imported (not even implicitly).
/// ```dart
/// print(Widget);
/// ```
String
?
foo
;
dev/bots/test/analyze_snippet_code_test.dart
View file @
32aa3128
...
@@ -11,6 +11,7 @@ import 'dart:io';
...
@@ -11,6 +11,7 @@ import 'dart:io';
import
'common.dart'
;
import
'common.dart'
;
const
List
<
String
>
expectedMainErrors
=
<
String
>[
const
List
<
String
>
expectedMainErrors
=
<
String
>[
'dev/bots/test/analyze-snippet-code-test-input/custom_imports_broken.dart:19:11: (statement) (undefined_identifier)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:30:5: (expression) (unnecessary_new)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:30:5: (expression) (unnecessary_new)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:103:5: (statement) (always_specify_types)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:103:5: (statement) (always_specify_types)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:111:5: (top-level declaration) (prefer_const_declarations)'
,
'dev/bots/test/analyze-snippet-code-test-input/known_broken_documentation.dart:111:5: (top-level declaration) (prefer_const_declarations)'
,
...
@@ -68,7 +69,7 @@ void main() {
...
@@ -68,7 +69,7 @@ void main() {
final
List
<
String
>
stderrNoDescriptions
=
stderrLines
.
map
(
removeLintDescriptions
).
toList
();
final
List
<
String
>
stderrNoDescriptions
=
stderrLines
.
map
(
removeLintDescriptions
).
toList
();
expect
(
stderrNoDescriptions
,
<
String
>[
expect
(
stderrNoDescriptions
,
<
String
>[
...
expectedMainErrors
,
...
expectedMainErrors
,
'Found 1
5
snippet code errors.'
,
'Found 1
6
snippet code errors.'
,
'See the documentation at the top of dev/bots/analyze_snippet_code.dart for details.'
,
'See the documentation at the top of dev/bots/analyze_snippet_code.dart for details.'
,
''
,
// because we end with a newline, split gives us an extra blank line
''
,
// because we end with a newline, split gives us an extra blank line
]);
]);
...
@@ -92,7 +93,7 @@ void main() {
...
@@ -92,7 +93,7 @@ void main() {
expect
(
stderrNoDescriptions
,
<
String
>[
expect
(
stderrNoDescriptions
,
<
String
>[
...
expectedUiErrors
,
...
expectedUiErrors
,
...
expectedMainErrors
,
...
expectedMainErrors
,
'Found
19
snippet code errors.'
,
'Found
20
snippet code errors.'
,
'See the documentation at the top of dev/bots/analyze_snippet_code.dart for details.'
,
'See the documentation at the top of dev/bots/analyze_snippet_code.dart for details.'
,
''
,
// because we end with a newline, split gives us an extra blank line
''
,
// because we end with a newline, split gives us an extra blank line
]);
]);
...
...
packages/flutter_driver/lib/src/driver/driver.dart
View file @
32aa3128
...
@@ -83,6 +83,11 @@ const CommonFinders find = CommonFinders._();
...
@@ -83,6 +83,11 @@ const CommonFinders find = CommonFinders._();
/// See also [FlutterDriver.waitFor].
/// See also [FlutterDriver.waitFor].
typedef
EvaluatorFunction
=
dynamic
Function
();
typedef
EvaluatorFunction
=
dynamic
Function
();
// Examples can assume:
// import 'package:flutter_driver/flutter_driver.dart';
// import 'package:test/test.dart';
// late FlutterDriver driver;
/// Drives a Flutter Application running in another process.
/// Drives a Flutter Application running in another process.
abstract
class
FlutterDriver
{
abstract
class
FlutterDriver
{
/// Default constructor.
/// Default constructor.
...
@@ -478,7 +483,7 @@ abstract class FlutterDriver {
...
@@ -478,7 +483,7 @@ abstract class FlutterDriver {
///
///
/// ```dart
/// ```dart
/// test('enters text in a text field', () async {
/// test('enters text in a text field', () async {
///
va
r textField = find.byValueKey('enter-text-field');
///
final SerializableFinde
r textField = find.byValueKey('enter-text-field');
/// await driver.tap(textField); // acquire focus
/// await driver.tap(textField); // acquire focus
/// await driver.enterText('Hello!'); // enter text
/// await driver.enterText('Hello!'); // enter text
/// await driver.waitFor(find.text('Hello!')); // verify text appears on UI
/// await driver.waitFor(find.text('Hello!')); // verify text appears on UI
...
@@ -520,7 +525,7 @@ abstract class FlutterDriver {
...
@@ -520,7 +525,7 @@ abstract class FlutterDriver {
///
///
/// ```dart
/// ```dart
/// test('submit text in a text field', () async {
/// test('submit text in a text field', () async {
///
va
r textField = find.byValueKey('enter-text-field');
///
final SerializableFinde
r textField = find.byValueKey('enter-text-field');
/// await driver.tap(textField); // acquire focus
/// await driver.tap(textField); // acquire focus
/// await driver.enterText('Hello!'); // enter text
/// await driver.enterText('Hello!'); // enter text
/// await driver.waitFor(find.text('Hello!')); // verify text appears on UI
/// await driver.waitFor(find.text('Hello!')); // verify text appears on UI
...
...
packages/flutter_driver/lib/src/driver/gc_summarizer.dart
View file @
32aa3128
...
@@ -17,7 +17,7 @@ const Set<String> kGCRootEvents = <String>{
...
@@ -17,7 +17,7 @@ const Set<String> kGCRootEvents = <String>{
/// Summarizes [TimelineEvents]s corresponding to [kGCRootEvents] category.
/// Summarizes [TimelineEvents]s corresponding to [kGCRootEvents] category.
///
///
/// A sample event (some fields have been omitted for brevity):
/// A sample event (some fields have been omitted for brevity):
/// ```
/// ```
json
/// {
/// {
/// "name": "StartConcurrentMarking",
/// "name": "StartConcurrentMarking",
/// "cat": "GC",
/// "cat": "GC",
...
...
packages/flutter_driver/lib/src/driver/profiling_summarizer.dart
View file @
32aa3128
...
@@ -36,7 +36,7 @@ enum ProfileType {
...
@@ -36,7 +36,7 @@ enum ProfileType {
/// Summarizes [TimelineEvents]s corresponding to [kProfilingEvents] category.
/// Summarizes [TimelineEvents]s corresponding to [kProfilingEvents] category.
///
///
/// A sample event (some fields have been omitted for brevity):
/// A sample event (some fields have been omitted for brevity):
/// ```
/// ```
json
/// {
/// {
/// "category": "embedder",
/// "category": "embedder",
/// "name": "CpuUsage",
/// "name": "CpuUsage",
...
...
packages/flutter_driver/lib/src/driver/raster_cache_summarizer.dart
View file @
32aa3128
...
@@ -16,7 +16,7 @@ const String _kPictureMemory = 'PictureMBytes';
...
@@ -16,7 +16,7 @@ const String _kPictureMemory = 'PictureMBytes';
/// Summarizes [TimelineEvents]s corresponding to [kRasterCacheEvent] events.
/// Summarizes [TimelineEvents]s corresponding to [kRasterCacheEvent] events.
///
///
/// A sample event (some fields have been omitted for brevity):
/// A sample event (some fields have been omitted for brevity):
/// ```
/// ```
json
/// {
/// {
/// "name": "RasterCache",
/// "name": "RasterCache",
/// "ts": 75598996256,
/// "ts": 75598996256,
...
...
packages/flutter_driver/lib/src/driver/scene_display_lag_summarizer.dart
View file @
32aa3128
...
@@ -13,7 +13,7 @@ const String _kVsyncTransitionsMissed = 'vsync_transitions_missed';
...
@@ -13,7 +13,7 @@ const String _kVsyncTransitionsMissed = 'vsync_transitions_missed';
/// Summarizes [TimelineEvents]s corresponding to [kSceneDisplayLagEvent] events.
/// Summarizes [TimelineEvents]s corresponding to [kSceneDisplayLagEvent] events.
///
///
/// A sample event (some fields have been omitted for brevity):
/// A sample event (some fields have been omitted for brevity):
/// ```
/// ```
json
/// {
/// {
/// "name": "SceneDisplayLag",
/// "name": "SceneDisplayLag",
/// "ts": 408920509340,
/// "ts": 408920509340,
...
...
packages/flutter_driver/lib/src/extension/extension.dart
View file @
32aa3128
...
@@ -58,6 +58,19 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
...
@@ -58,6 +58,19 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
}
}
}
}
// Examples can assume:
// import 'package:flutter_driver/flutter_driver.dart';
// import 'package:flutter/widgets.dart';
// import 'package:flutter_driver/driver_extension.dart';
// import 'package:flutter_test/flutter_test.dart' hide find;
// import 'package:flutter_test/flutter_test.dart' as flutter_test;
// typedef MyHomeWidget = Placeholder;
// abstract class SomeWidget extends StatelessWidget { const SomeWidget({super.key, required this.title}); final String title; }
// late FlutterDriver driver;
// abstract class StubNestedCommand { int get times; SerializableFinder get finder; }
// class StubCommandResult extends Result { const StubCommandResult(this.arg); final String arg; @override Map<String, dynamic> toJson() => <String, dynamic>{}; }
// abstract class StubProberCommand { int get times; SerializableFinder get finder; }
/// Enables Flutter Driver VM service extension.
/// Enables Flutter Driver VM service extension.
///
///
/// This extension is required for tests that use `package:flutter_driver` to
/// This extension is required for tests that use `package:flutter_driver` to
...
@@ -87,26 +100,40 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
...
@@ -87,26 +100,40 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
/// The `finders` and `commands` parameters are optional and used to add custom
/// The `finders` and `commands` parameters are optional and used to add custom
/// finders or commands, as in the following example.
/// finders or commands, as in the following example.
///
///
/// ```dart
main
/// ```dart
/// void main() {
/// void main() {
/// enableFlutterDriverExtension(
/// enableFlutterDriverExtension(
/// finders: <FinderExtension>[ SomeFinderExtension() ],
/// finders: <FinderExtension>[ SomeFinderExtension() ],
/// commands: <CommandExtension>[ SomeCommandExtension() ],
/// commands: <CommandExtension>[ SomeCommandExtension() ],
/// );
/// );
///
///
///
app.main(
);
///
runApp(const MyHomeWidget()
);
/// }
/// }
/// ```
///
///
///
```dart
///
class SomeFinderExtension extends FinderExtension {
///
driver.sendCommand(SomeCommand(ByValueKey('Button'), 7));
///
@override
///
```
///
String get finderType => 'SomeFinder';
///
///
/// `SomeFinder` and `SomeFinderExtension` must be placed in different files
/// @override
/// to avoid `dart:ui` import issue. Imports relative to `dart:ui` can't be
/// SerializableFinder deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory) {
/// accessed from host runner, where flutter runtime is not accessible.
/// return SomeFinder(params['title']!);
/// }
///
///
/// ```dart
/// @override
/// Finder createFinder(SerializableFinder finder, CreateFinderFactory finderFactory) {
/// final SomeFinder someFinder = finder as SomeFinder;
///
/// return flutter_test.find.byElementPredicate((Element element) {
/// final Widget widget = element.widget;
/// if (widget is SomeWidget) {
/// return widget.title == someFinder.title;
/// }
/// return false;
/// });
/// }
/// }
///
/// // Use this class in a test anywhere where a SerializableFinder is expected.
/// class SomeFinder extends SerializableFinder {
/// class SomeFinder extends SerializableFinder {
/// const SomeFinder(this.title);
/// const SomeFinder(this.title);
///
///
...
@@ -120,43 +147,51 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
...
@@ -120,43 +147,51 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
/// 'title': title,
/// 'title': title,
/// });
/// });
/// }
/// }
/// ```
///
///
/// ```dart
/// class SomeCommandExtension extends CommandExtension {
/// class SomeFinderExtension extends FinderExtension {
/// @override
/// String get commandKind => 'SomeCommand';
///
///
/// String get finderType => 'SomeFinder';
/// @override
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// final SomeCommand someCommand = command as SomeCommand;
///
///
/// SerializableFinder deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory) {
/// // Deserialize [Finder]:
/// return SomeFinder(json['title']);
/// final Finder finder = finderFactory.createFinder(someCommand.finder);
/// }
///
///
///
Finder createFinder(SerializableFinder finder, CreateFinderFactory finderFactory) {
///
// Wait for [Element]:
///
Some someFinder = finder as SomeFinder
;
///
handlerFactory.waitForElement(finder)
;
///
///
/// return find.byElementPredicate((Element element) {
/// // Alternatively, wait for [Element] absence:
/// final Widget widget = element.widget;
/// handlerFactory.waitForAbsentElement(finder);
/// if (element.widget is SomeWidget) {
/// return element.widget.title == someFinder.title;
/// }
/// return false;
/// });
/// }
/// }
/// ```
///
///
/// `SomeCommand`, `SomeResult` and `SomeCommandExtension` must be placed in
/// // Submit known [Command]s:
/// different files to avoid `dart:ui` import issue. Imports relative to `dart:ui`
/// for (int i = 0; i < someCommand.times; i++) {
/// can't be accessed from host runner, where flutter runtime is not accessible.
/// await handlerFactory.handleCommand(Tap(someCommand.finder), prober, finderFactory);
/// }
///
///
/// ```dart
/// // Alternatively, use [WidgetController]:
/// for (int i = 0; i < someCommand.times; i++) {
/// await prober.tap(finder);
/// }
///
/// return const SomeCommandResult('foo bar');
/// }
///
/// @override
/// Command deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory, DeserializeCommandFactory commandFactory) {
/// return SomeCommand.deserialize(params, finderFactory);
/// }
/// }
///
/// // Pass an instance of this class to `FlutterDriver.sendCommand` to invoke
/// // the custom command during a test.
/// class SomeCommand extends CommandWithTarget {
/// class SomeCommand extends CommandWithTarget {
/// SomeCommand(SerializableFinder finder, this.times, {Duration? timeout})
/// SomeCommand(super.finder, this.times, {super.timeout});
/// : super(finder, timeout: timeout);
///
///
/// SomeCommand.deserialize(
Map<String, String> json, DeserializeFinderFactory
finderFactory)
/// SomeCommand.deserialize(
super.json, super.
finderFactory)
/// : times = int.parse(json['times']!),
/// : times = int.parse(json['times']!),
/// super.deserialize(
json, finderFactory
);
/// super.deserialize();
///
///
/// @override
/// @override
/// Map<String, String> serialize() {
/// Map<String, String> serialize() {
...
@@ -168,9 +203,7 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
...
@@ -168,9 +203,7 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
///
///
/// final int times;
/// final int times;
/// }
/// }
/// ```
///
///
/// ```dart
/// class SomeCommandResult extends Result {
/// class SomeCommandResult extends Result {
/// const SomeCommandResult(this.resultParam);
/// const SomeCommandResult(this.resultParam);
///
///
...
@@ -184,45 +217,6 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
...
@@ -184,45 +217,6 @@ class _DriverBinding extends BindingBase with SchedulerBinding, ServicesBinding,
/// }
/// }
/// }
/// }
/// ```
/// ```
///
/// ```dart
/// class SomeCommandExtension extends CommandExtension {
/// @override
/// String get commandKind => 'SomeCommand';
///
/// @override
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// final SomeCommand someCommand = command as SomeCommand;
///
/// // Deserialize [Finder]:
/// final Finder finder = finderFactory.createFinder(stubCommand.finder);
///
/// // Wait for [Element]:
/// handlerFactory.waitForElement(finder);
///
/// // Alternatively, wait for [Element] absence:
/// handlerFactory.waitForAbsentElement(finder);
///
/// // Submit known [Command]s:
/// for (int index = 0; i < someCommand.times; index++) {
/// await handlerFactory.handleCommand(Tap(someCommand.finder), prober, finderFactory);
/// }
///
/// // Alternatively, use [WidgetController]:
/// for (int index = 0; i < stubCommand.times; index++) {
/// await prober.tap(finder);
/// }
///
/// return const SomeCommandResult('foo bar');
/// }
///
/// @override
/// Command deserialize(Map<String, String> params, DeserializeFinderFactory finderFactory, DeserializeCommandFactory commandFactory) {
/// return SomeCommand.deserialize(params, finderFactory);
/// }
/// }
/// ```
///
void
enableFlutterDriverExtension
(
{
DataHandler
?
handler
,
bool
silenceErrors
=
false
,
bool
enableTextEntryEmulation
=
true
,
List
<
FinderExtension
>?
finders
,
List
<
CommandExtension
>?
commands
})
{
void
enableFlutterDriverExtension
(
{
DataHandler
?
handler
,
bool
silenceErrors
=
false
,
bool
enableTextEntryEmulation
=
true
,
List
<
FinderExtension
>?
finders
,
List
<
CommandExtension
>?
commands
})
{
_DriverBinding
(
handler
,
silenceErrors
,
enableTextEntryEmulation
,
finders
??
<
FinderExtension
>[],
commands
??
<
CommandExtension
>[]);
_DriverBinding
(
handler
,
silenceErrors
,
enableTextEntryEmulation
,
finders
??
<
FinderExtension
>[],
commands
??
<
CommandExtension
>[]);
assert
(
WidgetsBinding
.
instance
is
_DriverBinding
);
assert
(
WidgetsBinding
.
instance
is
_DriverBinding
);
...
@@ -287,7 +281,7 @@ abstract class CommandExtension {
...
@@ -287,7 +281,7 @@ abstract class CommandExtension {
/// @override
/// @override
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// final StubNestedCommand stubCommand = command as StubNestedCommand;
/// final StubNestedCommand stubCommand = command as StubNestedCommand;
/// for (int i
ndex = 0; i < stubCommand.times; index
++) {
/// for (int i
= 0; i < stubCommand.times; i
++) {
/// await handlerFactory.handleCommand(Tap(stubCommand.finder), prober, finderFactory);
/// await handlerFactory.handleCommand(Tap(stubCommand.finder), prober, finderFactory);
/// }
/// }
/// return const StubCommandResult('stub response');
/// return const StubCommandResult('stub response');
...
@@ -300,7 +294,7 @@ abstract class CommandExtension {
...
@@ -300,7 +294,7 @@ abstract class CommandExtension {
/// @override
/// @override
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// Future<Result> call(Command command, WidgetController prober, CreateFinderFactory finderFactory, CommandHandlerFactory handlerFactory) async {
/// final StubProberCommand stubCommand = command as StubProberCommand;
/// final StubProberCommand stubCommand = command as StubProberCommand;
/// for (int i
ndex = 0; i < stubCommand.times; index
++) {
/// for (int i
= 0; i < stubCommand.times; i
++) {
/// await prober.tap(finderFactory.createFinder(stubCommand.finder));
/// await prober.tap(finderFactory.createFinder(stubCommand.finder));
/// }
/// }
/// return const StubCommandResult('stub response');
/// return const StubCommandResult('stub response');
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment