Skip to content
This repository has been archived by the owner on Sep 14, 2024. It is now read-only.

WIP: Tests #191

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions lib/src/models/animated_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ typedef RemovedItemBuilder<T> = Widget Function(
Animation<double> animation,
);

abstract class AnimatedListModel<E> {
abstract class AnimatedListModel<E> extends Iterable<E> {
AnimatedListModel({
required this.listKey,
required this.removedItemBuilder,
Expand Down Expand Up @@ -54,13 +54,16 @@ abstract class AnimatedListModel<E> {
}
}

@override
bool get isNotEmpty => _items.isNotEmpty;

@override
int get length => _items.length;

E operator [](int index) => _items[index];

int indexOf(E item) => _items.indexOf(item);
@override
Iterator<E> get iterator => _items.iterator;
}

class AnimatedTimerList extends AnimatedListModel<Timer> {
Expand Down
2 changes: 2 additions & 0 deletions lib/src/models/app_authentication.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ class AppAuthentication extends Equatable {
..responseType = ResponseType.plain;

if (isSelfSignedCertificate) {
// coverage:ignore-start
authenticatedClient.httpClientAdapter = IOHttpClientAdapter(
onHttpClientCreate: (client) {
client.badCertificateCallback = (cert, host, port) => true;
return client;
},
);
// coverage:ignore-end
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/src/models/image_response.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart';

// coverage:ignore-start
class ImageResponse {
const ImageResponse({
required this.data,
Expand All @@ -8,3 +9,4 @@ class ImageResponse {
final Uint8List data;
final bool isSvg;
}
// coverage:ignore-end
52 changes: 26 additions & 26 deletions lib/src/models/recipe.dart
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
import 'package:nc_cookbook_api/nc_cookbook_api.dart';

extension RecipeExtension on Recipe {
Map<String, String> get nutritionList {
extension NutritionExtension on Nutrition {
Map<String, String> get asMap {
final items = <String, String>{};
if (nutrition.calories != null) {
items['calories'] = nutrition.calories!;
if (calories != null) {
items['calories'] = calories!;
}
if (nutrition.carbohydrateContent != null) {
items['carbohydrateContent'] = nutrition.carbohydrateContent!;
if (carbohydrateContent != null) {
items['carbohydrateContent'] = carbohydrateContent!;
}
if (nutrition.cholesterolContent != null) {
items['cholesterolContent'] = nutrition.cholesterolContent!;
if (cholesterolContent != null) {
items['cholesterolContent'] = cholesterolContent!;
}
if (nutrition.fatContent != null) {
items['fatContent'] = nutrition.fatContent!;
if (fatContent != null) {
items['fatContent'] = fatContent!;
}
if (nutrition.fiberContent != null) {
items['fiberContent'] = nutrition.fiberContent!;
if (fiberContent != null) {
items['fiberContent'] = fiberContent!;
}
if (nutrition.proteinContent != null) {
items['proteinContent'] = nutrition.proteinContent!;
if (proteinContent != null) {
items['proteinContent'] = proteinContent!;
}
if (nutrition.saturatedFatContent != null) {
items['saturatedFatContent'] = nutrition.saturatedFatContent!;
if (saturatedFatContent != null) {
items['saturatedFatContent'] = saturatedFatContent!;
}
if (nutrition.servingSize != null) {
items['servingSize'] = nutrition.servingSize!;
if (servingSize != null) {
items['servingSize'] = servingSize!;
}
if (nutrition.sodiumContent != null) {
items['sodiumContent'] = nutrition.sodiumContent!;
if (sodiumContent != null) {
items['sodiumContent'] = sodiumContent!;
}
if (nutrition.sugarContent != null) {
items['sugarContent'] = nutrition.sugarContent!;
if (sugarContent != null) {
items['sugarContent'] = sugarContent!;
}
if (nutrition.transFatContent != null) {
items['transFatContent'] = nutrition.transFatContent!;
if (transFatContent != null) {
items['transFatContent'] = transFatContent!;
}
if (nutrition.unsaturatedFatContent != null) {
items['unsaturatedFatContent'] = nutrition.unsaturatedFatContent!;
if (unsaturatedFatContent != null) {
items['unsaturatedFatContent'] = unsaturatedFatContent!;
}

return items;
Expand Down
22 changes: 15 additions & 7 deletions lib/src/models/timer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class Timer {
}
@visibleForTesting
@JsonKey(
toJson: _recipeToJson,
fromJson: _recipeFromJson,
toJson: recipeToJson,
fromJson: recipeFromJson,
)
final Recipe? recipe;
final DateTime done;
Expand Down Expand Up @@ -114,10 +114,18 @@ class Timer {
duration,
recipeId,
);
// coverage:ignore-start
@override
String toString() =>
'Timer(done: $done, id: $id, title: $title, body: $body, duration: $duration, recipeId: $recipeId)';
// coverage:ignore-end
}

Recipe _recipeFromJson(String data) =>
standardSerializers.fromJson<Recipe>(Recipe.serializer, data)!;

String? _recipeToJson(Object? data) =>
data != null ? standardSerializers.toJson(Recipe.serializer, data) : null;
@visibleForTesting
Recipe recipeFromJson(Map<String, dynamic>? data) =>
standardSerializers.deserializeWith<Recipe>(Recipe.serializer, data)!;
@visibleForTesting
Map<String, dynamic>? recipeToJson(Object? data) => data != null
? standardSerializers.serializeWith(Recipe.serializer, data)
as Map<String, dynamic>?
: null;
4 changes: 2 additions & 2 deletions lib/src/models/timer.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/src/screens/recipe_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ class _RecipeScreenBodyState extends State<RecipeScreenBody> {
@override
Widget build(BuildContext context) {
final list = [
if (recipe.nutritionList.isNotEmpty) NutritionList(recipe.nutritionList),
if (recipe.nutrition.asMap.isNotEmpty)
NutritionList(recipe.nutrition.asMap),
if (recipe.recipeIngredient.isNotEmpty) IngredientList(recipe),
InstructionList(recipe),
];
Expand Down
10 changes: 8 additions & 2 deletions lib/src/services/api_provider.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
part of 'services.dart';

class ApiProvider {
factory ApiProvider() => _apiProvider;
factory ApiProvider() => _apiProvider ??= ApiProvider._();

// coverage:ignore-start
@visibleForTesting
factory ApiProvider.mocked(ApiProvider mock) => _apiProvider ??= mock;
// coverage:ignore-end

ApiProvider._() {
final auth = UserRepository().currentAppAuthentication;

Expand Down Expand Up @@ -37,7 +43,7 @@ class ApiProvider {
miscApi = ncCookbookApi.getMiscApi();
tagsApi = ncCookbookApi.getTagsApi();
}
static final ApiProvider _apiProvider = ApiProvider._();
static ApiProvider? _apiProvider;

late NcCookbookApi ncCookbookApi;
late RecipesApi recipeApi;
Expand Down
2 changes: 2 additions & 0 deletions lib/src/services/authentication_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
part of 'services.dart';

// coverage:ignore-start
class AuthenticationProvider {
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
final String _appAuthenticationKey = 'appAuthentication';
Expand Down Expand Up @@ -241,3 +242,4 @@ class AuthenticationProvider {
await _secureStorage.delete(key: _appAuthenticationKey);
}
}
// coverage:ignore-end
29 changes: 15 additions & 14 deletions lib/src/services/data_repository.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
part of 'services.dart';

class DataRepository {
factory DataRepository() => _dataRepository;
factory DataRepository() => _dataRepository ??= const DataRepository._();

DataRepository._();
// coverage:ignore-start
@visibleForTesting
factory DataRepository.mocked(DataRepository mock) =>
_dataRepository ??= mock;
// coverage:ignore-end

const DataRepository._();
// Singleton
static final DataRepository _dataRepository = DataRepository._();
static DataRepository? _dataRepository;

// Provider List
final ApiProvider api = ApiProvider();
static final api = ApiProvider();

final NextcloudMetadataApi _nextcloudMetadataApi = NextcloudMetadataApi();
static final _nextcloudMetadataApi = NextcloudMetadataApi();

// Data
static final String categoryAll = translate('categories.all_categories');
Expand Down Expand Up @@ -108,16 +114,11 @@ class DataRepository {
}

Future<RecipeStub?> _fetchCategoryMainRecipe(Category category) async {
try {
final categoryRecipes = await fetchRecipesShort(category: category.name);
if (categoryRecipes != null && categoryRecipes.isNotEmpty) {
return categoryRecipes.first;
}
} catch (e) {
log('Could not load main recipe of Category!');
rethrow;
}
final categoryRecipes = await fetchRecipesShort(category: category.name);

if (categoryRecipes != null && categoryRecipes.isNotEmpty) {
return categoryRecipes.first;
}
return null;
}

Expand Down
13 changes: 10 additions & 3 deletions lib/src/services/intent_repository.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
part of 'services.dart';

class IntentRepository {
factory IntentRepository() => _intentRepository;
factory IntentRepository() =>
_intentRepository ??= const IntentRepository._();

IntentRepository._();
// coverage:ignore-start
@visibleForTesting
factory IntentRepository.mocked(IntentRepository mock) =>
_intentRepository ??= mock;
// coverage:ignore-end

const IntentRepository._();
// Singleton Pattern
static final IntentRepository _intentRepository = IntentRepository._();
static IntentRepository? _intentRepository;

static final _navigationKey = GlobalKey<NavigatorState>();
static const platform = MethodChannel('app.channel.shared.data');
Expand Down
2 changes: 2 additions & 0 deletions lib/src/services/net/nextcloud_metadata_api.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
part of '../services.dart';

// coverage:ignore-start
class NextcloudMetadataApi {
factory NextcloudMetadataApi() => NextcloudMetadataApi._(
UserRepository().currentAppAuthentication,
Expand All @@ -11,3 +12,4 @@ class NextcloudMetadataApi {
String getUserAvatarUrl() =>
'${_appAuthentication.server}/avatar/${_appAuthentication.loginName}/80';
}
// coverage:ignore-end
13 changes: 10 additions & 3 deletions lib/src/services/notification_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ final FlutterLocalNotificationsPlugin _localNotifications =
FlutterLocalNotificationsPlugin();

class NotificationService {
factory NotificationService() => _notificationService;

factory NotificationService() =>
_notificationService ??= NotificationService._();

// coverage:ignore-start
@visibleForTesting
factory NotificationService.mocked(NotificationService mock) =>
_notificationService ??= mock;
// coverage:ignore-end

NotificationService._();
static final NotificationService _notificationService =
NotificationService._();
static NotificationService? _notificationService;
int curId = 0;

Future<void> init() async {
Expand Down
1 change: 0 additions & 1 deletion lib/src/services/services.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';

import 'package:dio/dio.dart' as dio;
import 'package:dio/dio.dart';
Expand Down
9 changes: 7 additions & 2 deletions lib/src/services/timer_repository.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
part of 'services.dart';

class TimerList {
factory TimerList() => _instance;
factory TimerList() => _instance ??= TimerList._();

// coverage:ignore-start
@visibleForTesting
factory TimerList.mocked(TimerList mock) => _instance ??= mock;
// coverage:ignore-end

TimerList._() : _timers = <Timer>[];
static final TimerList _instance = TimerList._();
static TimerList? _instance;
final List<Timer> _timers;

List<Timer> get timers => _timers;
Expand Down
14 changes: 10 additions & 4 deletions lib/src/services/user_repository.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
part of 'services.dart';

class UserRepository {
factory UserRepository() => _userRepository;
factory UserRepository() => _userRepository ??= const UserRepository._();

UserRepository._();
// coverage:ignore-start
@visibleForTesting
factory UserRepository.mocked(UserRepository mock) =>
_userRepository ??= mock;
// coverage:ignore-end

const UserRepository._();
// Singleton
static final UserRepository _userRepository = UserRepository._();
static UserRepository? _userRepository;

AuthenticationProvider authenticationProvider = AuthenticationProvider();
static final authenticationProvider = AuthenticationProvider();

Future<AppAuthentication> authenticate(
String serverUrl,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/util/theme_data.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';

// coverage:ignore-start
class AppTheme {
const AppTheme._();

Expand Down Expand Up @@ -90,3 +91,4 @@ class SnackBarThemes extends ThemeExtension<SnackBarThemes> {
@override
String toString() => 'SnackBarThemes(colorScheme: $colorScheme)';
}
// coverage:ignore-end
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ dev_dependencies:
sdk: flutter

lint: ^2.0.0
mocktail: ^0.3.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
Expand Down
Loading