Files
biz_xfj_cloud/test/pages/terminal/query_method_panel_test.dart
T
2026-09-07 12:53:05 +08:00

286 lines
10 KiB
Dart

import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:forui/forui.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
import 'package:pj_supermarket_cloud/pages/terminal/menu_query/controller.dart';
import 'package:pj_supermarket_cloud/pages/terminal/menu_query/widgets/query_method_panel.dart';
void main() {
testWidgets('手动模式保留开始按钮,自动模式改为饭卡等待状态且仍可切换方式', (tester) async {
await tester.binding.setSurfaceSize(const Size(1920, 1080));
addTearDown(() => tester.binding.setSurfaceSize(null));
var startCalls = 0;
ReservationRecognitionMethod? selectedMethod;
final mealSlotController = FRadioSelectGroupController<String>(value: '1');
addTearDown(mealSlotController.dispose);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: ReservationMenuQueryStatus.idle,
mealSlotController: mealSlotController,
autoRecognitionEnabled: false,
continuousRecognitionEnabled: false,
quickVerificationEnabled: false,
allowManualMealPeriodChange: true,
onSelect: (method) => selectedMethod = method,
onStart: () => startCalls++,
),
),
);
expect(find.text('开始识别'), findsOneWidget);
await tester.tap(find.text('开始识别'));
await tester.pump(const Duration(milliseconds: 250));
expect(startCalls, 1);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: ReservationMenuQueryStatus.recognizing,
mealSlotController: mealSlotController,
autoRecognitionEnabled: true,
continuousRecognitionEnabled: true,
quickVerificationEnabled: false,
allowManualMealPeriodChange: true,
onSelect: (method) => selectedMethod = method,
onStart: () => startCalls++,
),
),
);
expect(find.text('开始识别'), findsNothing);
expect(find.text('持续等待饭卡'), findsOneWidget);
await tester.tap(find.text('人脸查询'));
await tester.pump(const Duration(milliseconds: 250));
expect(selectedMethod, ReservationRecognitionMethod.face);
expect(startCalls, 1);
expect(tester.takeException(), isNull);
});
testWidgets('餐段只由快速核销开关控制,不受持续自动识别影响', (tester) async {
await tester.binding.setSurfaceSize(const Size(1920, 1080));
addTearDown(() => tester.binding.setSurfaceSize(null));
final mealSlotController = FRadioSelectGroupController<String>(value: '2');
addTearDown(mealSlotController.dispose);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: ReservationMenuQueryStatus.recognizing,
mealSlotController: mealSlotController,
autoRecognitionEnabled: true,
continuousRecognitionEnabled: true,
quickVerificationEnabled: false,
allowManualMealPeriodChange: true,
onSelect: (_) {},
onStart: () {},
),
),
);
expect(
find.byKey(const ValueKey('reservation-meal-slot-trigger')),
findsNothing,
);
expect(find.text('餐段'), findsNothing);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: ReservationMenuQueryStatus.recognizing,
mealSlotController: mealSlotController,
autoRecognitionEnabled: true,
continuousRecognitionEnabled: false,
quickVerificationEnabled: true,
allowManualMealPeriodChange: true,
onSelect: (_) {},
onStart: () {},
),
),
);
expect(
find.byKey(const ValueKey('reservation-meal-slot-trigger')),
findsOneWidget,
);
expect(find.text('餐段'), findsOneWidget);
expect(find.text('中餐'), findsOneWidget);
expect(tester.takeException(), isNull);
});
testWidgets('餐段默认仅显示当前值,点开后可选择且查询中禁用', (tester) async {
await tester.binding.setSurfaceSize(const Size(1920, 1080));
addTearDown(() => tester.binding.setSurfaceSize(null));
var selectedMealSlot = ReservationMealSlot.morning;
var status = ReservationMenuQueryStatus.idle;
final mealSlotController = FRadioSelectGroupController<String>(value: '1');
mealSlotController.addValueListener((selectedCodes) {
final selectedCode = selectedCodes.firstOrNull;
selectedMealSlot = ReservationMealSlot.querySelectableValues.firstWhere(
(mealSlot) => mealSlot.code == selectedCode,
);
});
addTearDown(mealSlotController.dispose);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: status,
mealSlotController: mealSlotController,
autoRecognitionEnabled: false,
continuousRecognitionEnabled: true,
quickVerificationEnabled: true,
allowManualMealPeriodChange: true,
onSelect: (_) {},
onStart: () {},
),
),
);
expect(find.byType(FSelectMenuTile<String>), findsOneWidget);
expect(find.text('餐段'), findsOneWidget);
expect(find.text('早餐'), findsOneWidget);
expect(find.text('中餐'), findsNothing);
expect(find.text('晚餐'), findsNothing);
final trigger = find.byKey(
const ValueKey('reservation-meal-slot-trigger'),
);
expect(tester.getSize(trigger).height, greaterThanOrEqualTo(48));
expect(tester.widget<FSelectMenuTile<String>>(trigger).enabled, isTrue);
await tester.tap(trigger);
await tester.pumpAndSettle();
final noonOption = find.byKey(
const ValueKey('meal-slot-option-2'),
);
expect(tester.getSize(noonOption).height, greaterThanOrEqualTo(48));
await tester.tap(noonOption);
await tester.pumpAndSettle();
expect(selectedMealSlot, ReservationMealSlot.noon);
expect(find.text('早餐'), findsNothing);
expect(find.text('中餐'), findsOneWidget);
status = ReservationMenuQueryStatus.recognizing;
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: status,
mealSlotController: mealSlotController,
autoRecognitionEnabled: false,
continuousRecognitionEnabled: true,
quickVerificationEnabled: true,
allowManualMealPeriodChange: true,
onSelect: (_) {},
onStart: () {},
),
),
);
await tester.tap(trigger);
await tester.pumpAndSettle();
final nightOption = find.byKey(
const ValueKey('meal-slot-option-3'),
);
await tester.tap(nightOption);
await tester.pumpAndSettle();
expect(selectedMealSlot, ReservationMealSlot.night);
status = ReservationMenuQueryStatus.querying;
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: status,
mealSlotController: mealSlotController,
autoRecognitionEnabled: false,
continuousRecognitionEnabled: true,
quickVerificationEnabled: true,
allowManualMealPeriodChange: true,
onSelect: (_) {},
onStart: () {},
),
),
);
final disabledTrigger = find.byKey(
const ValueKey('reservation-meal-slot-trigger'),
);
expect(
tester.widget<FSelectMenuTile<String>>(disabledTrigger).enabled,
isFalse,
);
await tester.tap(disabledTrigger);
await tester.pump();
expect(selectedMealSlot, ReservationMealSlot.night);
expect(
find.byKey(const ValueKey('meal-slot-option-1')),
findsNothing,
);
expect(tester.takeException(), isNull);
});
testWidgets('禁止主动修改餐段时选择器禁用且无法打开餐段菜单', (tester) async {
await tester.binding.setSurfaceSize(const Size(1920, 1080));
addTearDown(() => tester.binding.setSurfaceSize(null));
final mealSlotController = FRadioSelectGroupController<String>(value: '1');
addTearDown(mealSlotController.dispose);
await tester.pumpWidget(
_testApp(
QueryMethodPanel(
methods: ReservationRecognitionMethod.values,
selected: ReservationRecognitionMethod.card,
status: ReservationMenuQueryStatus.idle,
mealSlotController: mealSlotController,
autoRecognitionEnabled: false,
continuousRecognitionEnabled: true,
quickVerificationEnabled: true,
allowManualMealPeriodChange: false,
onSelect: (_) {},
onStart: () {},
),
),
);
final trigger = find.byKey(
const ValueKey('reservation-meal-slot-trigger'),
);
expect(find.text('早餐'), findsOneWidget);
expect(tester.widget<FSelectMenuTile<String>>(trigger).enabled, isFalse);
await tester.tap(trigger);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('meal-slot-option-2')), findsNothing);
expect(mealSlotController.value, {'1'});
expect(tester.takeException(), isNull);
});
}
/// 构建与终端设计基准一致的主题和尺寸环境,只验证本组件的可见状态与点击链路。
Widget _testApp(Widget child) {
return ScreenUtilInit(
designSize: const Size(1920, 1080),
builder: (_, __) => MaterialApp(
home: FTheme(
data: FThemes.zinc.light,
child: Scaffold(body: child),
),
),
);
}