import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:jiu_client/core/theme/themes.dart'; import 'package:jiu_client/widgets/ds/grid_combo_cell.dart'; import 'package:jiu_client/widgets/searchable_option_field.dart' show OptionItem; void main() { testWidgets('popupSearch grid combo: tapping a row fills selection', (tester) async { int? picked; int selectedId = 0; final fn = FocusNode(); final opts = [ OptionItem(id: 1, name: '茅台飞天'), OptionItem(id: 2, name: '五粮液'), ]; await tester.pumpWidget(MaterialApp( theme: buildTheme('a'), home: Scaffold( body: StatefulBuilder(builder: (context, setState) { return Center( child: SizedBox( width: 240, child: GridComboCell( focusNode: fn, cell: true, popupSearch: true, hint: '选择商品名称', options: opts, selectedId: selectedId == 0 ? null : selectedId, onChanged: (v) => setState(() { picked = v; selectedId = v ?? 0; }), ), ), ); }), ), )); fn.requestFocus(); await tester.pump(); await tester.pump(const Duration(milliseconds: 50)); expect(find.text('五粮液'), findsOneWidget); // Realistic pointer: down, drain microtasks/frames (focus-loss handlers run // here), THEN up — mirrors real input where the search field defocuses on // pointer-down before the row's onTap fires on pointer-up. final g = await tester.startGesture(tester.getCenter(find.text('五粮液'))); await tester.pump(); await tester.pump(const Duration(milliseconds: 50)); await g.up(); await tester.pump(); await tester.pump(const Duration(milliseconds: 50)); expect(picked, 2, reason: 'onChanged should fire with id=2 after tapping row'); }); }