import "package:flutter/widgets.dart"; import "package:flutter_test/flutter_test.dart"; import "package:garage_ui/garage_ui.dart"; import "support/test_scheme.dart"; // A feature lives INSIDE the field. It never gets to decide how tall the field // is - that is the density's job and nothing elses. // // It used to. The field wrapped its whole content row in textFieldPadding, so // anything you put in a feature slot got the padding stacked on top of its own // height. A trailing IconButton is a full control tall (23 at compact), so the // field came out at 23 + 12 = 35 against a controlHeight of 23 - half again as // tall as the plain field sat next to it. The packages OWN InputFeature.clear() // did it too, which is how it survived this long unnoticed. // // The existing feature-height probe in the app only ever passed a bare Text, so // none of this was covered. Future _height( WidgetTester tester, { required Density density, required List features, String text = "", }) async { final controller = TextEditingController(text: text); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: GarageTheme( data: ThemeData(colorScheme: testScheme, density: density), child: Center( child: SizedBox( width: 300, child: TextField(controller: controller, features: features), ), ), ), ), ); await tester.pump(); return tester.getSize(find.byType(TextField)).height; } void main() { for (final density in const [Density(), Density.normal()]) { final label = density.control.name; testWidgets("a trailing button doesnt grow the field ($label)", ( tester, ) async { final plain = await _height(tester, density: density, features: const []); final withButton = await _height( tester, density: density, features: [ InputFeature.trailing( IconButton( variance: ButtonVariance.ghost, density: ControlDensity.compact, icon: const Icon(LucideIcons.eye), onPressed: () {}, ), ), ], ); expect(plain, density.controlHeight); expect( withButton, plain, reason: "a button in a trailing slot pushed the field to $withButton " "against a plain $plain", ); }); testWidgets("every built-in feature holds controlHeight ($label)", ( tester, ) async { final cases = >{ "leading icon": const [InputFeature.leading(Icon(LucideIcons.lock))], "clear": [const InputFeature.clear()], "spinner": [const InputFeature.spinner()], "increment": [const InputFeature.incrementButton()], "scrub": [const InputFeature.scrub()], }; for (final entry in cases.entries) { // empty AND filled - the clear button only shows up once theres text. for (final text in const ["", "12"]) { final h = await _height( tester, density: density, features: entry.value, text: text, ); expect( tester.takeException(), isNull, reason: "${entry.key} threw laying out in the field", ); expect( h, density.controlHeight, reason: "${entry.key} made the field $h", ); } } }); } }