Fix import paths and test issues
- Fixed test file import paths to point to correct Bloc file locations - Fixed Bloc file import paths for models (../../../models/models.dart) - Added explicit type annotations to resolve null safety warnings - Fixed null safety issues in wishlist_bloc_test.dart - All 70 tests now passing
This commit is contained in:
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import '../bloc/app_bloc.dart';
|
import '../../bloc/app_bloc.dart';
|
||||||
import '../bloc/app_event.dart';
|
import '../../bloc/app_event.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class AddBookScreen extends StatefulWidget {
|
class AddBookScreen extends StatefulWidget {
|
||||||
final dynamic initialData;
|
final dynamic initialData;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'add_book_event.dart';
|
import 'add_book_event.dart';
|
||||||
import 'add_book_state.dart';
|
import 'add_book_state.dart';
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class AddBookState extends Equatable {
|
class AddBookState extends Equatable {
|
||||||
final String title;
|
final String title;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'book_details_event.dart';
|
import 'book_details_event.dart';
|
||||||
import 'book_details_state.dart';
|
import 'book_details_state.dart';
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
abstract class BookDetailsEvent {
|
abstract class BookDetailsEvent {
|
||||||
const BookDetailsEvent();
|
const BookDetailsEvent();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class BookDetailsState extends Equatable {
|
class BookDetailsState extends Equatable {
|
||||||
final Book? book;
|
final Book? book;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import '../bloc/app_bloc.dart';
|
import '../../bloc/app_bloc.dart';
|
||||||
import '../bloc/app_event.dart';
|
import '../../bloc/app_event.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class BookDetailsScreen extends StatelessWidget {
|
class BookDetailsScreen extends StatelessWidget {
|
||||||
final Book book;
|
final Book book;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'categories_event.dart';
|
import 'categories_event.dart';
|
||||||
import 'categories_state.dart';
|
import 'categories_state.dart';
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
abstract class CategoriesEvent {
|
abstract class CategoriesEvent {
|
||||||
const CategoriesEvent();
|
const CategoriesEvent();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class CategoriesState extends Equatable {
|
class CategoriesState extends Equatable {
|
||||||
final List<Category> categories;
|
final List<Category> categories;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class CategoriesScreen extends StatelessWidget {
|
class CategoriesScreen extends StatelessWidget {
|
||||||
const CategoriesScreen({super.key});
|
const CategoriesScreen({super.key});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'home_event.dart';
|
import 'home_event.dart';
|
||||||
import 'home_state.dart';
|
import 'home_state.dart';
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
abstract class HomeEvent {
|
abstract class HomeEvent {
|
||||||
const HomeEvent();
|
const HomeEvent();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class HomeState extends Equatable {
|
class HomeState extends Equatable {
|
||||||
final AppScreen currentScreen;
|
final AppScreen currentScreen;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../bloc/app_bloc.dart';
|
import '../../bloc/app_bloc.dart';
|
||||||
import '../bloc/app_state.dart';
|
import '../../bloc/app_state.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'library_screen.dart';
|
import '../library/library_screen.dart';
|
||||||
import 'categories_screen.dart';
|
import '../categories/categories_screen.dart';
|
||||||
import 'book_details_screen.dart';
|
import '../book_details/book_details_screen.dart';
|
||||||
import 'add_book_screen.dart';
|
import '../add_book/add_book_screen.dart';
|
||||||
import 'scanner_screen.dart';
|
import '../scanner/scanner_screen.dart';
|
||||||
import 'wishlist_screen.dart';
|
import '../wishlist/wishlist_screen.dart';
|
||||||
import 'settings_screen.dart';
|
import '../settings/settings_screen.dart';
|
||||||
import '../widgets/bottom_nav.dart';
|
import '../../widgets/bottom_nav.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatelessWidget {
|
class HomeScreen extends StatelessWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'library_event.dart';
|
import 'library_event.dart';
|
||||||
import 'library_state.dart';
|
import 'library_state.dart';
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ class LibraryBloc extends Bloc<LibraryEvent, LibraryState> {
|
|||||||
|
|
||||||
void _onSearchBooks(SearchBooks event, Emitter<LibraryState> emit) {
|
void _onSearchBooks(SearchBooks event, Emitter<LibraryState> emit) {
|
||||||
final query = event.query.toLowerCase();
|
final query = event.query.toLowerCase();
|
||||||
final filtered = state.books.where((book) {
|
final filtered = state.books.where((Book book) {
|
||||||
return book.title.toLowerCase().contains(query) ||
|
return book.title.toLowerCase().contains(query) ||
|
||||||
book.author.toLowerCase().contains(query) ||
|
book.author.toLowerCase().contains(query) ||
|
||||||
book.genre.toLowerCase().contains(query);
|
book.genre.toLowerCase().contains(query);
|
||||||
@@ -104,7 +104,7 @@ class LibraryBloc extends Bloc<LibraryEvent, LibraryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onFilterByStatus(FilterByStatus event, Emitter<LibraryState> emit) {
|
void _onFilterByStatus(FilterByStatus event, Emitter<LibraryState> emit) {
|
||||||
final filtered = state.books.where((book) {
|
final filtered = state.books.where((Book book) {
|
||||||
return book.status == event.status;
|
return book.status == event.status;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
abstract class LibraryEvent {
|
abstract class LibraryEvent {
|
||||||
const LibraryEvent();
|
const LibraryEvent();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class LibraryState extends Equatable {
|
class LibraryState extends Equatable {
|
||||||
final List<Book> books;
|
final List<Book> books;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import '../bloc/app_bloc.dart';
|
import '../../bloc/app_bloc.dart';
|
||||||
import '../bloc/app_event.dart';
|
import '../../bloc/app_event.dart';
|
||||||
import '../bloc/app_state.dart';
|
import '../../bloc/app_state.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class LibraryScreen extends StatefulWidget {
|
class LibraryScreen extends StatefulWidget {
|
||||||
const LibraryScreen({super.key});
|
const LibraryScreen({super.key});
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../bloc/app_bloc.dart';
|
import '../../bloc/app_bloc.dart';
|
||||||
import '../bloc/app_event.dart';
|
import '../../bloc/app_event.dart';
|
||||||
import '../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class ScannerScreen extends StatelessWidget {
|
class ScannerScreen extends StatelessWidget {
|
||||||
const ScannerScreen({super.key});
|
const ScannerScreen({super.key});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
import 'wishlist_event.dart';
|
import 'wishlist_event.dart';
|
||||||
import 'wishlist_state.dart';
|
import 'wishlist_state.dart';
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import '../../models/models.dart';
|
import '../../../models/models.dart';
|
||||||
|
|
||||||
class WishlistState extends Equatable {
|
class WishlistState extends Equatable {
|
||||||
final List<Book> books;
|
final List<Book> books;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'bloc/app_bloc.dart';
|
import 'bloc/app_bloc.dart';
|
||||||
import 'screens/home_screen.dart';
|
import 'features/home/home_screen.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const BookshelfApp());
|
runApp(const BookshelfApp());
|
||||||
|
|||||||
10
bookshelf_flutter/lib/models/app_screen.dart
Normal file
10
bookshelf_flutter/lib/models/app_screen.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// App screen enum
|
||||||
|
enum AppScreen {
|
||||||
|
library,
|
||||||
|
categories,
|
||||||
|
wishlist,
|
||||||
|
settings,
|
||||||
|
details,
|
||||||
|
addBook,
|
||||||
|
scanner,
|
||||||
|
}
|
||||||
127
bookshelf_flutter/lib/models/book.dart
Normal file
127
bookshelf_flutter/lib/models/book.dart
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import 'book_status.dart';
|
||||||
|
|
||||||
|
// Book record - using Dart records as data classes
|
||||||
|
typedef Book = ({
|
||||||
|
String id,
|
||||||
|
String title,
|
||||||
|
String author,
|
||||||
|
String genre,
|
||||||
|
String annotation,
|
||||||
|
String? coverUrl,
|
||||||
|
int? pages,
|
||||||
|
String? language,
|
||||||
|
int? publishedYear,
|
||||||
|
double? rating,
|
||||||
|
BookStatus status,
|
||||||
|
int? progress,
|
||||||
|
bool isFavorite,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to create a Book record
|
||||||
|
Book createBook({
|
||||||
|
required String id,
|
||||||
|
required String title,
|
||||||
|
required String author,
|
||||||
|
required String genre,
|
||||||
|
required String annotation,
|
||||||
|
String? coverUrl,
|
||||||
|
int? pages,
|
||||||
|
String? language,
|
||||||
|
int? publishedYear,
|
||||||
|
double? rating,
|
||||||
|
required BookStatus status,
|
||||||
|
int? progress,
|
||||||
|
required bool isFavorite,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
id: id,
|
||||||
|
title: title,
|
||||||
|
author: author,
|
||||||
|
genre: genre,
|
||||||
|
annotation: annotation,
|
||||||
|
coverUrl: coverUrl,
|
||||||
|
pages: pages,
|
||||||
|
language: language,
|
||||||
|
publishedYear: publishedYear,
|
||||||
|
rating: rating,
|
||||||
|
status: status,
|
||||||
|
progress: progress,
|
||||||
|
isFavorite: isFavorite,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension to create Book from partial data
|
||||||
|
extension BookExtension on Book {
|
||||||
|
Book copyWith({
|
||||||
|
String? id,
|
||||||
|
String? title,
|
||||||
|
String? author,
|
||||||
|
String? genre,
|
||||||
|
String? annotation,
|
||||||
|
String? coverUrl,
|
||||||
|
int? pages,
|
||||||
|
String? language,
|
||||||
|
int? publishedYear,
|
||||||
|
double? rating,
|
||||||
|
BookStatus? status,
|
||||||
|
int? progress,
|
||||||
|
bool? isFavorite,
|
||||||
|
}) {
|
||||||
|
return createBook(
|
||||||
|
id: id ?? this.id,
|
||||||
|
title: title ?? this.title,
|
||||||
|
author: author ?? this.author,
|
||||||
|
genre: genre ?? this.genre,
|
||||||
|
annotation: annotation ?? this.annotation,
|
||||||
|
coverUrl: coverUrl ?? this.coverUrl,
|
||||||
|
pages: pages ?? this.pages,
|
||||||
|
language: language ?? this.language,
|
||||||
|
publishedYear: publishedYear ?? this.publishedYear,
|
||||||
|
rating: rating ?? this.rating,
|
||||||
|
status: status ?? this.status,
|
||||||
|
progress: progress ?? this.progress,
|
||||||
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to JSON for storage/transport
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'title': title,
|
||||||
|
'author': author,
|
||||||
|
'genre': genre,
|
||||||
|
'annotation': annotation,
|
||||||
|
'coverUrl': coverUrl,
|
||||||
|
'pages': pages,
|
||||||
|
'language': language,
|
||||||
|
'publishedYear': publishedYear,
|
||||||
|
'rating': rating,
|
||||||
|
'status': status.name,
|
||||||
|
'progress': progress,
|
||||||
|
'isFavorite': isFavorite,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Book from JSON
|
||||||
|
static Book fromJson(Map<String, dynamic> json) {
|
||||||
|
return (
|
||||||
|
id: json['id'] as String,
|
||||||
|
title: json['title'] as String,
|
||||||
|
author: json['author'] as String,
|
||||||
|
genre: json['genre'] as String,
|
||||||
|
annotation: json['annotation'] as String,
|
||||||
|
coverUrl: json['coverUrl'] as String?,
|
||||||
|
pages: json['pages'] as int?,
|
||||||
|
language: json['language'] as String?,
|
||||||
|
publishedYear: json['publishedYear'] as int?,
|
||||||
|
rating: (json['rating'] as num?)?.toDouble(),
|
||||||
|
status: BookStatus.values.firstWhere(
|
||||||
|
(e) => e.name == json['status'],
|
||||||
|
orElse: () => BookStatus.wantToRead,
|
||||||
|
),
|
||||||
|
progress: json['progress'] as int?,
|
||||||
|
isFavorite: json['isFavorite'] as bool? ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
bookshelf_flutter/lib/models/book_status.dart
Normal file
20
bookshelf_flutter/lib/models/book_status.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Book status enum
|
||||||
|
enum BookStatus {
|
||||||
|
reading,
|
||||||
|
done,
|
||||||
|
wantToRead,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension for BookStatus to get display name
|
||||||
|
extension BookStatusExtension on BookStatus {
|
||||||
|
String get displayName {
|
||||||
|
switch (this) {
|
||||||
|
case BookStatus.reading:
|
||||||
|
return 'Reading Now';
|
||||||
|
case BookStatus.done:
|
||||||
|
return 'Completed';
|
||||||
|
case BookStatus.wantToRead:
|
||||||
|
return 'Wishlist';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
bookshelf_flutter/lib/models/category.dart
Normal file
27
bookshelf_flutter/lib/models/category.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// Category record - using Dart records as data classes
|
||||||
|
typedef Category = ({
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
int count,
|
||||||
|
IconData icon,
|
||||||
|
String colorClass,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to create a Category record
|
||||||
|
Category createCategory({
|
||||||
|
required String id,
|
||||||
|
required String name,
|
||||||
|
required int count,
|
||||||
|
required IconData icon,
|
||||||
|
required String colorClass,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
count: count,
|
||||||
|
icon: icon,
|
||||||
|
colorClass: colorClass,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,185 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
// Barrel export for all models
|
||||||
|
export 'app_screen.dart';
|
||||||
// App screen enum
|
export 'book_status.dart';
|
||||||
enum AppScreen {
|
export 'category.dart';
|
||||||
library,
|
export 'book.dart';
|
||||||
categories,
|
|
||||||
wishlist,
|
|
||||||
settings,
|
|
||||||
details,
|
|
||||||
addBook,
|
|
||||||
scanner,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Book status enum
|
|
||||||
enum BookStatus {
|
|
||||||
reading,
|
|
||||||
done,
|
|
||||||
wantToRead,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension for BookStatus to get display name
|
|
||||||
extension BookStatusExtension on BookStatus {
|
|
||||||
String get displayName {
|
|
||||||
switch (this) {
|
|
||||||
case BookStatus.reading:
|
|
||||||
return 'Reading Now';
|
|
||||||
case BookStatus.done:
|
|
||||||
return 'Completed';
|
|
||||||
case BookStatus.wantToRead:
|
|
||||||
return 'Wishlist';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category record - using Dart records as data classes
|
|
||||||
typedef Category = ({
|
|
||||||
String id,
|
|
||||||
String name,
|
|
||||||
int count,
|
|
||||||
IconData icon,
|
|
||||||
String colorClass,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Book record - using Dart records as data classes
|
|
||||||
typedef Book = ({
|
|
||||||
String id,
|
|
||||||
String title,
|
|
||||||
String author,
|
|
||||||
String genre,
|
|
||||||
String annotation,
|
|
||||||
String? coverUrl,
|
|
||||||
int? pages,
|
|
||||||
String? language,
|
|
||||||
int? publishedYear,
|
|
||||||
double? rating,
|
|
||||||
BookStatus status,
|
|
||||||
int? progress,
|
|
||||||
bool isFavorite,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper function to create a Category record
|
|
||||||
Category createCategory({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required int count,
|
|
||||||
required IconData icon,
|
|
||||||
required String colorClass,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
count: count,
|
|
||||||
icon: icon,
|
|
||||||
colorClass: colorClass,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to create a Book record
|
|
||||||
Book createBook({
|
|
||||||
required String id,
|
|
||||||
required String title,
|
|
||||||
required String author,
|
|
||||||
required String genre,
|
|
||||||
required String annotation,
|
|
||||||
String? coverUrl,
|
|
||||||
int? pages,
|
|
||||||
String? language,
|
|
||||||
int? publishedYear,
|
|
||||||
double? rating,
|
|
||||||
required BookStatus status,
|
|
||||||
int? progress,
|
|
||||||
required bool isFavorite,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
id: id,
|
|
||||||
title: title,
|
|
||||||
author: author,
|
|
||||||
genre: genre,
|
|
||||||
annotation: annotation,
|
|
||||||
coverUrl: coverUrl,
|
|
||||||
pages: pages,
|
|
||||||
language: language,
|
|
||||||
publishedYear: publishedYear,
|
|
||||||
rating: rating,
|
|
||||||
status: status,
|
|
||||||
progress: progress,
|
|
||||||
isFavorite: isFavorite,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension to create Book from partial data
|
|
||||||
extension BookExtension on Book {
|
|
||||||
Book copyWith({
|
|
||||||
String? id,
|
|
||||||
String? title,
|
|
||||||
String? author,
|
|
||||||
String? genre,
|
|
||||||
String? annotation,
|
|
||||||
String? coverUrl,
|
|
||||||
int? pages,
|
|
||||||
String? language,
|
|
||||||
int? publishedYear,
|
|
||||||
double? rating,
|
|
||||||
BookStatus? status,
|
|
||||||
int? progress,
|
|
||||||
bool? isFavorite,
|
|
||||||
}) {
|
|
||||||
return createBook(
|
|
||||||
id: id ?? this.id,
|
|
||||||
title: title ?? this.title,
|
|
||||||
author: author ?? this.author,
|
|
||||||
genre: genre ?? this.genre,
|
|
||||||
annotation: annotation ?? this.annotation,
|
|
||||||
coverUrl: coverUrl ?? this.coverUrl,
|
|
||||||
pages: pages ?? this.pages,
|
|
||||||
language: language ?? this.language,
|
|
||||||
publishedYear: publishedYear ?? this.publishedYear,
|
|
||||||
rating: rating ?? this.rating,
|
|
||||||
status: status ?? this.status,
|
|
||||||
progress: progress ?? this.progress,
|
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to JSON for storage/transport
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'id': id,
|
|
||||||
'title': title,
|
|
||||||
'author': author,
|
|
||||||
'genre': genre,
|
|
||||||
'annotation': annotation,
|
|
||||||
'coverUrl': coverUrl,
|
|
||||||
'pages': pages,
|
|
||||||
'language': language,
|
|
||||||
'publishedYear': publishedYear,
|
|
||||||
'rating': rating,
|
|
||||||
'status': status.name,
|
|
||||||
'progress': progress,
|
|
||||||
'isFavorite': isFavorite,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Book from JSON
|
|
||||||
static Book fromJson(Map<String, dynamic> json) {
|
|
||||||
return (
|
|
||||||
id: json['id'] as String,
|
|
||||||
title: json['title'] as String,
|
|
||||||
author: json['author'] as String,
|
|
||||||
genre: json['genre'] as String,
|
|
||||||
annotation: json['annotation'] as String,
|
|
||||||
coverUrl: json['coverUrl'] as String?,
|
|
||||||
pages: json['pages'] as int?,
|
|
||||||
language: json['language'] as String?,
|
|
||||||
publishedYear: json['publishedYear'] as int?,
|
|
||||||
rating: (json['rating'] as num?)?.toDouble(),
|
|
||||||
status: BookStatus.values.firstWhere(
|
|
||||||
(e) => e.name == json['status'],
|
|
||||||
orElse: () => BookStatus.wantToRead,
|
|
||||||
),
|
|
||||||
progress: json['progress'] as int?,
|
|
||||||
isFavorite: json['isFavorite'] as bool? ?? false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/add_book/add_book_bloc.dart';
|
import 'package:bookshelf_flutter/features/add_book/bloc/add_book_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/add_book/add_book_event.dart';
|
import 'package:bookshelf_flutter/features/add_book/bloc/add_book_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/add_book/add_book_state.dart';
|
import 'package:bookshelf_flutter/features/add_book/bloc/add_book_state.dart';
|
||||||
import 'package:bookshelf_flutter/models/models.dart';
|
import 'package:bookshelf_flutter/models/models.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/book_details/book_details_bloc.dart';
|
import 'package:bookshelf_flutter/features/book_details/bloc/book_details_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/book_details/book_details_event.dart';
|
import 'package:bookshelf_flutter/features/book_details/bloc/book_details_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/book_details/book_details_state.dart';
|
import 'package:bookshelf_flutter/features/book_details/bloc/book_details_state.dart';
|
||||||
import 'package:bookshelf_flutter/models/models.dart';
|
import 'package:bookshelf_flutter/models/models.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/categories/categories_bloc.dart';
|
import 'package:bookshelf_flutter/features/categories/bloc/categories_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/categories/categories_event.dart';
|
import 'package:bookshelf_flutter/features/categories/bloc/categories_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/categories/categories_state.dart';
|
import 'package:bookshelf_flutter/features/categories/bloc/categories_state.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('CategoriesBloc', () {
|
group('CategoriesBloc', () {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/home/home_bloc.dart';
|
import 'package:bookshelf_flutter/features/home/bloc/home_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/home/home_event.dart';
|
import 'package:bookshelf_flutter/features/home/bloc/home_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/home/home_state.dart';
|
import 'package:bookshelf_flutter/features/home/bloc/home_state.dart';
|
||||||
import 'package:bookshelf_flutter/models/models.dart';
|
import 'package:bookshelf_flutter/models/models.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/library/library_bloc.dart';
|
import 'package:bookshelf_flutter/features/library/bloc/library_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/library/library_event.dart';
|
import 'package:bookshelf_flutter/features/library/bloc/library_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/library/library_state.dart';
|
import 'package:bookshelf_flutter/features/library/bloc/library_state.dart';
|
||||||
import 'package:bookshelf_flutter/models/models.dart';
|
import 'package:bookshelf_flutter/models/models.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -100,7 +100,7 @@ void main() {
|
|||||||
libraryBloc.add(const FilterByStatus(BookStatus.reading));
|
libraryBloc.add(const FilterByStatus(BookStatus.reading));
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
expect(libraryBloc.state.filteredBooks.every((book) => book.status == BookStatus.reading), true);
|
expect(libraryBloc.state.filteredBooks.every((Book book) => book.status == BookStatus.reading), true);
|
||||||
expect(libraryBloc.state.statusFilter, BookStatus.reading);
|
expect(libraryBloc.state.statusFilter, BookStatus.reading);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/scanner/scanner_bloc.dart';
|
import 'package:bookshelf_flutter/features/scanner/bloc/scanner_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/scanner/scanner_event.dart';
|
import 'package:bookshelf_flutter/features/scanner/bloc/scanner_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/scanner/scanner_state.dart';
|
import 'package:bookshelf_flutter/features/scanner/bloc/scanner_state.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('ScannerBloc', () {
|
group('ScannerBloc', () {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/settings/settings_bloc.dart';
|
import 'package:bookshelf_flutter/features/settings/bloc/settings_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/settings/settings_event.dart';
|
import 'package:bookshelf_flutter/features/settings/bloc/settings_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/settings/settings_state.dart';
|
import 'package:bookshelf_flutter/features/settings/bloc/settings_state.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('SettingsBloc', () {
|
group('SettingsBloc', () {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_bloc.dart';
|
import 'package:bookshelf_flutter/features/wishlist/bloc/wishlist_bloc.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_event.dart';
|
import 'package:bookshelf_flutter/features/wishlist/bloc/wishlist_event.dart';
|
||||||
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_state.dart';
|
import 'package:bookshelf_flutter/features/wishlist/bloc/wishlist_state.dart';
|
||||||
import 'package:bookshelf_flutter/models/models.dart';
|
import 'package:bookshelf_flutter/models/models.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -37,16 +37,19 @@ void main() {
|
|||||||
wishlistBloc.add(const LoadWishlist());
|
wishlistBloc.add(const LoadWishlist());
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
final bookId = wishlistBloc.state.books.first.id;
|
expect(wishlistBloc.state.books.isNotEmpty, true, reason: 'No books loaded');
|
||||||
|
final book = wishlistBloc.state.books.first;
|
||||||
|
final bookId = book.id;
|
||||||
|
|
||||||
wishlistBloc.add(RemoveFromWishlist(bookId));
|
wishlistBloc.add(RemoveFromWishlist(bookId));
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
wishlistBloc.state.books.any((book) => book.id == bookId),
|
wishlistBloc.state.books.any((b) => b.id == bookId),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
wishlistBloc.state.filteredBooks.any((book) => book.id == bookId),
|
wishlistBloc.state.filteredBooks.any((b) => b.id == bookId),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -91,12 +94,15 @@ void main() {
|
|||||||
wishlistBloc.add(const LoadWishlist());
|
wishlistBloc.add(const LoadWishlist());
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
final bookId = wishlistBloc.state.books.first.id;
|
expect(wishlistBloc.state.books.isNotEmpty, true, reason: 'No books loaded');
|
||||||
|
final book = wishlistBloc.state.books.first;
|
||||||
|
final bookId = book.id;
|
||||||
|
|
||||||
wishlistBloc.add(MoveToLibrary(bookId));
|
wishlistBloc.add(MoveToLibrary(bookId));
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
final book = wishlistBloc.state.books.firstWhere((b) => b.id == bookId);
|
final foundBook = wishlistBloc.state.books.firstWhere((b) => b.id == bookId);
|
||||||
expect(book.status, BookStatus.reading);
|
expect(foundBook.status, BookStatus.reading);
|
||||||
expect(wishlistBloc.state.movedBookId, bookId);
|
expect(wishlistBloc.state.movedBookId, bookId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user