diff --git a/assets/images/todos_issue.png b/assets/images/todos_issue.png new file mode 100644 index 0000000000000000000000000000000000000000..ee122f9ec21f61547bb29e7841527c198a583256 Binary files /dev/null and b/assets/images/todos_issue.png differ diff --git a/assets/images/todos_issue.svg b/assets/images/todos_issue.svg new file mode 100644 index 0000000000000000000000000000000000000000..5e12169d26b41e5bd50b032d8848727a9b7470f0 --- /dev/null +++ b/assets/images/todos_issue.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg"> + <title>todos-issue</title> + <g id="To-Do-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="To-Do--最新版" transform="translate(-15.000000, -99.000000)"> + <g id="编组-7" transform="translate(15.000000, 99.000000)"> + <rect id="矩形" fill="#DEDEFF" x="0" y="0" width="28" height="28" rx="13"></rect> + <g id="ç›®å½•æ ‘å¤‡ä»½-2" transform="translate(6.000000, 6.000000)" fill="#6666C4" fill-rule="nonzero"> + <g id="编组-2" transform="translate(0.666667, 1.333333)"> + <path d="M1.33333333,13.3333333 C0.596953667,13.3333333 0,12.7363797 0,12 L0,1.33333333 C0,0.596953667 0.596953667,0 1.33333333,0 L10,0 C10.6930632,0 11.262621,0.528789407 11.3272297,1.20492444 L11.3333333,1.33333333 L13.2885882,1.33333333 C14.0249679,1.33333333 14.6219216,1.930287 14.6219216,2.66666667 C14.6219216,2.75434672 14.6132729,2.84181297 14.596101,2.92779506 L12.7321052,12.2611284 C12.6076033,12.8845302 12.0603051,13.3333333 11.4245924,13.3333333 L1.33333333,13.3333333 Z M9.833,1.5 L1.5,1.5 L1.5,11.833 L9.833,11.833 L9.833,1.5 Z M11.333,11.6073333 L13.085,2.833 L11.333,2.83233333 L11.333,11.6073333 Z" id="形状结åˆ"></path> + </g> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/assets/images/todos_mr.png b/assets/images/todos_mr.png new file mode 100644 index 0000000000000000000000000000000000000000..d836efd8d3187536404442d27e58b8885922c98d Binary files /dev/null and b/assets/images/todos_mr.png differ diff --git a/assets/images/todos_mr.svg b/assets/images/todos_mr.svg new file mode 100644 index 0000000000000000000000000000000000000000..26b5bb873383b68232fe2dbd33ab86d4487a60be --- /dev/null +++ b/assets/images/todos_mr.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg"> + <title>todos-mr</title> + <g id="To-Do-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="To-Do--最新版" transform="translate(-15.000000, -334.000000)"> + <g id="编组-10" transform="translate(15.000000, 334.000000)"> + <rect id="矩形备份-4" fill="#E5F0FF" x="0" y="0" width="28" height="28" rx="13"></rect> + <g id="git-merge-line" transform="translate(7.000000, 5.000000)" fill="#0063E9" fill-rule="nonzero"> + <path d="M3.5,1.25 C4.74264069,1.25 5.75,2.25735931 5.75,3.5 C5.75,4.47195517 5.13370913,5.29996789 4.27058374,5.61458182 C4.44227496,6.89814292 5.51260934,7.53731455 7.74097496,7.58238606 L8,7.58495336 C10.3757305,7.58495336 11.9102454,8.25436694 12.6999036,9.4270162 C12.9097615,9.73865704 13.0469597,10.0409807 13.13194,10.3407879 C14.0662313,10.6121364 14.75,11.4762168 14.75,12.5 C14.75,13.7426407 13.7426407,14.75 12.5,14.75 C11.2573593,14.75 10.25,13.7426407 10.25,12.5 C10.25,11.5909881 10.7890542,10.807879 11.5649356,10.4528997 C11.5354375,10.3909963 11.4983666,10.3282054 11.4557064,10.2648546 C10.9965858,9.58305663 10.0173791,9.13181393 8.28056444,9.08839415 L8,9.08495336 C6.33935316,9.08495336 5.04069212,8.7682029 4.14971801,8.14361951 L4.15055925,10.3454827 C5.07600625,10.6245418 5.75,11.483561 5.75,12.5 C5.75,13.7426407 4.74264069,14.75 3.5,14.75 C2.25735931,14.75 1.25,13.7426407 1.25,12.5 C1.25,11.558121 1.82873999,10.751414 2.65001064,10.4160882 L2.65001064,5.58391175 C1.82873999,5.24858603 1.25,4.441879 1.25,3.5 C1.25,2.25735931 2.25735931,1.25 3.5,1.25 Z M3.5,11.75 C3.08578644,11.75 2.75,12.0857864 2.75,12.5 C2.75,12.9142136 3.08578644,13.25 3.5,13.25 C3.91421356,13.25 4.25,12.9142136 4.25,12.5 C4.25,12.0857864 3.91421356,11.75 3.5,11.75 Z M12.5,11.75 C12.0857864,11.75 11.75,12.0857864 11.75,12.5 C11.75,12.9142136 12.0857864,13.25 12.5,13.25 C12.9142136,13.25 13.25,12.9142136 13.25,12.5 C13.25,12.0857864 12.9142136,11.75 12.5,11.75 Z M3.5,2.75 C3.08578644,2.75 2.75,3.08578644 2.75,3.5 C2.75,3.91421356 3.08578644,4.25 3.5,4.25 C3.91421356,4.25 4.25,3.91421356 4.25,3.5 C4.25,3.08578644 3.91421356,2.75 3.5,2.75 Z" id="形状结åˆ"></path> + </g> + </g> + </g> + </g> +</svg> \ No newline at end of file diff --git a/lib/modules/to_dos/to_do_type_painter.dart b/lib/modules/to_dos/to_do_type_painter.dart new file mode 100644 index 0000000000000000000000000000000000000000..b243188d2dae0390b01d09308a3e51e8f43d4e19 --- /dev/null +++ b/lib/modules/to_dos/to_do_type_painter.dart @@ -0,0 +1,32 @@ +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; + +class ToDoTypePainter extends CustomPainter { + final double _width = 28; + final bool showTopLine; + final bool showBottomLine; + final ui.Image? image; + late final Paint _paint; + + ToDoTypePainter({required this.image, required this.showTopLine, required this.showBottomLine}) + : _paint = Paint() + ..color = const Color(0xFFEAEAEA) + ..strokeWidth = 1.0 + ..isAntiAlias = true; + + @override + void paint(Canvas canvas, Size size) { + final double radius = _width / 2; + if (image != null) { + if (showTopLine) canvas.drawLine(Offset(radius, 0), Offset(radius, 10), _paint); + if (showBottomLine) canvas.drawLine(Offset(radius, 38), Offset(radius, size.height), _paint); + canvas.drawImage(image!, const Offset(0, 10), _paint); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return (oldDelegate as ToDoTypePainter).image != image || (oldDelegate).showTopLine != showTopLine || (oldDelegate).showBottomLine != showBottomLine; + } +} diff --git a/lib/modules/to_dos/to_do_view.dart b/lib/modules/to_dos/to_do_view.dart new file mode 100644 index 0000000000000000000000000000000000000000..ae88c83541f018049cffa7262e4d8aaa5a1ce3ab --- /dev/null +++ b/lib/modules/to_dos/to_do_view.dart @@ -0,0 +1,123 @@ +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:jihu_gitlab_app/core/domain/assignee/assignee.dart'; +import 'package:jihu_gitlab_app/core/layout/adaptive.dart'; +import 'package:jihu_gitlab_app/core/time.dart'; +import 'package:jihu_gitlab_app/core/widgets/avatar/avatar.dart'; +import 'package:jihu_gitlab_app/core/widgets/changeable_time.dart'; +import 'package:jihu_gitlab_app/modules/to_dos/to_do.dart'; +import 'package:jihu_gitlab_app/modules/to_dos/to_do_type_painter.dart'; +import 'package:jihu_gitlab_app/modules/to_dos/to_dos_view_model.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class ToDoView extends StatefulWidget { + const ToDoView({required this.toDo, required this.state, this.image, required this.isFirst, this.isSelected = false, required this.isLast, this.onItemTapped, this.onMarkDone, Key? key}) + : super(key: key); + final ToDo toDo; + final ToDoState state; + final ui.Image? image; + final bool isFirst; + final bool isLast; + final bool isSelected; + final VoidCallback? onItemTapped; + final VoidFutureCallBack? onMarkDone; + + @override + State<ToDoView> createState() => _ToDoViewState(); +} + +class _ToDoViewState extends State<ToDoView> { + bool _isMarkingDone = false; + + @override + Widget build(BuildContext context) { + ToDo todoItem = widget.toDo; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: CustomPaint( + painter: ToDoTypePainter(image: widget.image, showTopLine: !widget.isFirst, showBottomLine: !widget.isLast), + child: Container( + margin: const EdgeInsets.only(left: 32), + child: InkWell( + key: const Key('ToDosViewItem'), + onTap: widget.onItemTapped, + child: Container( + constraints: const BoxConstraints(minHeight: 50), + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.only(left: 12, top: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(4)), + border: isDesktopLayout(context) && widget.isSelected ? Border.all(color: const Color(0xFFFC6D26)) : null), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ + _buildAuthorInfo(todoItem.author, todoItem.updatedAt), + Padding( + padding: const EdgeInsets.only(right: 12), + child: Text.rich(TextSpan(text: todoItem.target.title, style: const TextStyle(fontSize: 14, color: Color(0xFF03162F), fontWeight: FontWeight.w500))), + ), + const SizedBox(height: 8), + const Divider(color: Color(0xFFEAEAEA), thickness: 1.0, height: 1, endIndent: 12), + _buildProjectInfo(todoItem.project.name) + ]), + ), + ), + ))); + } + + Widget _buildAuthorInfo(Assignee author, Time updatedAt) { + return Container( + padding: const EdgeInsets.only(right: 12), + child: Row( + children: [ + Avatar(avatarUrl: author.avatarUrl, size: 28), + const SizedBox(width: 6), + Expanded( + child: Text( + author.name, + style: const TextStyle(fontSize: 14, color: Color(0xFF03162F), fontWeight: FontWeight.w400), + overflow: TextOverflow.ellipsis, + ), + ), + Align( + alignment: Alignment.centerRight, + child: ChangeableTime.show(() => setState(() {}), false, updatedAt, const Color(0xFF95979A), FontWeight.w400), + ) + ], + ), + ); + } + + Widget _buildProjectInfo(String projectName) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset("assets/images/project_icon.svg", height: 16, width: 16), + const SizedBox(width: 2), + Expanded(child: Text(projectName, style: const TextStyle(fontSize: 12, color: Color(0xFF95979A)))), + _buildMarkDoneButton() + ], + ); + } + + Widget _buildMarkDoneButton() { + if (_isMarkingDone) { + return Padding(padding: const EdgeInsets.all(12), child: SizedBox(width: 16, height: 16, child: CircularProgressIndicator(color: Theme.of(context).primaryColor, strokeWidth: 2))); + } + if (pending) { + return InkWell(onTap: _onMarkDoneButtonTapped, child: Padding(padding: const EdgeInsets.all(12), child: SvgPicture.asset("assets/images/mark_as_done.svg", height: 16, width: 16))); + } + return const Padding(padding: EdgeInsets.all(12), child: SizedBox(height: 16, width: 16)); + } + + void _onMarkDoneButtonTapped() async { + if (pending) { + setState(() => _isMarkingDone = true); + await widget.onMarkDone?.call(); + setState(() => _isMarkingDone = false); + } + } + + bool get pending => widget.state == ToDoState.pending; +} diff --git a/lib/modules/to_dos/to_dos_view.dart b/lib/modules/to_dos/to_dos_view.dart index dbb1841fe4d23ff6f769961c149987be6a39fe28..37a88d4a6c18694a7921e02cebffa74981101c21 100644 --- a/lib/modules/to_dos/to_dos_view.dart +++ b/lib/modules/to_dos/to_dos_view.dart @@ -1,12 +1,12 @@ +import 'dart:ui' as ui; + import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter/services.dart'; import 'package:jihu_gitlab_app/core/layout/adaptive.dart'; import 'package:jihu_gitlab_app/core/load_state.dart'; import 'package:jihu_gitlab_app/core/connection_provider/connection_provider.dart'; import 'package:jihu_gitlab_app/core/widgets/alerter.dart'; -import 'package:jihu_gitlab_app/core/widgets/avatar/avatar.dart'; -import 'package:jihu_gitlab_app/core/widgets/changeable_time.dart'; import 'package:jihu_gitlab_app/core/widgets/http_fail_view.dart'; import 'package:jihu_gitlab_app/core/widgets/message_alert_view.dart'; import 'package:jihu_gitlab_app/core/widgets/toast.dart'; @@ -15,6 +15,7 @@ import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart'; import 'package:jihu_gitlab_app/modules/issues/details/issue_details_page.dart'; import 'package:jihu_gitlab_app/modules/mr/merge_request_page.dart'; import 'package:jihu_gitlab_app/modules/to_dos/to_do.dart'; +import 'package:jihu_gitlab_app/modules/to_dos/to_do_view.dart'; import 'package:jihu_gitlab_app/modules/to_dos/to_dos_view_model.dart'; import 'package:provider/provider.dart'; @@ -32,10 +33,14 @@ class ToDosView extends StatefulWidget { class ToDosViewState extends State<ToDosView> with AutomaticKeepAliveClientMixin { final ToDosViewModel _model = ToDosViewModel(); + ui.Image? _issueImage; + ui.Image? _mrImage; @override void initState() { super.initState(); + _getAssetImage("assets/images/todos_issue.png", width: 28, height: 28).then((value) => setState(() => _issueImage = value)); + _getAssetImage("assets/images/todos_mr.png", width: 28, height: 28).then((value) => setState(() => _mrImage = value)); _model.init(state: widget.state); } @@ -95,110 +100,32 @@ class ToDosViewState extends State<ToDosView> with AutomaticKeepAliveClientMixin return ListView.builder( key: const Key('ToDosView'), itemBuilder: (context, index) { - return _buildListViewItem(data[index], index, context); + var toDo = data[index]; + return ToDoView( + toDo: toDo, + state: widget.state, + image: toDo.isNotIssue ? _mrImage : _issueImage, + isFirst: index == 0, + isLast: index == data.length - 1, + isSelected: _model.selectedItemId == toDo.id, + onItemTapped: () => onItemTaped(toDo, index), + onMarkDone: () async { + return _model.markItemAsDoneAt(toDo).then((value) { + widget.onItemMarkedDone?.call(); + _model.onRefresh(); + }); + }, + ); }, itemCount: data.length, ); } - Widget _buildListViewItem(ToDo todoItem, int index, BuildContext context) { - return Padding( - padding: const EdgeInsets.fromLTRB(12, 6, 12, 6), - child: _buildCard(todoItem, index, context), - ); - } - - InkWell _buildCard(ToDo todoItem, int index, BuildContext context) { - return InkWell( - key: const Key('ToDosViewItem'), - onTap: () => onItemTaped(todoItem, index), - child: Container( - constraints: const BoxConstraints(minHeight: 50), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(4)), - border: isDesktopLayout(context) && todoItem.id == _model.selectedItemId ? Border.all(color: const Color(0xFFFC6D26)) : null), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: <Widget>[ - Container( - margin: const EdgeInsets.only(top: 12, left: 12, right: 12), - child: Row( - children: [ - Avatar(avatarUrl: todoItem.author.avatarUrl, size: 24), - const SizedBox(width: 8), - Expanded( - child: Text( - todoItem.author.name, - style: const TextStyle(fontSize: 14, color: Color(0xFF03162F), fontWeight: FontWeight.w400), - overflow: TextOverflow.ellipsis, - ), - ), - Align( - alignment: Alignment.centerRight, - child: ChangeableTime.show(() => setState(() {}), false, todoItem.updatedAt, const Color(0xFF95979A), FontWeight.w400), - ) - ], - ), - ), - const SizedBox(height: 4), - Container( - margin: const EdgeInsets.only(left: 12, right: 12), - child: Text.rich(TextSpan( - children: [ - TextSpan(text: '${todoItem.actionName.actionName()} ', style: const TextStyle(fontSize: 14, color: Color(0xFF5C5963), fontWeight: FontWeight.w400)), - TextSpan( - text: todoItem.isNotIssue ? "${JiHuLocalizations.dictionary().mr} !${todoItem.target.iid}" : "${JiHuLocalizations.dictionary().issue} #${todoItem.target.iid}", - style: const TextStyle(fontSize: 14, color: Color(0xFF5C5963), fontWeight: FontWeight.w400), - ) - ], - )), - ), - const SizedBox(height: 4), - Container( - margin: const EdgeInsets.only(left: 12, right: 12), - child: Text.rich(TextSpan(text: todoItem.target.title, style: const TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.w400))), - ), - const SizedBox(height: 8), - Container(height: 1, margin: const EdgeInsets.only(left: 12, right: 12), child: const Divider(color: Color(0xFFEAEAEA), thickness: 1.0)), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - margin: const EdgeInsets.only(left: 12), - child: SvgPicture.asset( - "assets/images/project_icon.svg", - height: 16, - width: 16, - ), - ), - const SizedBox(width: 2), - Expanded(child: Text(todoItem.project.name, style: const TextStyle(fontSize: 12, color: Color(0xFF95979A)))), - widget.state == ToDoState.pending - ? InkWell( - child: Container( - padding: const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 12), - child: SvgPicture.asset( - "assets/images/mark_as_done.svg", - height: 16, - width: 16, - ), - ), - onTap: () { - if (widget.state != ToDoState.pending) return; - _model.markItemAsDoneAt(todoItem).then((value) { - widget.onItemMarkedDone?.call(); - _model.onRefresh(); - }); - }, - ) - : Container(padding: const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 12), child: const SizedBox(height: 16, width: 16)) - ], - ) - ], - ), - ), - ); + Future<ui.Image> _getAssetImage(String asset, {int? width, int? height}) async { + ByteData data = await DefaultAssetBundle.of(context).load(asset); + ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width, targetHeight: height); + var frameInfo = await codec.getNextFrame(); + return frameInfo.image; } void showConfirmToMarkTodoItemDoneAlert(ToDo todo) { diff --git a/lib/modules/to_dos/to_dos_view_model.dart b/lib/modules/to_dos/to_dos_view_model.dart index 93543d66d7cb9aa3737ccc6bb720528efcdf101f..6e3976fcc0dc9466dc39bae1f027c8b423b2fa12 100644 --- a/lib/modules/to_dos/to_dos_view_model.dart +++ b/lib/modules/to_dos/to_dos_view_model.dart @@ -23,10 +23,7 @@ class ToDosViewModel { init({required ToDoState state}) { _state = state; notifier = ValueNotifier(todos); - _refreshController ??= EasyRefreshController( - controlFinishRefresh: true, - controlFinishLoad: true, - ); + _refreshController ??= EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); ConnectionProvider().addListener(() { _refresh().then((value) { notifier.value = List.of(todos); @@ -88,6 +85,7 @@ class ToDosViewModel { ToDo newItem = await _markItemAsDone(todo.id); var result = todo.id == newItem.id && newItem.state == 'done'; if (result) todos.remove(todo); + notifier.value = List.of(todos); return result; } catch (e) { LogHelper.err("ToDosModel _markItemAsDone error", e); diff --git a/test/modules/to_dos/to_dos_page_test.dart b/test/modules/to_dos/to_dos_page_test.dart index 91ef2a34eed5476be904f9839f66bf2be3614763..4b8434fc4cebd3019cc9dd868ec317b5bd5d152f 100644 --- a/test/modules/to_dos/to_dos_page_test.dart +++ b/test/modules/to_dos/to_dos_page_test.dart @@ -547,4 +547,39 @@ void main() { locator.unregister(instance: issueDetailsModel); }); + + testWidgets('Should be able to mark to do item as done', (WidgetTester tester) async { + await setUpMobileBinding(tester); + ConnectionProvider().reset(Tester.jihuLabUser()); + + var pendingTodos = todoPendingResponseData.sublist(0, 1); + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => r.Response.of<List<dynamic>>(pendingTodos))); + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=done")).thenAnswer((_) => Future(() => r.Response.of<List<dynamic>>([]))); + when(client.get<List<dynamic>>("/api/v4/projects/72936/issues/6/discussions?page=1&per_page=50")).thenAnswer((_) => Future(() => r.Response.of<List<dynamic>>([]))); + when(client.post<Map<String, dynamic>>("/api/v4/todos/143191/mark_as_done", {})).thenAnswer((_) => Future(() => r.Response.of<Map<String, dynamic>>({"id": 143191, "state": "done"}))); + when(client.post('/api/graphql', getIssueDetailsGraphQLRequestBody('ultimate-plan/jihu-gitlab-app/demo-mr-test', 6))) + .thenAnswer((_) => Future(() => r.Response.of<dynamic>(issueDetailsGraphQLResponse))); + HttpClient.injectInstanceForTesting(client); + var provider = MockDiscussionProvider(); + when(provider.loadFromLocal()).thenAnswer((_) => Future(() => [Discussion('abc123', false, noteList)])); + await tester.pumpWidget(MultiProvider( + providers: [ChangeNotifierProvider(create: (context) => ConnectionProvider()), ChangeNotifierProvider(create: (context) => LocaleProvider())], + child: MaterialApp( + onGenerateRoute: onGenerateRoute, + home: const Scaffold(body: ToDosPage()), + localizationsDelegates: const [GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, S.delegate], + ))); + await tester.pumpAndSettle(); + expect(find.text("To Do"), findsOneWidget); + expect(find.text("demo mr test"), findsOneWidget); + var svgFinder = SvgFinder("assets/images/mark_as_done.svg"); + expect(svgFinder, findsOneWidget); + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => r.Response.of<List<dynamic>>([]))); + await tester.tap(svgFinder); + await tester.pumpAndSettle(const Duration(seconds: 1)); + verify(client.post<Map<String, dynamic>>("/api/v4/todos/143191/mark_as_done", {})).called(1); + expect(find.text("demo mr test"), findsNothing); + expect(svgFinder, findsNothing); + ConnectionProvider().fullReset(); + }); }