Skip to content

Commit

Permalink
Merge pull request #8 from VGVentures/feat/generate-random-data
Browse files Browse the repository at this point in the history
feat: generate random data
  • Loading branch information
jsgalarraga authored Aug 6, 2024
2 parents f4386d9 + 1f0ca69 commit beed289
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 39 deletions.
5 changes: 5 additions & 0 deletions lib/demo/widgets/app_one.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:financial_dashboard/demo/demo.dart';
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:financial_dashboard/ui/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class AppOne extends StatelessWidget {
const AppOne({super.key});
Expand All @@ -12,6 +14,9 @@ class AppOne extends StatelessWidget {
final colorScheme = theme.colorScheme;

return AppScaffold(
onRefresh: () async {
context.read<FinancialDataBloc>().add(const FinancialDataRequested());
},
predictionChart: Card(
child: Padding(
padding: const EdgeInsets.symmetric(
Expand Down
5 changes: 5 additions & 0 deletions lib/demo/widgets/app_three.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:financial_dashboard/demo/demo.dart';
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:financial_dashboard/ui/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class AppThree extends StatelessWidget {
const AppThree({super.key});
Expand All @@ -12,6 +14,9 @@ class AppThree extends StatelessWidget {
final colorScheme = theme.colorScheme;

return AppScaffold(
onRefresh: () async {
context.read<FinancialDataBloc>().add(const FinancialDataRequested());
},
showTitleInAppBar: true,
predictionChart: Card(
child: Padding(
Expand Down
5 changes: 5 additions & 0 deletions lib/demo/widgets/app_two.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:financial_dashboard/demo/demo.dart';
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:financial_dashboard/ui/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class AppTwo extends StatelessWidget {
const AppTwo({super.key});
Expand All @@ -13,6 +15,9 @@ class AppTwo extends StatelessWidget {
const spacing = AppSpacing.xxlg * 2;

return AppScaffold(
onRefresh: () async {
context.read<FinancialDataBloc>().add(const FinancialDataRequested());
},
extendBodyBehindAppBar: true,
titlePadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.xlg,
Expand Down
2 changes: 1 addition & 1 deletion lib/demo/widgets/transactions_table.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class TransactionsTable extends StatelessWidget {
for (final transaction in transactions)
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(transaction.title),
title: Text(transaction.type.title(context)),
subtitle: DefaultTextStyle(
style: textTheme.bodyMedium!.copyWith(
color: colorScheme.onSurfaceVariant.withOpacity(0.8),
Expand Down
44 changes: 32 additions & 12 deletions lib/financial_data/bloc/financial_data_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,44 +1,64 @@
import 'dart:math' show Random;

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:financial_dashboard/l10n/l10n.dart';
import 'package:flutter/widgets.dart';

part 'financial_data_event.dart';
part 'financial_data_state.dart';

class FinancialDataBloc extends Bloc<FinancialDataEvent, FinancialDataState> {
FinancialDataBloc() : super(const FinancialDataState()) {
FinancialDataBloc({
Random? random,
}) : _random = random ?? Random(),
super(const FinancialDataState()) {
on<FinancialDataRequested>(_onLoadFinancialData);
}

final Random _random;

Future<void> _onLoadFinancialData(
FinancialDataRequested event,
Emitter<FinancialDataState> emit,
) async {
final savingsDataPoints = createSampleData(random: _random);
final index = _random.nextInt(savingsDataPoints.length);
final currentSavings = savingsDataPoints[index].value;
final monthlySpendingLimitGoal = (_random.nextDouble() * 1000) + 3000;

emit(
state.copyWith(
currentSavings: 234567.91,
savingsDataPoints: createSampleData(),
monthlySpendingLimitGoal: 3210.55,
transactions: [
const Transaction(title: 'Paycheck', amount: 3000),
const Transaction(title: 'Rent', amount: -1050.20),
const Transaction(title: 'Food', amount: -670.50),
],
currentSavings: currentSavings,
savingsDataPoints: savingsDataPoints,
monthlySpendingLimitGoal: monthlySpendingLimitGoal,
transactions: createSampleTransactions(random: _random),
),
);
}
}

@visibleForTesting
List<SavingsDataPoint> createSampleData() {
List<SavingsDataPoint> createSampleData({Random? random}) {
random ??= Random();

final data = <SavingsDataPoint>[];
var value = 100000.0;
for (var age = 25; age <= 90; age++) {
const toAdd = 10000;
final toAdd = random.nextInt(12000);
value += toAdd;
data.add(
SavingsDataPoint(age: age, value: value),
);
}
return data;
}

@visibleForTesting
List<Transaction> createSampleTransactions({Random? random}) {
random ??= Random();

return TransactionType.values
.map((type) => Transaction(type: type, amount: type.amount(random!)))
.toList();
}
49 changes: 46 additions & 3 deletions lib/financial_data/bloc/financial_data_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,60 @@ class SavingsDataPoint extends Equatable {
List<Object?> get props => [age, value];
}

enum TransactionType {
paycheck(4000, 5500),
rent(1000, 2000),
groceries(320, 500),
car(350, 600),
gym(40, 90),
other(200, 350);

const TransactionType(this.min, this.max);

final int min;
final int max;
}

extension TransactionTypeX on TransactionType {
List<TransactionType> get incomeTypes => [TransactionType.paycheck];

double amount(Random random) {
final value = random.nextDouble() * (max - min) + min;

if (incomeTypes.contains(this)) return value;
return -value;
}

String title(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case TransactionType.paycheck:
return l10n.paycheck;
case TransactionType.groceries:
return l10n.groceries;
case TransactionType.rent:
return l10n.rent;
case TransactionType.car:
return l10n.car;
case TransactionType.gym:
return l10n.gym;
case TransactionType.other:
return l10n.other;
}
}
}

class Transaction extends Equatable {
const Transaction({
required this.title,
required this.type,
required this.amount,
});

final String title;
final TransactionType type;
final double amount;

@override
List<Object?> get props => [title, amount];
List<Object?> get props => [type, amount];
}

extension TransactionListX on List<Transaction> {
Expand Down
36 changes: 36 additions & 0 deletions lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,41 @@
"description": "Text shown when there is no data available",
"type": "text",
"placeholders": {}
},
"paycheck": "Paycheck",
"@paycheck":{
"description": "Label for paycheck",
"type": "text",
"placeholders": {}
},
"groceries": "Groceries",
"@groceries":{
"description": "Label for groceries",
"type": "text",
"placeholders": {}
},
"rent": "Rent",
"@rent":{
"description": "Label for rent",
"type": "text",
"placeholders": {}
},
"car": "Car loan",
"@car":{
"description": "Label for car",
"type": "text",
"placeholders": {}
},
"gym": "Gym membership",
"@gym":{
"description": "Label for gym",
"type": "text",
"placeholders": {}
},
"other": "Other",
"@other":{
"description": "Label for other",
"type": "text",
"placeholders": {}
}
}
40 changes: 35 additions & 5 deletions test/demo/demo_page_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ void main() {
currentSavings: 123456,
savingsDataPoints: createSampleData(),
monthlySpendingLimitGoal: 1000,
transactions: [
const Transaction(title: 'Paycheck', amount: 3000),
const Transaction(title: 'Rent', amount: -1050.20),
const Transaction(title: 'Food', amount: -670.50),
],
transactions: createSampleTransactions(),
),
);
});
Expand Down Expand Up @@ -78,5 +74,39 @@ void main() {
);
expect(find.byType(AppThree), findsOneWidget);
});

group('adds $FinancialDataRequested event when pulling to refresh', () {
for (final flavor in AppFlavor.values) {
testWidgets(
'in $flavor',
(tester) async {
when(() => flavorCubit.state).thenReturn(flavor);

await tester.pumpExperience(
const DemoView(),
flavorCubit: flavorCubit,
themeModeCubit: themeModeCubit,
financialDataBloc: financialDataBloc,
);
await tester.pumpAndSettle();

final widgetToFling = find.byType(AppScaffold);
expect(widgetToFling, findsOneWidget);

await tester.fling(widgetToFling, const Offset(0, 500), 1000);

// Finish the scroll animation.
await tester.pump(const Duration(seconds: 1));
// Finish the indicator settle animation.
await tester.pump(const Duration(seconds: 1));
// Finish the indicator hide animation.
await tester.pump(const Duration(seconds: 1));

verify(() => financialDataBloc.add(const FinancialDataRequested()))
.called(1);
},
);
}
});
});
}
28 changes: 19 additions & 9 deletions test/financial_data/bloc/financial_data_bloc_test.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import 'dart:math' show Random;

import 'package:bloc_test/bloc_test.dart';
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class _MockRandom extends Mock implements Random {}

void main() {
group('FinancialDataBloc', () {
late Random random;

setUp(() {
random = _MockRandom();

when(() => random.nextInt(any())).thenReturn(0);
when(() => random.nextDouble()).thenReturn(0);
});

blocTest<FinancialDataBloc, FinancialDataState>(
'loads financial data when $FinancialDataRequested is added',
build: FinancialDataBloc.new,
build: () => FinancialDataBloc(random: random),
act: (bloc) => bloc.add(FinancialDataRequested()),
expect: () => [
FinancialDataState(
currentSavings: 234567.91,
savingsDataPoints: createSampleData(),
monthlySpendingLimitGoal: 3210.55,
transactions: [
const Transaction(title: 'Paycheck', amount: 3000),
const Transaction(title: 'Rent', amount: -1050.20),
const Transaction(title: 'Food', amount: -670.50),
],
currentSavings: 100000,
savingsDataPoints: createSampleData(random: random),
monthlySpendingLimitGoal: 3000,
transactions: createSampleTransactions(random: random),
),
],
);
Expand Down
11 changes: 7 additions & 4 deletions test/financial_data/bloc/financial_data_state_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ void main() {

test('copies transactions', () {
final state = FinancialDataState();
final transaction = Transaction(title: 'test', amount: 123.45);
final transaction = Transaction(
type: TransactionType.other,
amount: 123.45,
);
final newState = state.copyWith(transactions: [transaction]);

expect(newState.transactions, equals([transaction]));
Expand All @@ -53,9 +56,9 @@ void main() {

group('Transaction', () {
test('supports value equality', () {
const pointA = Transaction(title: 'test', amount: 100);
const secondPointA = Transaction(title: 'test', amount: 100);
const pointB = Transaction(title: 'test-two', amount: 200);
const pointA = Transaction(type: TransactionType.gym, amount: 100);
const secondPointA = Transaction(type: TransactionType.gym, amount: 100);
const pointB = Transaction(type: TransactionType.other, amount: 200);

expect(pointA, equals(secondPointA));
expect(pointA, isNot(equals(pointB)));
Expand Down
6 changes: 1 addition & 5 deletions test/src/ui/widgets/goal_progress_indicator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ void main() {
when(() => financialDataBloc.state).thenReturn(
FinancialDataState(
monthlySpendingLimitGoal: 1000,
transactions: [
const Transaction(title: 'Paycheck', amount: 3000),
const Transaction(title: 'Rent', amount: -1050.20),
const Transaction(title: 'Food', amount: -670.50),
],
transactions: createSampleTransactions(),
),
);
});
Expand Down

0 comments on commit beed289

Please sign in to comment.