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,138 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:bookshelf_flutter/bloc/book_details/book_details_bloc.dart';
import 'package:bookshelf_flutter/bloc/book_details/book_details_event.dart';
import 'package:bookshelf_flutter/bloc/book_details/book_details_state.dart';
import 'package:bookshelf_flutter/models/models.dart';
void main() {
group('BookDetailsBloc', () {
late BookDetailsBloc bookDetailsBloc;
late Book testBook;
setUp(() {
bookDetailsBloc = BookDetailsBloc();
testBook = createBook(
id: '1',
title: 'Test Book',
author: 'Test Author',
genre: 'Test Genre',
annotation: 'Test annotation',
coverUrl: 'https://example.com/cover.jpg',
pages: 200,
language: 'English',
publishedYear: 2020,
rating: 4.5,
status: BookStatus.reading,
progress: 50,
isFavorite: false,
);
});
tearDown(() {
bookDetailsBloc.close();
});
test('initial state is BookDetailsState.initial()', () {
expect(bookDetailsBloc.state, isA<BookDetailsState>());
expect(bookDetailsBloc.state.isLoading, true);
});
group('LoadBookDetails', () {
test('loads book details into state', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.id, testBook.id);
expect(bookDetailsBloc.state.book?.title, testBook.title);
expect(bookDetailsBloc.state.isLoading, false);
});
});
group('ToggleFavorite', () {
test('toggles isFavorite from false to true', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const ToggleFavorite());
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.isFavorite, true);
});
test('toggles isFavorite from true to false', () async {
final favoriteBook = testBook.copyWith(isFavorite: true);
bookDetailsBloc.add(LoadBookDetails(favoriteBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const ToggleFavorite());
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.isFavorite, false);
});
test('does nothing when no book is loaded', () async {
final initialFavorite = bookDetailsBloc.state.book?.isFavorite;
bookDetailsBloc.add(const ToggleFavorite());
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.isFavorite, initialFavorite);
});
});
group('UpdateProgress', () {
test('updates progress and keeps reading status', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const UpdateProgress(75));
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.progress, 75);
expect(bookDetailsBloc.state.book?.status, BookStatus.reading);
});
test('updates status to done when progress is 100', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const UpdateProgress(100));
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.progress, 100);
expect(bookDetailsBloc.state.book?.status, BookStatus.done);
});
test('does nothing when no book is loaded', () async {
final initialProgress = bookDetailsBloc.state.book?.progress;
bookDetailsBloc.add(const UpdateProgress(50));
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.progress, initialProgress);
});
});
group('UpdateStatus', () {
test('updates book status', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const UpdateStatus(BookStatus.done));
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.book?.status, BookStatus.done);
});
});
group('DeleteBook', () {
test('sets isDeleted to true', () async {
bookDetailsBloc.add(LoadBookDetails(testBook));
await Future.delayed(Duration.zero);
bookDetailsBloc.add(const DeleteBook());
await Future.delayed(Duration.zero);
expect(bookDetailsBloc.state.isDeleted, true);
});
});
});
}