mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at ft/monetization 108 lines 3.7 kB view raw
1import 'package:bloc_test/bloc_test.dart'; 2import 'package:flutter/material.dart'; 3import 'package:flutter_bloc/flutter_bloc.dart'; 4import 'package:flutter_test/flutter_test.dart'; 5import 'package:in_app_purchase/in_app_purchase.dart'; 6import 'package:lazurite/features/tips/cubit/tip_cubit.dart'; 7import 'package:lazurite/features/tips/cubit/tip_state.dart'; 8import 'package:lazurite/features/tips/presentation/tip_sheet.dart'; 9import 'package:mocktail/mocktail.dart'; 10 11class MockTipCubit extends MockCubit<TipState> implements TipCubit {} 12 13void main() { 14 late MockTipCubit cubit; 15 late ProductDetails coffee; 16 late ProductDetails latte; 17 18 setUp(() { 19 cubit = MockTipCubit(); 20 coffee = ProductDetails( 21 id: 'tip_coffee', 22 title: 'Coffee', 23 description: 'Small tip', 24 price: r'$1.99', 25 rawPrice: 1.99, 26 currencyCode: 'USD', 27 currencySymbol: r'$', 28 ); 29 latte = ProductDetails( 30 id: 'tip_latte', 31 title: 'Latte', 32 description: 'Large tip', 33 price: r'$4.99', 34 rawPrice: 4.99, 35 currencyCode: 'USD', 36 currencySymbol: r'$', 37 ); 38 registerFallbackValue(coffee); 39 40 when(() => cubit.loadProducts()).thenAnswer((_) async {}); 41 when(() => cubit.purchaseTip(any())).thenAnswer((_) async {}); 42 }); 43 44 Widget buildSubject(TipState state) { 45 when(() => cubit.state).thenReturn(state); 46 whenListen(cubit, const Stream<TipState>.empty(), initialState: state); 47 48 return MaterialApp( 49 home: Scaffold( 50 body: BlocProvider<TipCubit>.value(value: cubit, child: const TipSheet()), 51 ), 52 ); 53 } 54 55 testWidgets('renders loading skeletons while products load', (tester) async { 56 await tester.pumpWidget(buildSubject(const TipState(storeStatus: TipStoreStatus.loading))); 57 58 expect(find.byKey(const Key('tip_skeleton_0')), findsOneWidget); 59 expect(find.byKey(const Key('tip_skeleton_1')), findsOneWidget); 60 }); 61 62 testWidgets('renders products with localized prices and ads note', (tester) async { 63 await tester.pumpWidget( 64 buildSubject(TipState(storeStatus: TipStoreStatus.available, products: [latte, coffee], adsRemoved: false)), 65 ); 66 67 expect(find.text('Coffee'), findsOneWidget); 68 expect(find.text('Latte'), findsOneWidget); 69 expect(find.text(r'$1.99'), findsOneWidget); 70 expect(find.text(r'$4.99'), findsOneWidget); 71 expect(find.text('Your first tip removes ads forever.'), findsOneWidget); 72 }); 73 74 testWidgets('renders thank-you banner when ads are already removed', (tester) async { 75 await tester.pumpWidget( 76 buildSubject(TipState(storeStatus: TipStoreStatus.available, products: [coffee, latte], adsRemoved: true)), 77 ); 78 79 expect(find.text('Ads removed — thanks for your support!'), findsOneWidget); 80 expect(find.text('Your first tip removes ads forever.'), findsNothing); 81 }); 82 83 testWidgets('renders store unavailable state and retries', (tester) async { 84 await tester.pumpWidget(buildSubject(const TipState(storeStatus: TipStoreStatus.unavailable))); 85 86 expect(find.text('Store unavailable'), findsOneWidget); 87 88 await tester.tap(find.text('Retry')); 89 await tester.pump(); 90 91 verify(() => cubit.loadProducts()).called(1); 92 }); 93 94 testWidgets('shows pending spinner and disables other purchases', (tester) async { 95 await tester.pumpWidget( 96 buildSubject( 97 TipState( 98 storeStatus: TipStoreStatus.available, 99 products: [coffee, latte], 100 purchaseStatus: TipPurchaseStatus.pending, 101 ), 102 ), 103 ); 104 105 expect(find.byType(CircularProgressIndicator), findsNWidgets(2)); 106 expect(find.byType(FilledButton), findsNothing); 107 }); 108}