refactor: create separate BLoCs for each screen with comprehensive tests

- Created 8 separate BLoCs (Home, Library, BookDetails, AddBook, Scanner, Categories, Wishlist, Settings)
- Each BLoC has its own event, state, and bloc files
- Added 70 comprehensive tests covering all BLoC functionality
- All tests passing (70/70)
- Fixed linting issues and updated deprecated APIs
- Improved code organization and maintainability
This commit is contained in:
Yuriy Panov
2026-02-04 14:40:00 +06:00
parent 3004f712f3
commit 310463e89a
177 changed files with 9718 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_bloc.dart';
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_event.dart';
import 'package:bookshelf_flutter/bloc/wishlist/wishlist_state.dart';
import 'package:bookshelf_flutter/models/models.dart';
void main() {
group('WishlistBloc', () {
late WishlistBloc wishlistBloc;
setUp(() {
wishlistBloc = WishlistBloc();
});
tearDown(() {
wishlistBloc.close();
});
test('initial state is WishlistState.initial()', () {
expect(wishlistBloc.state, isA<WishlistState>());
expect(wishlistBloc.state.isLoading, true);
});
group('LoadWishlist', () {
test('loads wishlist books into state', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
expect(wishlistBloc.state.books.isNotEmpty, true);
expect(wishlistBloc.state.filteredBooks.isNotEmpty, true);
expect(wishlistBloc.state.isLoading, false);
});
});
group('RemoveFromWishlist', () {
test('removes book from both lists', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
final bookId = wishlistBloc.state.books.first.id;
wishlistBloc.add(RemoveFromWishlist(bookId));
await Future.delayed(Duration.zero);
expect(
wishlistBloc.state.books.any((book) => book.id == bookId),
false,
);
expect(
wishlistBloc.state.filteredBooks.any((book) => book.id == bookId),
false,
);
});
});
group('SearchWishlist', () {
test('filters books by title', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
wishlistBloc.add(const SearchWishlist('1984'));
await Future.delayed(Duration.zero);
expect(wishlistBloc.state.filteredBooks.length, 1);
expect(wishlistBloc.state.filteredBooks.first.title, contains('1984'));
});
test('filters books by author', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
wishlistBloc.add(const SearchWishlist('Оруэлл'));
await Future.delayed(Duration.zero);
expect(wishlistBloc.state.filteredBooks.length, 1);
expect(wishlistBloc.state.filteredBooks.first.author, contains('Оруэлл'));
});
test('returns empty list when no matches found', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
wishlistBloc.add(const SearchWishlist('NonExistent'));
await Future.delayed(Duration.zero);
expect(wishlistBloc.state.filteredBooks.length, 0);
});
});
group('MoveToLibrary', () {
test('updates book status to reading', () async {
wishlistBloc.add(const LoadWishlist());
await Future.delayed(Duration.zero);
final bookId = wishlistBloc.state.books.first.id;
wishlistBloc.add(MoveToLibrary(bookId));
await Future.delayed(Duration.zero);
final book = wishlistBloc.state.books.firstWhere((b) => b.id == bookId);
expect(book.status, BookStatus.reading);
expect(wishlistBloc.state.movedBookId, bookId);
});
});
});
}