Files
eatvio_app/test/features/payment/consultation_payment_flow_test.dart

311 lines
9.0 KiB
Dart

import 'package:eatvio_app/features/payment/models/promo_apply_response.dart';
import 'package:eatvio_app/features/payment/models/promo_code_model.dart';
import 'package:eatvio_app/features/payment/models/promo_validate_response.dart';
import 'package:eatvio_app/features/payment/presentation/payment_flow.dart';
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_modal.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
Widget createTestApp() {
return const MaterialApp(
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [Locale('ru')],
home: Scaffold(body: SizedBox()),
);
}
group('showConsultationPaymentFlow', () {
late bool gateCalled;
late bool? gateResult;
late bool formCalled;
late PromoCodeResult? formResult;
late PromoCodeModel? formInitialPromo;
late bool sbpCalled;
late int sbpPlanId;
late int validateCalls;
late int applyCalls;
late String? lastValidatedCode;
late String? lastAppliedCode;
late PromoValidateResponse validateResponse;
late PromoApplyResponse applyResponse;
late bool applyThrows;
late PromoCodeModel? listAppliedResult;
Future<bool?> mockGate(BuildContext context) async {
gateCalled = true;
return gateResult;
}
Future<PromoCodeResult?> mockForm(
BuildContext context, {
Future<PromoValidation> Function(String)? onValidate,
Future<bool> Function()? onRemove,
PromoCodeModel? initialPromo,
}) async {
formCalled = true;
formInitialPromo = initialPromo;
if (formResult?.hasPromoCode == true &&
!formResult!.alreadyApplied &&
onValidate != null) {
final validation = await onValidate(formResult!.code!);
if (!validation.isValid) {
return null;
}
return PromoCodeResult(
code: formResult!.code,
discountPercent: validation.discountPercent,
discountAmountRub: validation.discountAmountRub,
originalAmountRub: validation.originalAmountRub,
finalAmountRub: validation.finalAmountRub,
);
}
return formResult;
}
Future<void> mockSbp(BuildContext context, {required int planId}) async {
sbpCalled = true;
sbpPlanId = planId;
}
Future<PromoValidateResponse> mockValidate({
required int planId,
required String promoCode,
}) async {
validateCalls++;
lastValidatedCode = promoCode;
return validateResponse;
}
Future<PromoApplyResponse> mockApply({
required int planId,
required String promoCode,
}) async {
applyCalls++;
lastAppliedCode = promoCode;
if (applyThrows) {
throw Exception('network');
}
return applyResponse;
}
Future<PromoCodeModel?> mockListApplied(int planId) async {
return listAppliedResult;
}
Future<bool> mockRemove(int planId) async {
return true;
}
setUp(() {
gateCalled = false;
gateResult = null;
formCalled = false;
formResult = null;
formInitialPromo = null;
sbpCalled = false;
sbpPlanId = 0;
validateCalls = 0;
applyCalls = 0;
lastValidatedCode = null;
lastAppliedCode = null;
listAppliedResult = null;
validateResponse = const PromoValidateResponse(
valid: true,
promoCode: 'TEST123',
discountPercent: 5,
discountAmountRub: 60,
originalAmountRub: 1200,
finalAmountRub: 1140,
);
applyResponse = const PromoApplyResponse(success: true);
applyThrows = false;
});
Future<void> runFlow(WidgetTester tester) {
return showConsultationPaymentFlow(
tester.element(find.byType(SizedBox)),
planId: 42,
showGate: mockGate,
showForm: mockForm,
showSbp: mockSbp,
validatePromo: mockValidate,
applyPromo: mockApply,
listApplied: mockListApplied,
removePromo: mockRemove,
);
}
testWidgets('Нет → вызывает SbpBanks (не форму)', (tester) async {
gateResult = false;
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isTrue);
expect(formCalled, isFalse);
expect(sbpCalled, isTrue);
expect(sbpPlanId, 42);
expect(validateCalls, 0);
expect(applyCalls, 0);
});
testWidgets('Да → форма → Пропустить → вызывает SbpBanks', (tester) async {
gateResult = true;
formResult = const PromoCodeResult();
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isTrue);
expect(formCalled, isTrue);
expect(sbpCalled, isTrue);
expect(sbpPlanId, 42);
expect(applyCalls, 0);
});
testWidgets('Да → форма отменена (null) → Sbp не вызывается', (tester) async {
gateResult = true;
formResult = null;
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isTrue);
expect(formCalled, isTrue);
expect(sbpCalled, isFalse);
});
testWidgets('гейт отменён (null) → ничего не вызывается', (tester) async {
gateResult = null;
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isTrue);
expect(formCalled, isFalse);
expect(sbpCalled, isFalse);
});
testWidgets('Да → код → validate + apply → SbpBanks', (tester) async {
gateResult = true;
formResult = const PromoCodeResult(code: 'TEST123');
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(validateCalls, 1);
expect(lastValidatedCode, 'TEST123');
expect(applyCalls, 1);
expect(lastAppliedCode, 'TEST123');
expect(sbpCalled, isTrue);
expect(sbpPlanId, 42);
});
testWidgets('Да → код → apply fail → Sbp не вызывается', (tester) async {
gateResult = true;
formResult = const PromoCodeResult(code: 'BAD');
applyResponse = const PromoApplyResponse(
success: false,
error: 'Промокод уже использован',
);
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(applyCalls, 1);
expect(sbpCalled, isFalse);
});
testWidgets('applied rehydrate → Continue → SBP без apply и без гейта', (tester) async {
listAppliedResult = const PromoCodeModel(
id: 1,
chosenPlanId: 42,
promoCode: 'SAVE5',
discountPercent: 5,
discountAmountRub: 60,
state: 'applied',
);
formResult = const PromoCodeResult(
code: 'SAVE5',
discountPercent: 5,
discountAmountRub: 60,
alreadyApplied: true,
);
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isFalse);
expect(formCalled, isTrue);
expect(formInitialPromo?.promoCode, 'SAVE5');
expect(applyCalls, 0);
expect(sbpCalled, isTrue);
});
testWidgets('list пустой → идёт через гейт', (tester) async {
listAppliedResult = null;
gateResult = false;
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isTrue);
expect(formCalled, isFalse);
expect(sbpCalled, isTrue);
});
testWidgets('rehydrate → Remove → новый код → apply → SBP', (tester) async {
listAppliedResult = const PromoCodeModel(
id: 1,
chosenPlanId: 42,
promoCode: 'OLD',
discountPercent: 5,
discountAmountRub: 60,
state: 'applied',
);
formResult = const PromoCodeResult(code: 'NEW123');
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isFalse);
expect(formCalled, isTrue);
expect(validateCalls, 1);
expect(applyCalls, 1);
expect(lastAppliedCode, 'NEW123');
expect(sbpCalled, isTrue);
});
testWidgets('rehydrate → Remove → Skip → SBP без apply', (tester) async {
listAppliedResult = const PromoCodeModel(
id: 1,
chosenPlanId: 42,
promoCode: 'OLD',
discountPercent: 5,
discountAmountRub: 60,
state: 'applied',
);
formResult = const PromoCodeResult();
await tester.pumpWidget(createTestApp());
await runFlow(tester);
expect(gateCalled, isFalse);
expect(formCalled, isTrue);
expect(applyCalls, 0);
expect(sbpCalled, isTrue);
});
});
}