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
b8fd21b0
Unverified
Commit
b8fd21b0
authored
Jan 25, 2022
by
Lasse R.H. Nielsen
Committed by
GitHub
Jan 25, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adds `CommonFinders.bySubtype<T extends Widget>()` finder. (#91415)
parent
c1710723
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
130 additions
and
35 deletions
+130
-35
finders.dart
packages/flutter_test/lib/src/finders.dart
+33
-0
finders_test.dart
packages/flutter_test/test/finders_test.dart
+97
-35
No files found.
packages/flutter_test/lib/src/finders.dart
View file @
b8fd21b0
...
@@ -128,6 +128,24 @@ class CommonFinders {
...
@@ -128,6 +128,24 @@ class CommonFinders {
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder
byKey
(
Key
key
,
{
bool
skipOffstage
=
true
})
=>
_KeyFinder
(
key
,
skipOffstage:
skipOffstage
);
Finder
byKey
(
Key
key
,
{
bool
skipOffstage
=
true
})
=>
_KeyFinder
(
key
,
skipOffstage:
skipOffstage
);
/// Finds widgets by searching for widgets implementing a particular type.
///
/// This matcher accepts subtypes. For example a
/// `bySubtype<StatefulWidget>()` will find any stateful widget.
///
/// ## Sample code
///
/// ```dart
/// expect(find.bySubtype<IconButton>(), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
///
/// See also:
/// * [byType], which does not do subtype tests.
Finder
bySubtype
<
T
extends
Widget
>({
bool
skipOffstage
=
true
})
=>
_WidgetSubtypeFinder
<
T
>(
skipOffstage:
skipOffstage
);
/// Finds widgets by searching for widgets with a particular type.
/// Finds widgets by searching for widgets with a particular type.
///
///
/// This does not do subclass tests, so for example
/// This does not do subclass tests, so for example
...
@@ -144,6 +162,9 @@ class CommonFinders {
...
@@ -144,6 +162,9 @@ class CommonFinders {
///
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// nodes that are [Offstage] or that are from inactive [Route]s.
///
/// See also:
/// * [bySubtype], which allows subtype tests.
Finder
byType
(
Type
type
,
{
bool
skipOffstage
=
true
})
=>
_WidgetTypeFinder
(
type
,
skipOffstage:
skipOffstage
);
Finder
byType
(
Type
type
,
{
bool
skipOffstage
=
true
})
=>
_WidgetTypeFinder
(
type
,
skipOffstage:
skipOffstage
);
/// Finds [Icon] widgets containing icon data equal to the `icon`
/// Finds [Icon] widgets containing icon data equal to the `icon`
...
@@ -713,6 +734,18 @@ class _KeyFinder extends MatchFinder {
...
@@ -713,6 +734,18 @@ class _KeyFinder extends MatchFinder {
}
}
}
}
class
_WidgetSubtypeFinder
<
T
extends
Widget
>
extends
MatchFinder
{
_WidgetSubtypeFinder
({
bool
skipOffstage
=
true
})
:
super
(
skipOffstage:
skipOffstage
);
@override
String
get
description
=>
'is "
$T
"'
;
@override
bool
matches
(
Element
candidate
)
{
return
candidate
.
widget
is
T
;
}
}
class
_WidgetTypeFinder
extends
MatchFinder
{
class
_WidgetTypeFinder
extends
MatchFinder
{
_WidgetTypeFinder
(
this
.
widgetType
,
{
bool
skipOffstage
=
true
})
:
super
(
skipOffstage:
skipOffstage
);
_WidgetTypeFinder
(
this
.
widgetType
,
{
bool
skipOffstage
=
true
})
:
super
(
skipOffstage:
skipOffstage
);
...
...
packages/flutter_test/test/finders_test.dart
View file @
b8fd21b0
...
@@ -11,17 +11,18 @@ import 'package:flutter_test/flutter_test.dart';
...
@@ -11,17 +11,18 @@ import 'package:flutter_test/flutter_test.dart';
void
main
(
)
{
void
main
(
)
{
group
(
'image'
,
()
{
group
(
'image'
,
()
{
testWidgets
(
'finds Image widgets'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Image widgets'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
Image
(
image:
FileImage
(
File
(
'test'
)))
.
pumpWidget
(
_boilerplate
(
Image
(
image:
FileImage
(
File
(
'test'
)))));
));
expect
(
find
.
image
(
FileImage
(
File
(
'test'
))),
findsOneWidget
);
expect
(
find
.
image
(
FileImage
(
File
(
'test'
))),
findsOneWidget
);
});
});
testWidgets
(
'finds Button widgets with Image'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Button widgets with Image'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
.
pumpWidget
(
_boilerplate
(
ElevatedButton
(
ElevatedButton
(
onPressed:
null
,
child:
Image
(
image:
FileImage
(
File
(
'test'
))),)
onPressed:
null
,
));
child:
Image
(
image:
FileImage
(
File
(
'test'
))),
expect
(
find
.
widgetWithImage
(
ElevatedButton
,
FileImage
(
File
(
'test'
))),
findsOneWidget
);
)));
expect
(
find
.
widgetWithImage
(
ElevatedButton
,
FileImage
(
File
(
'test'
))),
findsOneWidget
);
});
});
});
});
...
@@ -34,9 +35,10 @@ void main() {
...
@@ -34,9 +35,10 @@ void main() {
});
});
testWidgets
(
'finds Text.rich widgets'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Text.rich widgets'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
.
pumpWidget
(
_boilerplate
(
const
Text
.
rich
(
const
Text
.
rich
(
TextSpan
(
TextSpan
(
text:
't'
,
children:
<
TextSpan
>[
text:
't'
,
children:
<
TextSpan
>[
TextSpan
(
text:
'e'
),
TextSpan
(
text:
'e'
),
TextSpan
(
text:
'st'
),
TextSpan
(
text:
'st'
),
],
],
...
@@ -137,15 +139,16 @@ void main() {
...
@@ -137,15 +139,16 @@ void main() {
});
});
testWidgets
(
'finds Text.rich widgets'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Text.rich widgets'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
.
pumpWidget
(
_boilerplate
(
const
Text
.
rich
(
const
Text
.
rich
(
TextSpan
(
TextSpan
(
text:
'this'
,
children:
<
TextSpan
>[
text:
'this'
,
TextSpan
(
text:
'is'
),
children:
<
TextSpan
>[
TextSpan
(
text:
'a'
),
TextSpan
(
text:
'is'
),
TextSpan
(
text:
'test'
),
TextSpan
(
text:
'a'
),
],
TextSpan
(
text:
'test'
),
),
],
)));
),
)));
expect
(
find
.
textContaining
(
RegExp
(
r'isatest'
)),
findsOneWidget
);
expect
(
find
.
textContaining
(
RegExp
(
r'isatest'
)),
findsOneWidget
);
expect
(
find
.
textContaining
(
'isatest'
),
findsOneWidget
);
expect
(
find
.
textContaining
(
'isatest'
),
findsOneWidget
);
...
@@ -166,11 +169,13 @@ void main() {
...
@@ -166,11 +169,13 @@ void main() {
});
});
group
(
'semantics'
,
()
{
group
(
'semantics'
,
()
{
testWidgets
(
'Throws StateError if semantics are not enabled'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Throws StateError if semantics are not enabled'
,
(
WidgetTester
tester
)
async
{
expect
(()
=>
find
.
bySemanticsLabel
(
'Add'
),
throwsStateError
);
expect
(()
=>
find
.
bySemanticsLabel
(
'Add'
),
throwsStateError
);
},
semanticsEnabled:
false
);
},
semanticsEnabled:
false
);
testWidgets
(
'finds Semantically labeled widgets'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Semantically labeled widgets'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
.
pumpWidget
(
_boilerplate
(
Semantics
(
Semantics
(
...
@@ -186,7 +191,8 @@ void main() {
...
@@ -186,7 +191,8 @@ void main() {
semanticsHandle
.
dispose
();
semanticsHandle
.
dispose
();
});
});
testWidgets
(
'finds Semantically labeled widgets by RegExp'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Semantically labeled widgets by RegExp'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
.
pumpWidget
(
_boilerplate
(
Semantics
(
Semantics
(
...
@@ -202,18 +208,19 @@ void main() {
...
@@ -202,18 +208,19 @@ void main() {
semanticsHandle
.
dispose
();
semanticsHandle
.
dispose
();
});
});
testWidgets
(
'finds Semantically labeled widgets without explicit Semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'finds Semantically labeled widgets without explicit Semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
final
SemanticsHandle
semanticsHandle
=
tester
.
ensureSemantics
();
await
tester
.
pumpWidget
(
_boilerplate
(
await
tester
const
SimpleCustomSemanticsWidget
(
'Foo'
)
.
pumpWidget
(
_boilerplate
(
const
SimpleCustomSemanticsWidget
(
'Foo'
)));
));
expect
(
find
.
bySemanticsLabel
(
'Foo'
),
findsOneWidget
);
expect
(
find
.
bySemanticsLabel
(
'Foo'
),
findsOneWidget
);
semanticsHandle
.
dispose
();
semanticsHandle
.
dispose
();
});
});
});
});
group
(
'hitTestable'
,
()
{
group
(
'hitTestable'
,
()
{
testWidgets
(
'excludes non-hit-testable widgets'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'excludes non-hit-testable widgets'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
_boilerplate
(
IndexedStack
(
_boilerplate
(
IndexedStack
(
sizing:
StackFit
.
expand
,
sizing:
StackFit
.
expand
,
...
@@ -221,13 +228,13 @@ void main() {
...
@@ -221,13 +228,13 @@ void main() {
GestureDetector
(
GestureDetector
(
key:
const
ValueKey
<
int
>(
0
),
key:
const
ValueKey
<
int
>(
0
),
behavior:
HitTestBehavior
.
opaque
,
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
},
onTap:
()
{},
child:
const
SizedBox
.
expand
(),
child:
const
SizedBox
.
expand
(),
),
),
GestureDetector
(
GestureDetector
(
key:
const
ValueKey
<
int
>(
1
),
key:
const
ValueKey
<
int
>(
1
),
behavior:
HitTestBehavior
.
opaque
,
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
},
onTap:
()
{},
child:
const
SizedBox
.
expand
(),
child:
const
SizedBox
.
expand
(),
),
),
],
],
...
@@ -258,13 +265,52 @@ void main() {
...
@@ -258,13 +265,52 @@ void main() {
// candidates, it should find 1 instead of 2. If the _LastFinder wasn't
// candidates, it should find 1 instead of 2. If the _LastFinder wasn't
// correctly chained after the descendant's candidates, the last element
// correctly chained after the descendant's candidates, the last element
// with a Text widget would have been 2.
// with a Text widget would have been 2.
final
Text
text
=
find
.
descendant
(
final
Text
text
=
find
of:
find
.
byKey
(
key1
),
.
descendant
(
matching:
find
.
byType
(
Text
),
of:
find
.
byKey
(
key1
),
).
last
.
evaluate
().
single
.
widget
as
Text
;
matching:
find
.
byType
(
Text
),
)
.
last
.
evaluate
()
.
single
.
widget
as
Text
;
expect
(
text
.
data
,
'1'
);
expect
(
text
.
data
,
'1'
);
});
});
testWidgets
(
'finds multiple subtypes'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
_boilerplate
(
Row
(
children:
<
Widget
>[
Column
(
children:
const
<
Widget
>[
Text
(
'Hello'
),
Text
(
'World'
),
]),
Column
(
children:
<
Widget
>[
Image
(
image:
FileImage
(
File
(
'test'
))),
]),
Column
(
children:
const
<
Widget
>[
SimpleGenericWidget
<
int
>(
child:
Text
(
'one'
)),
SimpleGenericWidget
<
double
>(
child:
Text
(
'pi'
)),
SimpleGenericWidget
<
String
>(
child:
Text
(
'two'
)),
]),
]),
));
expect
(
find
.
bySubtype
<
Row
>(),
findsOneWidget
);
expect
(
find
.
bySubtype
<
Column
>(),
findsNWidgets
(
3
));
// Finds both rows and columns.
expect
(
find
.
bySubtype
<
Flex
>(),
findsNWidgets
(
4
));
// Finds only the requested generic subtypes.
expect
(
find
.
bySubtype
<
SimpleGenericWidget
<
int
>>(),
findsOneWidget
);
expect
(
find
.
bySubtype
<
SimpleGenericWidget
<
num
>>(),
findsNWidgets
(
2
));
expect
(
find
.
bySubtype
<
SimpleGenericWidget
<
Object
>>(),
findsNWidgets
(
3
));
// Finds all widgets.
final
int
totalWidgetCount
=
find
.
byWidgetPredicate
((
_
)
=>
true
).
evaluate
().
length
;
expect
(
find
.
bySubtype
<
Widget
>(),
findsNWidgets
(
totalWidgetCount
));
});
}
}
Widget
_boilerplate
(
Widget
child
)
{
Widget
_boilerplate
(
Widget
child
)
{
...
@@ -280,7 +326,8 @@ class SimpleCustomSemanticsWidget extends LeafRenderObjectWidget {
...
@@ -280,7 +326,8 @@ class SimpleCustomSemanticsWidget extends LeafRenderObjectWidget {
final
String
label
;
final
String
label
;
@override
@override
RenderObject
createRenderObject
(
BuildContext
context
)
=>
SimpleCustomSemanticsRenderObject
(
label
);
RenderObject
createRenderObject
(
BuildContext
context
)
=>
SimpleCustomSemanticsRenderObject
(
label
);
}
}
class
SimpleCustomSemanticsRenderObject
extends
RenderBox
{
class
SimpleCustomSemanticsRenderObject
extends
RenderBox
{
...
@@ -299,6 +346,21 @@ class SimpleCustomSemanticsRenderObject extends RenderBox {
...
@@ -299,6 +346,21 @@ class SimpleCustomSemanticsRenderObject extends RenderBox {
@override
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
super
.
describeSemanticsConfiguration
(
config
);
config
..
label
=
label
..
textDirection
=
TextDirection
.
ltr
;
config
..
label
=
label
..
textDirection
=
TextDirection
.
ltr
;
}
}
class
SimpleGenericWidget
<
T
>
extends
StatelessWidget
{
const
SimpleGenericWidget
({
required
Widget
child
,
Key
?
key
})
:
_child
=
child
,
super
(
key:
key
);
final
Widget
_child
;
@override
Widget
build
(
BuildContext
context
)
{
return
_child
;
}
}
}
}
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