forked from eatvio/eatvio_app
311 lines
9.0 KiB
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);
|
|
});
|
|
});
|
|
}
|