// 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. import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { test('debugChildrenHaveDuplicateKeys control test', () { const Key key = Key('key'); final List<Widget> children = <Widget>[ Container(key: key), Container(key: key), ]; final Widget widget = Flex( direction: Axis.vertical, children: children, ); late FlutterError error; try { debugChildrenHaveDuplicateKeys(widget, children); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' ' Duplicate keys found.\n' ' If multiple keyed nodes exist as children of another node, they\n' ' must have unique keys.\n' ' Flex(direction: vertical, mainAxisAlignment: start,\n' ' crossAxisAlignment: center) has multiple children with key\n' " [<'key'>].\n", ), ); } }); test('debugItemsHaveDuplicateKeys control test', () { const Key key = Key('key'); final List<Widget> items = <Widget>[ Container(key: key), Container(key: key), ]; late FlutterError error; try { debugItemsHaveDuplicateKeys(items); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' " Duplicate key found: [<'key'>].\n", ), ); } }); testWidgets('debugCheckHasTable control test', (WidgetTester tester) async { await tester.pumpWidget( Builder( builder: (BuildContext context) { late FlutterError error; try { debugCheckHasTable(context); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect(error.diagnostics.length, 4); expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>()); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' ' No Table widget found.\n' ' Builder widgets require a Table widget ancestor.\n' ' The specific widget that could not find a Table ancestor was:\n' ' Builder\n' ' The ownership chain for the affected widget is: "Builder ←\n' ' [root]"\n', ), ); } return Container(); }, ), ); }); testWidgets('debugCheckHasMediaQuery control test', (WidgetTester tester) async { await tester.pumpWidget( Builder( builder: (BuildContext context) { late FlutterError error; try { debugCheckHasMediaQuery(context); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect(error.diagnostics.length, 5); expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>()); expect(error.diagnostics.last.level, DiagnosticLevel.hint); expect( error.diagnostics.last.toStringDeep(), equalsIgnoringHashCodes( 'No MediaQuery ancestor could be found starting from the context\n' 'that was passed to MediaQuery.of(). This can happen because you\n' 'have not added a WidgetsApp, CupertinoApp, or MaterialApp widget\n' '(those widgets introduce a MediaQuery), or it can happen if the\n' 'context you use comes from a widget above those widgets.\n', ), ); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' ' No MediaQuery widget ancestor found.\n' ' Builder widgets require a MediaQuery widget ancestor.\n' ' The specific widget that could not find a MediaQuery ancestor\n' ' was:\n' ' Builder\n' ' The ownership chain for the affected widget is: "Builder ←\n' ' [root]"\n' ' No MediaQuery ancestor could be found starting from the context\n' ' that was passed to MediaQuery.of(). This can happen because you\n' ' have not added a WidgetsApp, CupertinoApp, or MaterialApp widget\n' ' (those widgets introduce a MediaQuery), or it can happen if the\n' ' context you use comes from a widget above those widgets.\n', ), ); } return Container(); }, ), ); }); test('debugWidgetBuilderValue control test', () { final Widget widget = Container(); FlutterError? error; try { debugWidgetBuilderValue(widget, null); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect(error!.diagnostics.length, 4); expect(error.diagnostics[1], isA<DiagnosticsProperty<Widget>>()); expect(error.diagnostics[1].style, DiagnosticsTreeStyle.errorProperty); expect( error.diagnostics[1].toStringDeep(), equalsIgnoringHashCodes( 'The offending widget is:\n' ' Container\n', ), ); expect(error.diagnostics[2].level, DiagnosticLevel.info); expect(error.diagnostics[3].level, DiagnosticLevel.hint); expect( error.diagnostics[3].toStringDeep(), equalsIgnoringHashCodes( 'To return an empty space that causes the building widget to fill\n' 'available room, return "Container()". To return an empty space\n' 'that takes as little room as possible, return "Container(width:\n' '0.0, height: 0.0)".\n', ), ); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' ' A build function returned null.\n' ' The offending widget is:\n' ' Container\n' ' Build functions must never return null.\n' ' To return an empty space that causes the building widget to fill\n' ' available room, return "Container()". To return an empty space\n' ' that takes as little room as possible, return "Container(width:\n' ' 0.0, height: 0.0)".\n', ), ); error = null; } try { debugWidgetBuilderValue(widget, widget); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect(error!.diagnostics.length, 3); expect(error.diagnostics[1], isA<DiagnosticsProperty<Widget>>()); expect(error.diagnostics[1].style, DiagnosticsTreeStyle.errorProperty); expect( error.diagnostics[1].toStringDeep(), equalsIgnoringHashCodes( 'The offending widget is:\n' ' Container\n', ), ); expect( error.toStringDeep(), equalsIgnoringHashCodes( 'FlutterError\n' ' A build function returned context.widget.\n' ' The offending widget is:\n' ' Container\n' " Build functions must never return their BuildContext parameter's\n" ' widget or a child that contains "context.widget". Doing so\n' ' introduces a loop in the widget tree that can cause the app to\n' ' crash.\n', ), ); } }); testWidgets('debugCheckHasWidgetsLocalizations throws', (WidgetTester tester) async { final GlobalKey noLocalizationsAvailable = GlobalKey(); final GlobalKey localizationsAvailable = GlobalKey(); await tester.pumpWidget( Container( key: noLocalizationsAvailable, child: WidgetsApp( builder: (BuildContext context, Widget? child) { return Container( key: localizationsAvailable, ); }, color: const Color(0xFF4CAF50), ), ), ); expect( () => debugCheckHasWidgetsLocalizations(noLocalizationsAvailable.currentContext!), throwsA(isAssertionError.having( (AssertionError e) => e.message, 'message', contains('No WidgetsLocalizations found'), )), ); expect(debugCheckHasWidgetsLocalizations(localizationsAvailable.currentContext!), isTrue); }); test('debugAssertAllWidgetVarsUnset', () { debugHighlightDeprecatedWidgets = true; late FlutterError error; try { debugAssertAllWidgetVarsUnset('The value of a widget debug variable was changed by the test.'); } on FlutterError catch (e) { error = e; } finally { expect(error, isNotNull); expect(error.diagnostics.length, 1); expect( error.toStringDeep(), 'FlutterError\n' ' The value of a widget debug variable was changed by the test.\n', ); } debugHighlightDeprecatedWidgets = false; }); }