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
4373a319
Unverified
Commit
4373a319
authored
Sep 18, 2019
by
Michael Goderbauer
Committed by
GitHub
Sep 18, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reapply "Revert "Propagate textfield character limits to semantics (#40468)" (#40767)
parent
f19f040a
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
458 additions
and
25 deletions
+458
-25
main_test.dart
...ests/android_semantics_testing/test_driver/main_test.dart
+10
-2
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+21
-10
custom_paint.dart
packages/flutter/lib/src/rendering/custom_paint.dart
+6
-0
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+32
-0
semantics.dart
packages/flutter/lib/src/semantics/semantics.dart
+132
-2
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+8
-0
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+62
-0
semantics_test.dart
packages/flutter/test/semantics/semantics_test.dart
+4
-0
semantics_debugger_test.dart
packages/flutter/test/widgets/semantics_debugger_test.dart
+13
-2
semantics_tester.dart
packages/flutter/test/widgets/semantics_tester.dart
+31
-1
find.dart
packages/flutter_driver/lib/src/common/find.dart
+12
-0
driver.dart
packages/flutter_driver/lib/src/driver/driver.dart
+10
-2
extension.dart
packages/flutter_driver/lib/src/extension/extension.dart
+4
-2
extension_test.dart
packages/flutter_driver/test/src/extension_test.dart
+81
-0
find_test.dart
packages/flutter_driver/test/src/find_test.dart
+12
-4
matchers.dart
packages/flutter_test/lib/src/matchers.dart
+16
-0
matchers_test.dart
packages/flutter_test/test/matchers_test.dart
+4
-0
No files found.
dev/integration_tests/android_semantics_testing/test_driver/main_test.dart
View file @
4373a319
...
...
@@ -64,7 +64,11 @@ void main() {
});
test
(
'TextField has correct Android semantics'
,
()
async
{
final
SerializableFinder
normalTextField
=
find
.
byValueKey
(
normalTextFieldKeyValue
);
final
SerializableFinder
normalTextField
=
find
.
descendant
(
of:
find
.
byValueKey
(
normalTextFieldKeyValue
),
matching:
find
.
byType
(
'Semantics'
),
firstMatchOnly:
true
,
);
expect
(
await
getSemantics
(
normalTextField
),
hasAndroidSemantics
(
className:
AndroidClassName
.
editText
,
isEditable:
true
,
...
...
@@ -112,7 +116,11 @@ void main() {
});
test
(
'password TextField has correct Android semantics'
,
()
async
{
final
SerializableFinder
passwordTextField
=
find
.
byValueKey
(
passwordTextFieldKeyValue
);
final
SerializableFinder
passwordTextField
=
find
.
descendant
(
of:
find
.
byValueKey
(
passwordTextFieldKeyValue
),
matching:
find
.
byType
(
'Semantics'
),
firstMatchOnly:
true
,
);
expect
(
await
getSemantics
(
passwordTextField
),
hasAndroidSemantics
(
className:
AndroidClassName
.
editText
,
isEditable:
true
,
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
4373a319
...
...
@@ -753,6 +753,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
bool
get
_isEnabled
=>
widget
.
enabled
??
widget
.
decoration
?.
enabled
??
true
;
int
get
_currentLength
=>
_effectiveController
.
value
.
text
.
runes
.
length
;
InputDecoration
_getEffectiveDecoration
()
{
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
...
...
@@ -769,7 +771,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
// If buildCounter was provided, use it to generate a counter widget.
Widget
counter
;
final
int
currentLength
=
_
effectiveController
.
value
.
text
.
runes
.
l
ength
;
final
int
currentLength
=
_
currentL
ength
;
if
(
effectiveDecoration
.
counter
==
null
&&
effectiveDecoration
.
counterText
==
null
&&
widget
.
buildCounter
!=
null
)
{
...
...
@@ -1099,18 +1101,27 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
child:
child
,
);
}
return
Semantics
(
onTap:
()
{
if
(!
_effectiveController
.
selection
.
isValid
)
_effectiveController
.
selection
=
TextSelection
.
collapsed
(
offset:
_effectiveController
.
text
.
length
);
_requestKeyboard
();
},
return
IgnorePointer
(
ignoring:
!
_isEnabled
,
child:
MouseRegion
(
onEnter:
_handleMouseEnter
,
onExit:
_handleMouseExit
,
child:
IgnorePointer
(
ignoring:
!
_isEnabled
,
child:
AnimatedBuilder
(
animation:
controller
,
// changes the _currentLength
builder:
(
BuildContext
context
,
Widget
child
)
{
return
Semantics
(
maxValueLength:
widget
.
maxLengthEnforced
&&
widget
.
maxLength
!=
null
&&
widget
.
maxLength
>
0
?
widget
.
maxLength
:
null
,
currentValueLength:
_currentLength
,
onTap:
()
{
if
(!
_effectiveController
.
selection
.
isValid
)
_effectiveController
.
selection
=
TextSelection
.
collapsed
(
offset:
_effectiveController
.
text
.
length
);
_requestKeyboard
();
},
child:
child
,
);
},
child:
_selectionGestureDetectorBuilder
.
buildGestureDetector
(
behavior:
HitTestBehavior
.
translucent
,
child:
child
,
...
...
packages/flutter/lib/src/rendering/custom_paint.dart
View file @
4373a319
...
...
@@ -864,6 +864,12 @@ class RenderCustomPaint extends RenderProxyBox {
if
(
properties
.
liveRegion
!=
null
)
{
config
.
liveRegion
=
properties
.
liveRegion
;
}
if
(
properties
.
maxValueLength
!=
null
)
{
config
.
maxValueLength
=
properties
.
maxValueLength
;
}
if
(
properties
.
currentValueLength
!=
null
)
{
config
.
currentValueLength
=
properties
.
currentValueLength
;
}
if
(
properties
.
toggled
!=
null
)
{
config
.
isToggled
=
properties
.
toggled
;
}
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
4373a319
...
...
@@ -3497,6 +3497,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
bool
hidden
,
bool
image
,
bool
liveRegion
,
int
maxValueLength
,
int
currentValueLength
,
String
label
,
String
value
,
String
increasedValue
,
...
...
@@ -3544,6 +3546,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_scopesRoute
=
scopesRoute
,
_namesRoute
=
namesRoute
,
_liveRegion
=
liveRegion
,
_maxValueLength
=
maxValueLength
,
_currentValueLength
=
currentValueLength
,
_hidden
=
hidden
,
_image
=
image
,
_onDismiss
=
onDismiss
,
...
...
@@ -3799,6 +3803,28 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate
();
}
/// If non-null, sets the [SemanticsNode.maxValueLength] semantic to the given
/// value.
int
get
maxValueLength
=>
_maxValueLength
;
int
_maxValueLength
;
set
maxValueLength
(
int
value
)
{
if
(
_maxValueLength
==
value
)
return
;
_maxValueLength
=
value
;
markNeedsSemanticsUpdate
();
}
/// If non-null, sets the [SemanticsNode.currentValueLength] semantic to the
/// given value.
int
get
currentValueLength
=>
_currentValueLength
;
int
_currentValueLength
;
set
currentValueLength
(
int
value
)
{
if
(
_currentValueLength
==
value
)
return
;
_currentValueLength
=
value
;
markNeedsSemanticsUpdate
();
}
/// If non-null, sets the [SemanticsNode.isToggled] semantic to the given
/// value.
bool
get
toggled
=>
_toggled
;
...
...
@@ -4370,6 +4396,12 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config
.
namesRoute
=
namesRoute
;
if
(
liveRegion
!=
null
)
config
.
liveRegion
=
liveRegion
;
if
(
maxValueLength
!=
null
)
{
config
.
maxValueLength
=
maxValueLength
;
}
if
(
currentValueLength
!=
null
)
{
config
.
currentValueLength
=
currentValueLength
;
}
if
(
textDirection
!=
null
)
config
.
textDirection
=
textDirection
;
if
(
sortKey
!=
null
)
...
...
packages/flutter/lib/src/semantics/semantics.dart
View file @
4373a319
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/basic.dart
View file @
4373a319
...
...
@@ -6196,6 +6196,8 @@ class Semantics extends SingleChildRenderObjectWidget {
bool
hidden
,
bool
image
,
bool
liveRegion
,
int
maxValueLength
,
int
currentValueLength
,
String
label
,
String
value
,
String
increasedValue
,
...
...
@@ -6247,6 +6249,8 @@ class Semantics extends SingleChildRenderObjectWidget {
hidden:
hidden
,
image:
image
,
liveRegion:
liveRegion
,
maxValueLength:
maxValueLength
,
currentValueLength:
currentValueLength
,
label:
label
,
value:
value
,
increasedValue:
increasedValue
,
...
...
@@ -6350,6 +6354,8 @@ class Semantics extends SingleChildRenderObjectWidget {
readOnly:
properties
.
readOnly
,
focused:
properties
.
focused
,
liveRegion:
properties
.
liveRegion
,
maxValueLength:
properties
.
maxValueLength
,
currentValueLength:
properties
.
currentValueLength
,
inMutuallyExclusiveGroup:
properties
.
inMutuallyExclusiveGroup
,
obscured:
properties
.
obscured
,
multiline:
properties
.
multiline
,
...
...
@@ -6422,6 +6428,8 @@ class Semantics extends SingleChildRenderObjectWidget {
..
hidden
=
properties
.
hidden
..
image
=
properties
.
image
..
liveRegion
=
properties
.
liveRegion
..
maxValueLength
=
properties
.
maxValueLength
..
currentValueLength
=
properties
.
currentValueLength
..
label
=
properties
.
label
..
value
=
properties
.
value
..
increasedValue
=
properties
.
increasedValue
...
...
packages/flutter/test/material/text_field_test.dart
View file @
4373a319
...
...
@@ -3367,6 +3367,68 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'Disabled text field does not have tap action'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(
maxLength:
10
,
enabled:
false
,
),
),
),
),
);
expect
(
semantics
,
isNot
(
includesNodeWith
(
actions:
<
SemanticsAction
>[
SemanticsAction
.
tap
])));
semantics
.
dispose
();
});
testWidgets
(
'currentValueLength/maxValueLength are in the tree'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(
controller:
controller
,
maxLength:
10
,
),
),
),
),
);
expect
(
semantics
,
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
],
maxValueLength:
10
,
currentValueLength:
0
,
));
await
tester
.
showKeyboard
(
find
.
byType
(
TextField
));
const
String
testValue
=
'123'
;
tester
.
testTextInput
.
updateEditingValue
(
const
TextEditingValue
(
text:
testValue
,
selection:
TextSelection
.
collapsed
(
offset:
3
),
composing:
TextRange
(
start:
0
,
end:
testValue
.
length
),
));
await
tester
.
pump
();
expect
(
semantics
,
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
,
SemanticsFlag
.
isFocused
],
maxValueLength:
10
,
currentValueLength:
3
,
));
semantics
.
dispose
();
});
testWidgets
(
'Read only TextField identifies as read only text field in semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
...
...
packages/flutter/test/semantics/semantics_test.dart
View file @
4373a319
...
...
@@ -447,6 +447,8 @@ void main() {
' textDirection: null
\n
'
' sortKey: null
\n
'
' platformViewId: null
\n
'
' maxValueLength: null
\n
'
' currentValueLength: null
\n
'
' scrollChildren: null
\n
'
' scrollIndex: null
\n
'
' scrollExtentMin: null
\n
'
...
...
@@ -543,6 +545,8 @@ void main() {
' textDirection: null
\n
'
' sortKey: null
\n
'
' platformViewId: null
\n
'
' maxValueLength: null
\n
'
' currentValueLength: null
\n
'
' scrollChildren: null
\n
'
' scrollIndex: null
\n
'
' scrollExtentMin: null
\n
'
...
...
packages/flutter/test/widgets/semantics_debugger_test.dart
View file @
4373a319
...
...
@@ -452,8 +452,11 @@ void main() {
),
);
final
dynamic
semanticsDebuggerPainter
=
_getSemanticsDebuggerPainter
(
debuggerKey:
debugger
,
tester:
tester
);
final
RenderObject
renderTextfield
=
tester
.
renderObject
(
find
.
descendant
(
of:
find
.
byKey
(
textField
),
matching:
find
.
byType
(
Semantics
)).
first
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
textField
,
debuggerKey:
debugger
,
tester:
tester
),
semanticsDebuggerPainter
.
getMessage
(
renderTextfield
.
debugSemantics
),
'textfield'
,
);
});
...
...
@@ -463,6 +466,14 @@ String _getMessageShownInSemanticsDebugger({
@required
Key
widgetKey
,
@required
Key
debuggerKey
,
@required
WidgetTester
tester
,
})
{
final
dynamic
semanticsDebuggerPainter
=
_getSemanticsDebuggerPainter
(
debuggerKey:
debuggerKey
,
tester:
tester
);
return
semanticsDebuggerPainter
.
getMessage
(
tester
.
renderObject
(
find
.
byKey
(
widgetKey
)).
debugSemantics
);
}
dynamic
_getSemanticsDebuggerPainter
(
{
@required
Key
debuggerKey
,
@required
WidgetTester
tester
,
})
{
final
CustomPaint
customPaint
=
tester
.
widgetList
(
find
.
descendant
(
of:
find
.
byKey
(
debuggerKey
),
...
...
@@ -470,5 +481,5 @@ String _getMessageShownInSemanticsDebugger({
)).
first
;
final
dynamic
semanticsDebuggerPainter
=
customPaint
.
foregroundPainter
;
expect
(
semanticsDebuggerPainter
.
runtimeType
.
toString
(),
'_SemanticsDebuggerPainter'
);
return
semanticsDebuggerPainter
.
getMessage
(
tester
.
renderObject
(
find
.
byKey
(
widgetKey
)).
debugSemantics
)
;
return
semanticsDebuggerPainter
;
}
packages/flutter/test/widgets/semantics_tester.dart
View file @
4373a319
...
...
@@ -442,6 +442,8 @@ class SemanticsTester {
double
scrollPosition
,
double
scrollExtentMax
,
double
scrollExtentMin
,
int
currentValueLength
,
int
maxValueLength
,
SemanticsNode
ancestor
,
})
{
bool
checkNode
(
SemanticsNode
node
)
{
...
...
@@ -471,6 +473,12 @@ class SemanticsTester {
return
false
;
if
(
scrollExtentMin
!=
null
&&
!
nearEqual
(
node
.
scrollExtentMin
,
scrollExtentMin
,
0.1
))
return
false
;
if
(
currentValueLength
!=
null
&&
node
.
currentValueLength
!=
currentValueLength
)
{
return
false
;
}
if
(
maxValueLength
!=
null
&&
node
.
maxValueLength
!=
maxValueLength
)
{
return
false
;
}
return
true
;
}
...
...
@@ -713,7 +721,19 @@ class _IncludesNodeWith extends Matcher {
this
.
scrollPosition
,
this
.
scrollExtentMax
,
this
.
scrollExtentMin
,
})
:
assert
(
label
!=
null
||
value
!=
null
||
actions
!=
null
||
flags
!=
null
||
scrollPosition
!=
null
||
scrollExtentMax
!=
null
||
scrollExtentMin
!=
null
);
this
.
maxValueLength
,
this
.
currentValueLength
,
})
:
assert
(
label
!=
null
||
value
!=
null
||
actions
!=
null
||
flags
!=
null
||
scrollPosition
!=
null
||
scrollExtentMax
!=
null
||
scrollExtentMin
!=
null
||
maxValueLength
!=
null
||
currentValueLength
!=
null
);
final
String
label
;
final
String
value
;
...
...
@@ -724,6 +744,8 @@ class _IncludesNodeWith extends Matcher {
final
double
scrollPosition
;
final
double
scrollExtentMax
;
final
double
scrollExtentMin
;
final
int
currentValueLength
;
final
int
maxValueLength
;
@override
bool
matches
(
covariant
SemanticsTester
item
,
Map
<
dynamic
,
dynamic
>
matchState
)
{
...
...
@@ -737,6 +759,8 @@ class _IncludesNodeWith extends Matcher {
scrollPosition:
scrollPosition
,
scrollExtentMax:
scrollExtentMax
,
scrollExtentMin:
scrollExtentMin
,
currentValueLength:
currentValueLength
,
maxValueLength:
maxValueLength
,
).
isNotEmpty
;
}
...
...
@@ -761,6 +785,8 @@ class _IncludesNodeWith extends Matcher {
if
(
scrollPosition
!=
null
)
'scrollPosition "
$scrollPosition
"'
,
if
(
scrollExtentMax
!=
null
)
'scrollExtentMax "
$scrollExtentMax
"'
,
if
(
scrollExtentMin
!=
null
)
'scrollExtentMin "
$scrollExtentMin
"'
,
if
(
currentValueLength
!=
null
)
'currentValueLength "
$currentValueLength
"'
,
if
(
maxValueLength
!=
null
)
'maxValueLength "
$maxValueLength
"'
,
];
return
strings
.
join
(
', '
);
}
...
...
@@ -780,6 +806,8 @@ Matcher includesNodeWith({
double
scrollPosition
,
double
scrollExtentMax
,
double
scrollExtentMin
,
int
maxValueLength
,
int
currentValueLength
,
})
{
return
_IncludesNodeWith
(
label:
label
,
...
...
@@ -791,5 +819,7 @@ Matcher includesNodeWith({
scrollPosition:
scrollPosition
,
scrollExtentMax:
scrollExtentMax
,
scrollExtentMin:
scrollExtentMin
,
maxValueLength:
maxValueLength
,
currentValueLength:
currentValueLength
,
);
}
packages/flutter_driver/lib/src/common/find.dart
View file @
4373a319
...
...
@@ -317,6 +317,7 @@ class Descendant extends SerializableFinder {
@required
this
.
of
,
@required
this
.
matching
,
this
.
matchRoot
=
false
,
this
.
firstMatchOnly
=
false
,
});
/// The finder specifying the widget of which the descendant is to be found.
...
...
@@ -328,6 +329,9 @@ class Descendant extends SerializableFinder {
/// Whether the widget matching [of] will be considered for a match.
final
bool
matchRoot
;
/// If true then only the first descendant matching `matching` will be returned.
final
bool
firstMatchOnly
;
@override
String
get
finderType
=>
'Descendant'
;
...
...
@@ -338,6 +342,7 @@ class Descendant extends SerializableFinder {
..
addAll
(
matching
.
serialize
().
map
((
String
key
,
String
value
)
=>
MapEntry
<
String
,
String
>(
'matching_
$key
'
,
value
)))
..
addAll
(<
String
,
String
>{
'matchRoot'
:
matchRoot
?
'true'
:
'false'
,
'firstMatchOnly'
:
firstMatchOnly
?
'true'
:
'false'
,
});
}
...
...
@@ -359,6 +364,7 @@ class Descendant extends SerializableFinder {
of:
SerializableFinder
.
deserialize
(
of
),
matching:
SerializableFinder
.
deserialize
(
matching
),
matchRoot:
other
[
'matchRoot'
]
==
'true'
,
firstMatchOnly:
other
[
'firstMatchOnly'
]
==
'true'
,
);
}
}
...
...
@@ -374,6 +380,7 @@ class Ancestor extends SerializableFinder {
@required
this
.
of
,
@required
this
.
matching
,
this
.
matchRoot
=
false
,
this
.
firstMatchOnly
=
false
,
});
/// The finder specifying the widget of which the ancestor is to be found.
...
...
@@ -385,6 +392,9 @@ class Ancestor extends SerializableFinder {
/// Whether the widget matching [of] will be considered for a match.
final
bool
matchRoot
;
/// If true then only the first ancestor matching `matching` will be returned.
final
bool
firstMatchOnly
;
@override
String
get
finderType
=>
'Ancestor'
;
...
...
@@ -395,6 +405,7 @@ class Ancestor extends SerializableFinder {
..
addAll
(
matching
.
serialize
().
map
((
String
key
,
String
value
)
=>
MapEntry
<
String
,
String
>(
'matching_
$key
'
,
value
)))
..
addAll
(<
String
,
String
>{
'matchRoot'
:
matchRoot
?
'true'
:
'false'
,
'firstMatchOnly'
:
firstMatchOnly
?
'true'
:
'false'
,
});
}
...
...
@@ -416,6 +427,7 @@ class Ancestor extends SerializableFinder {
of:
SerializableFinder
.
deserialize
(
of
),
matching:
SerializableFinder
.
deserialize
(
matching
),
matchRoot:
other
[
'matchRoot'
]
==
'true'
,
firstMatchOnly:
other
[
'firstMatchOnly'
]
==
'true'
,
);
}
}
...
...
packages/flutter_driver/lib/src/driver/driver.dart
View file @
4373a319
...
...
@@ -1199,22 +1199,30 @@ class CommonFinders {
///
/// If the `matchRoot` argument is true then the widget specified by `of` will
/// be considered for a match. The argument defaults to false.
///
/// If `firstMatchOnly` is true then only the first ancestor matching
/// `matching` will be returned. Defaults to false.
SerializableFinder
ancestor
({
@required
SerializableFinder
of
,
@required
SerializableFinder
matching
,
bool
matchRoot
=
false
,
})
=>
Ancestor
(
of:
of
,
matching:
matching
,
matchRoot:
matchRoot
);
bool
firstMatchOnly
=
false
,
})
=>
Ancestor
(
of:
of
,
matching:
matching
,
matchRoot:
matchRoot
,
firstMatchOnly:
firstMatchOnly
);
/// Finds the widget that is an descendant of the `of` parameter and that
/// matches the `matching` parameter.
///
/// If the `matchRoot` argument is true then the widget specified by `of` will
/// be considered for a match. The argument defaults to false.
///
/// If `firstMatchOnly` is true then only the first descendant matching
/// `matching` will be returned. Defaults to false.
SerializableFinder
descendant
({
@required
SerializableFinder
of
,
@required
SerializableFinder
matching
,
bool
matchRoot
=
false
,
})
=>
Descendant
(
of:
of
,
matching:
matching
,
matchRoot:
matchRoot
);
bool
firstMatchOnly
=
false
,
})
=>
Descendant
(
of:
of
,
matching:
matching
,
matchRoot:
matchRoot
,
firstMatchOnly:
firstMatchOnly
);
}
/// An immutable 2D floating-point offset used by Flutter Driver.
...
...
packages/flutter_driver/lib/src/extension/extension.dart
View file @
4373a319
...
...
@@ -335,19 +335,21 @@ class FlutterDriverExtension {
}
Finder
_createAncestorFinder
(
Ancestor
arguments
)
{
return
find
.
ancestor
(
final
Finder
finder
=
find
.
ancestor
(
of:
_createFinder
(
arguments
.
of
),
matching:
_createFinder
(
arguments
.
matching
),
matchRoot:
arguments
.
matchRoot
,
);
return
arguments
.
firstMatchOnly
?
finder
.
first
:
finder
;
}
Finder
_createDescendantFinder
(
Descendant
arguments
)
{
return
find
.
descendant
(
final
Finder
finder
=
find
.
descendant
(
of:
_createFinder
(
arguments
.
of
),
matching:
_createFinder
(
arguments
.
matching
),
matchRoot:
arguments
.
matchRoot
,
);
return
arguments
.
firstMatchOnly
?
finder
.
first
:
finder
;
}
Finder
_createFinder
(
SerializableFinder
finder
)
{
...
...
packages/flutter_driver/test/src/extension_test.dart
View file @
4373a319
...
...
@@ -573,6 +573,39 @@ void main() {
expect
(
await
result
,
null
);
});
testWidgets
(
'descendant finder firstMatchOnly'
,
(
WidgetTester
tester
)
async
{
flutterDriverLog
.
listen
((
LogRecord
_
)
{});
// Silence logging.
final
FlutterDriverExtension
extension
=
FlutterDriverExtension
((
String
arg
)
async
=>
''
,
true
);
Future
<
String
>
getDescendantText
()
async
{
final
Map
<
String
,
Object
>
arguments
=
GetText
(
Descendant
(
of:
ByValueKey
(
'column'
),
matching:
const
ByType
(
'Text'
),
firstMatchOnly:
true
,
),
timeout:
const
Duration
(
seconds:
1
)).
serialize
();
final
Map
<
String
,
dynamic
>
result
=
await
extension
.
call
(
arguments
);
if
(
result
[
'isError'
])
{
return
null
;
}
return
GetTextResult
.
fromJson
(
result
[
'response'
]).
text
;
}
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Column
(
key:
const
ValueKey
<
String
>(
'column'
),
children:
const
<
Widget
>[
Text
(
'Hello1'
,
key:
ValueKey
<
String
>(
'text1'
)),
Text
(
'Hello2'
,
key:
ValueKey
<
String
>(
'text2'
)),
Text
(
'Hello3'
,
key:
ValueKey
<
String
>(
'text3'
)),
],
),
),
);
expect
(
await
getDescendantText
(),
'Hello1'
);
});
testWidgets
(
'ancestor finder'
,
(
WidgetTester
tester
)
async
{
flutterDriverLog
.
listen
((
LogRecord
_
)
{});
// Silence logging.
final
FlutterDriverExtension
extension
=
FlutterDriverExtension
((
String
arg
)
async
=>
''
,
true
);
...
...
@@ -642,6 +675,54 @@ void main() {
expect
(
await
result
,
null
);
});
testWidgets
(
'ancestor finder firstMatchOnly'
,
(
WidgetTester
tester
)
async
{
flutterDriverLog
.
listen
((
LogRecord
_
)
{});
// Silence logging.
final
FlutterDriverExtension
extension
=
FlutterDriverExtension
((
String
arg
)
async
=>
''
,
true
);
Future
<
Offset
>
getAncestorTopLeft
()
async
{
final
Map
<
String
,
Object
>
arguments
=
GetOffset
(
Ancestor
(
of:
ByValueKey
(
'leaf'
),
matching:
const
ByType
(
'Container'
),
firstMatchOnly:
true
,
),
OffsetType
.
topLeft
,
timeout:
const
Duration
(
seconds:
1
)).
serialize
();
final
Map
<
String
,
dynamic
>
response
=
await
extension
.
call
(
arguments
);
if
(
response
[
'isError'
])
{
return
null
;
}
final
GetOffsetResult
result
=
GetOffsetResult
.
fromJson
(
response
[
'response'
]);
return
Offset
(
result
.
dx
,
result
.
dy
);
}
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Center
(
child:
Container
(
height:
200
,
width:
200
,
child:
Center
(
child:
Container
(
height:
100
,
width:
100
,
child:
Center
(
child:
Container
(
key:
const
ValueKey
<
String
>(
'leaf'
),
height:
50
,
width:
50
,
),
),
),
),
),
),
),
);
expect
(
await
getAncestorTopLeft
(),
const
Offset
((
800
-
100
)
/
2
,
(
600
-
100
)
/
2
),
);
});
testWidgets
(
'GetDiagnosticsTree'
,
(
WidgetTester
tester
)
async
{
final
FlutterDriverExtension
extension
=
FlutterDriverExtension
((
String
arg
)
async
=>
''
,
true
);
...
...
packages/flutter_driver/test/src/find_test.dart
View file @
4373a319
...
...
@@ -15,6 +15,7 @@ void main() {
of:
of
,
matching:
matching
,
matchRoot:
true
,
firstMatchOnly:
true
,
);
expect
(
a
.
serialize
(),
<
String
,
String
>{
'finderType'
:
'Ancestor'
,
...
...
@@ -23,7 +24,8 @@ void main() {
'matching_finderType'
:
'ByValueKey'
,
'matching_keyValueString'
:
'hello'
,
'matching_keyValueType'
:
'String'
,
'matchRoot'
:
'true'
'matchRoot'
:
'true'
,
'firstMatchOnly'
:
'true'
,
});
});
...
...
@@ -35,13 +37,15 @@ void main() {
'matching_finderType'
:
'ByValueKey'
,
'matching_keyValueString'
:
'hello'
,
'matching_keyValueType'
:
'String'
,
'matchRoot'
:
'true'
'matchRoot'
:
'true'
,
'firstMatchOnly'
:
'true'
,
};
final
Ancestor
a
=
Ancestor
.
deserialize
(
serialized
);
expect
(
a
.
of
,
isA
<
ByType
>());
expect
(
a
.
matching
,
isA
<
ByValueKey
>());
expect
(
a
.
matchRoot
,
isTrue
);
expect
(
a
.
firstMatchOnly
,
isTrue
);
});
test
(
'Descendant finder serialize'
,
()
{
...
...
@@ -52,6 +56,7 @@ void main() {
of:
of
,
matching:
matching
,
matchRoot:
true
,
firstMatchOnly:
true
,
);
expect
(
a
.
serialize
(),
<
String
,
String
>{
'finderType'
:
'Descendant'
,
...
...
@@ -60,7 +65,8 @@ void main() {
'matching_finderType'
:
'ByValueKey'
,
'matching_keyValueString'
:
'hello'
,
'matching_keyValueType'
:
'String'
,
'matchRoot'
:
'true'
'matchRoot'
:
'true'
,
'firstMatchOnly'
:
'true'
,
});
});
...
...
@@ -72,12 +78,14 @@ void main() {
'matching_finderType'
:
'ByValueKey'
,
'matching_keyValueString'
:
'hello'
,
'matching_keyValueType'
:
'String'
,
'matchRoot'
:
'true'
'matchRoot'
:
'true'
,
'firstMatchOnly'
:
'true'
,
};
final
Descendant
a
=
Descendant
.
deserialize
(
serialized
);
expect
(
a
.
of
,
isA
<
ByType
>());
expect
(
a
.
matching
,
isA
<
ByValueKey
>());
expect
(
a
.
matchRoot
,
isTrue
);
expect
(
a
.
firstMatchOnly
,
isTrue
);
});
}
packages/flutter_test/lib/src/matchers.dart
View file @
4373a319
...
...
@@ -433,6 +433,8 @@ Matcher matchesSemantics({
double
elevation
,
double
thickness
,
int
platformViewId
,
int
maxValueLength
,
int
currentValueLength
,
// Flags //
bool
hasCheckedState
=
false
,
bool
isChecked
=
false
,
...
...
@@ -552,6 +554,8 @@ Matcher matchesSemantics({
platformViewId:
platformViewId
,
customActions:
customActions
,
hintOverrides:
hintOverrides
,
currentValueLength:
currentValueLength
,
maxValueLength:
maxValueLength
,
children:
children
,
);
}
...
...
@@ -1745,6 +1749,8 @@ class _MatchesSemanticsData extends Matcher {
this
.
elevation
,
this
.
thickness
,
this
.
platformViewId
,
this
.
maxValueLength
,
this
.
currentValueLength
,
this
.
customActions
,
this
.
hintOverrides
,
this
.
children
,
...
...
@@ -1765,6 +1771,8 @@ class _MatchesSemanticsData extends Matcher {
final
double
elevation
;
final
double
thickness
;
final
int
platformViewId
;
final
int
maxValueLength
;
final
int
currentValueLength
;
final
List
<
Matcher
>
children
;
@override
...
...
@@ -1796,6 +1804,10 @@ class _MatchesSemanticsData extends Matcher {
description
.
add
(
' with thickness:
$thickness
'
);
if
(
platformViewId
!=
null
)
description
.
add
(
' with platformViewId:
$platformViewId
'
);
if
(
maxValueLength
!=
null
)
description
.
add
(
' with maxValueLength:
$maxValueLength
'
);
if
(
currentValueLength
!=
null
)
description
.
add
(
' with currentValueLength:
$currentValueLength
'
);
if
(
customActions
!=
null
)
description
.
add
(
' with custom actions:
$customActions
'
);
if
(
hintOverrides
!=
null
)
...
...
@@ -1838,6 +1850,10 @@ class _MatchesSemanticsData extends Matcher {
return
failWithDescription
(
matchState
,
'thickness was:
${data.thickness}
'
);
if
(
platformViewId
!=
null
&&
platformViewId
!=
data
.
platformViewId
)
return
failWithDescription
(
matchState
,
'platformViewId was:
${data.platformViewId}
'
);
if
(
currentValueLength
!=
null
&&
currentValueLength
!=
data
.
currentValueLength
)
return
failWithDescription
(
matchState
,
'currentValueLength was:
${data.currentValueLength}
'
);
if
(
maxValueLength
!=
null
&&
maxValueLength
!=
data
.
maxValueLength
)
return
failWithDescription
(
matchState
,
'maxValueLength was:
${data.maxValueLength}
'
);
if
(
actions
!=
null
)
{
int
actionBits
=
0
;
for
(
SemanticsAction
action
in
actions
)
...
...
packages/flutter_test/test/matchers_test.dart
View file @
4373a319
...
...
@@ -525,6 +525,8 @@ void main() {
scrollExtentMin:
null
,
platformViewId:
105
,
customSemanticsActionIds:
<
int
>[
CustomSemanticsAction
.
getIdentifier
(
action
)],
currentValueLength:
10
,
maxValueLength:
15
,
);
final
_FakeSemanticsNode
node
=
_FakeSemanticsNode
();
node
.
data
=
data
;
...
...
@@ -535,6 +537,8 @@ void main() {
elevation:
3.0
,
thickness:
4.0
,
platformViewId:
105
,
currentValueLength:
10
,
maxValueLength:
15
,
/* Flags */
hasCheckedState:
true
,
isChecked:
true
,
...
...
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