// 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/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets( 'default search field has a border radius', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField(), ), ), ); final BoxDecoration decoration = tester .widget<DecoratedBox>( find.descendant( of: find.byType(CupertinoSearchTextField), matching: find.byType(DecoratedBox), ), ) .decoration as BoxDecoration; expect( decoration.borderRadius, const BorderRadius.all(Radius.circular(9)), ); }, ); testWidgets( 'decoration overrides default background color', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( decoration: BoxDecoration(color: Color.fromARGB(1, 1, 1, 1)), ), ), ), ); final BoxDecoration decoration = tester .widget<DecoratedBox>( find.descendant( of: find.byType(CupertinoSearchTextField), matching: find.byType(DecoratedBox), ), ) .decoration as BoxDecoration; expect( decoration.color, const Color.fromARGB(1, 1, 1, 1), ); }, ); testWidgets( 'decoration overrides default border radius', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( decoration: BoxDecoration(borderRadius: BorderRadius.zero), ), ), ), ); final BoxDecoration decoration = tester .widget<DecoratedBox>( find.descendant( of: find.byType(CupertinoSearchTextField), matching: find.byType(DecoratedBox), ), ) .decoration as BoxDecoration; expect( decoration.borderRadius, BorderRadius.zero, ); }, ); testWidgets( 'text entries are padded by default', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: TextEditingController(text: 'initial'), ), ), ), ); expect( tester.getTopLeft(find.text('initial')) - tester.getTopLeft(find.byType(CupertinoSearchTextField)), const Offset(29.8, 8.0), ); }, ); testWidgets( 'can control text content via controller', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, ), ), ), ); controller.text = 'controller text'; await tester.pump(); expect(find.text('controller text'), findsOneWidget); controller.text = ''; await tester.pump(); expect(find.text('controller text'), findsNothing); }, ); testWidgets('placeholder color', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.dark), home: Center( child: CupertinoSearchTextField(), ), ), ); Text placeholder = tester.widget(find.text('Search')); expect(placeholder.style!.color!.value, CupertinoColors.systemGrey.darkColor.value); await tester.pumpAndSettle(); await tester.pumpWidget( const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.light), home: Center( child: CupertinoSearchTextField(), ), ), ); placeholder = tester.widget(find.text('Search')); expect(placeholder.style!.color!.value, CupertinoColors.systemGrey.color.value); }); testWidgets( "placeholderStyle modifies placeholder's style and doesn't affect text's style", (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( placeholder: 'placeholder', style: TextStyle( color: Color(0x00FFFFFF), fontWeight: FontWeight.w300, ), placeholderStyle: TextStyle( color: Color(0xAAFFFFFF), fontWeight: FontWeight.w600, ), ), ), ), ); final Text placeholder = tester.widget(find.text('placeholder')); expect(placeholder.style!.color, const Color(0xAAFFFFFF)); expect(placeholder.style!.fontWeight, FontWeight.w600); await tester.enterText(find.byType(CupertinoSearchTextField), 'input'); await tester.pump(); final EditableText inputText = tester.widget(find.text('input')); expect(inputText.style.color, const Color(0x00FFFFFF)); expect(inputText.style.fontWeight, FontWeight.w300); }, ); testWidgets( 'prefix widget is in front of the text', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: TextEditingController(text: 'input'), ), ), ), ); expect( tester.getTopRight(find.byIcon(CupertinoIcons.search)).dx + 3.8, tester.getTopLeft(find.byType(EditableText)).dx, ); expect( tester.getTopLeft(find.byType(EditableText)).dx, tester.getTopLeft(find.byType(CupertinoSearchTextField)).dx + tester.getSize(find.byIcon(CupertinoIcons.search)).width + 9.8, ); }, ); testWidgets( 'suffix widget is after the text', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: TextEditingController(text: 'Hi'), ), ), ), ); expect( tester.getTopRight(find.byType(EditableText)).dx + 5.0, tester.getTopLeft(find.byIcon(CupertinoIcons.xmark_circle_fill)).dx, ); expect( tester.getTopRight(find.byType(EditableText)).dx, tester.getTopRight(find.byType(CupertinoSearchTextField)).dx - tester .getSize(find.byIcon(CupertinoIcons.xmark_circle_fill)) .width - 10.0, ); }, ); testWidgets('prefix widget visibility', (WidgetTester tester) async { const Key prefixIcon = Key('prefix'); await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( prefixIcon: SizedBox( key: prefixIcon, width: 50, height: 50, ), ), ), ), ); expect(find.byIcon(CupertinoIcons.search), findsNothing); expect(find.byKey(prefixIcon), findsOneWidget); await tester.enterText( find.byType(CupertinoSearchTextField), 'text input'); await tester.pump(); expect(find.text('text input'), findsOneWidget); expect(find.byIcon(CupertinoIcons.search), findsNothing); expect(find.byKey(prefixIcon), findsOneWidget); }); testWidgets( 'suffix widget respects visibility mode', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( suffixMode: OverlayVisibilityMode.notEditing, ), ), ), ); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsOneWidget); await tester.enterText(find.byType(CupertinoSearchTextField), 'text input'); await tester.pump(); expect(find.text('text input'), findsOneWidget); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsNothing); }, ); testWidgets( 'clear button shows with right visibility mode', (WidgetTester tester) async { TextEditingController controller = TextEditingController(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, placeholder: 'placeholder does not affect clear button', ), ), ), ); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsNothing); await tester.enterText(find.byType(CupertinoSearchTextField), 'text input'); await tester.pump(); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsOneWidget); expect(find.text('text input'), findsOneWidget); controller = TextEditingController(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, placeholder: 'placeholder does not affect clear button', suffixMode: OverlayVisibilityMode.notEditing, ), ), ), ); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsOneWidget); controller.text = 'input'; await tester.pump(); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsNothing); }, ); testWidgets( 'clear button removes text', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, ), ), ), ); controller.text = 'text entry'; await tester.pump(); await tester.tap(find.byIcon(CupertinoIcons.xmark_circle_fill)); await tester.pump(); expect(controller.text, ''); expect(find.text('Search'), findsOneWidget); expect(find.text('text entry'), findsNothing); expect(find.byIcon(CupertinoIcons.xmark_circle_fill), findsNothing); }, ); testWidgets( 'tapping clear button also calls onChanged when text not empty', (WidgetTester tester) async { String value = 'text entry'; final TextEditingController controller = TextEditingController(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, placeholder: 'placeholder', onChanged: (String newValue) => value = newValue, ), ), ), ); controller.text = value; await tester.pump(); await tester.tap(find.byIcon(CupertinoIcons.xmark_circle_fill)); await tester.pump(); expect(controller.text, isEmpty); expect(find.text('text entry'), findsNothing); expect(value, isEmpty); }, ); testWidgets( 'RTL puts attachments to the right places', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Directionality( textDirection: TextDirection.rtl, child: Center( child: CupertinoSearchTextField( suffixMode: OverlayVisibilityMode.always, ), ), ), ), ); expect( tester.getTopLeft(find.byIcon(CupertinoIcons.search)).dx, 800.0 - 26.0, ); expect( tester.getTopRight(find.byIcon(CupertinoIcons.xmark_circle_fill)).dx, 25.0, ); }, ); testWidgets( 'Can modify prefix and suffix insets', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( suffixMode: OverlayVisibilityMode.always, prefixInsets: EdgeInsets.zero, suffixInsets: EdgeInsets.zero, ), ), ), ); expect( tester.getTopLeft(find.byIcon(CupertinoIcons.search)).dx, 0.0, ); expect( tester.getTopRight(find.byIcon(CupertinoIcons.xmark_circle_fill)).dx, 800.0, ); }, ); testWidgets( 'custom suffix onTap overrides default clearing behavior', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'Text'); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( controller: controller, onSuffixTap: () {}, ), ), ), ); await tester.pump(); await tester.tap(find.byIcon(CupertinoIcons.xmark_circle_fill)); await tester.pump(); expect(controller.text, isNotEmpty); expect(find.text('Text'), findsOneWidget); }, ); testWidgets('onTap is properly forwarded to the inner text field', (WidgetTester tester) async { int onTapCallCount = 0; // onTap can be null. await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField(), ), ), ); // onTap callback is called if not null. await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( onTap: () { onTapCallCount++; }, ), ), ), ); expect(onTapCallCount, 0); await tester.tap(find.byType(CupertinoTextField)); expect(onTapCallCount, 1); }); testWidgets('autocorrect is properly forwarded to the inner text field', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( autocorrect: false, ), ), ), ); final CupertinoTextField textField = tester.widget(find.byType(CupertinoTextField)); expect(textField.autocorrect, false); }); testWidgets('enabled is properly forwarded to the inner text field', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField( enabled: false, ), ), ), ); final CupertinoTextField textField = tester.widget(find.byType(CupertinoTextField)); expect(textField.enabled, false); }); testWidgets('textInputAction is set to TextInputAction.search by default', (WidgetTester tester) async { await tester.pumpWidget( const CupertinoApp( home: Center( child: CupertinoSearchTextField(), ), ), ); final CupertinoTextField textField = tester.widget(find.byType(CupertinoTextField)); expect(textField.textInputAction, TextInputAction.search); }); testWidgets('autofocus:true gives focus to the widget', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoSearchTextField( focusNode: focusNode, autofocus: true, ), ), ), ); expect(focusNode.hasFocus, isTrue); }); }