forked from eatvio/eatvio_app
chore(version): update app version to 1.3.5+85 and improve test coverage reporting in CI
This commit is contained in:
@@ -6,414 +6,150 @@ import 'package:eatvio_app/features/feed/models/comment_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import 'comments_cubit_test.mocks.dart';
|
||||
class MockFeedRepository extends Mock implements FeedRepository {}
|
||||
|
||||
class MockCommentsRepository extends Mock implements CommentsRepository {}
|
||||
|
||||
@GenerateNiceMocks([
|
||||
MockSpec<FeedRepository>(),
|
||||
MockSpec<CommentsRepository>(),
|
||||
])
|
||||
void main() {
|
||||
group('CommentsCubit', () {
|
||||
CommentsCubit? commentsCubit;
|
||||
late CommentsCubit cubit;
|
||||
late MockFeedRepository mockFeedRepository;
|
||||
late MockCommentsRepository mockCommentsRepository;
|
||||
late CommentModel testCommentModel;
|
||||
|
||||
setUp(() {
|
||||
mockFeedRepository = MockFeedRepository();
|
||||
mockCommentsRepository = MockCommentsRepository();
|
||||
|
||||
testCommentModel = const CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(
|
||||
currentPage: 1,
|
||||
from: null,
|
||||
lastPage: 1,
|
||||
links: [],
|
||||
path: null,
|
||||
perPage: 10,
|
||||
to: null,
|
||||
total: 0,
|
||||
totalMinPrice: null,
|
||||
totalMaxPrice: null,
|
||||
),
|
||||
cubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
if (commentsCubit != null && !commentsCubit!.isClosed) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
commentsCubit!.close();
|
||||
commentsCubit = null;
|
||||
}
|
||||
tearDown(() {
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
test('начальное состояние - CommentsState', () {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
expect(commentsCubit!.state, const CommentsState());
|
||||
expect(cubit.state, isA<CommentsState>());
|
||||
});
|
||||
|
||||
test('getComments - успешная загрузка комментариев для blogPost', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
const id = '1';
|
||||
const mockComment = CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: null, lastPage: 1, links: [], perPage: 10, to: null, total: 0),
|
||||
);
|
||||
const postId = '1';
|
||||
|
||||
when(mockFeedRepository.getBlogPostComments(id: postId))
|
||||
.thenAnswer((_) async => testCommentModel);
|
||||
when(() => mockFeedRepository.getBlogPostComments(id: id))
|
||||
.thenAnswer((_) async => mockComment);
|
||||
|
||||
await commentsCubit!.getComments(postId, CommentSourceType.blogPost, true);
|
||||
await cubit.getComments(id, CommentSourceType.blogPost, true);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment, testCommentModel);
|
||||
verify(mockFeedRepository.getBlogPostComments(id: postId)).called(1);
|
||||
});
|
||||
|
||||
test('getComments - успешная загрузка комментариев для diet', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const dietId = '1';
|
||||
|
||||
when(mockFeedRepository.getDietsComments(id: dietId))
|
||||
.thenAnswer((_) async => testCommentModel);
|
||||
|
||||
await commentsCubit!.getComments(dietId, CommentSourceType.diet, true);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
verify(mockFeedRepository.getDietsComments(id: dietId)).called(1);
|
||||
});
|
||||
|
||||
test('getComments - успешная загрузка комментариев для recipe', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const recipeId = 'recipe-slug';
|
||||
|
||||
when(mockFeedRepository.getRecipesComments(id: recipeId))
|
||||
.thenAnswer((_) async => testCommentModel);
|
||||
|
||||
await commentsCubit!.getComments(recipeId, CommentSourceType.recipe, true);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
verify(mockFeedRepository.getRecipesComments(id: recipeId)).called(1);
|
||||
});
|
||||
|
||||
test('getComments - успешная загрузка комментариев для plan', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const planSlug = 'plan-slug';
|
||||
|
||||
when(mockFeedRepository.getPlansComments(slug: planSlug))
|
||||
.thenAnswer((_) async => testCommentModel);
|
||||
|
||||
await commentsCubit!.getComments(planSlug, CommentSourceType.plan, true);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
verify(mockFeedRepository.getPlansComments(slug: planSlug)).called(1);
|
||||
});
|
||||
|
||||
test('getComments - переход в состояние loading при первом запросе', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const postId = '1';
|
||||
|
||||
when(mockFeedRepository.getBlogPostComments(id: postId))
|
||||
.thenAnswer((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
return testCommentModel;
|
||||
});
|
||||
|
||||
final future = commentsCubit!.getComments(postId, CommentSourceType.blogPost, true);
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
|
||||
expect(commentsCubit!.state, const CommentsState.loading());
|
||||
await future;
|
||||
expect(cubit.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = cubit.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment, mockComment);
|
||||
});
|
||||
|
||||
test('getComments - обработка ошибки', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const postId = '1';
|
||||
const id = '1';
|
||||
|
||||
when(mockFeedRepository.getBlogPostComments(id: postId))
|
||||
.thenThrow(Exception('Ошибка загрузки'));
|
||||
when(() => mockFeedRepository.getBlogPostComments(id: id))
|
||||
.thenThrow(Exception('Error'));
|
||||
|
||||
await commentsCubit!.getComments(postId, CommentSourceType.blogPost, true);
|
||||
await cubit.getComments(id, CommentSourceType.blogPost, true);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateError>());
|
||||
final errorState = commentsCubit!.state as CommentsStateError;
|
||||
expect(errorState.errorMessage, contains('Exception'));
|
||||
expect(cubit.state, isA<CommentsStateError>());
|
||||
final errorState = cubit.state as CommentsStateError;
|
||||
expect(errorState.errorMessage, contains('Error'));
|
||||
});
|
||||
|
||||
test('sendComment - успешная отправка комментария для blogPost', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
test('sendComment - отправка комментария для blogPost', () async {
|
||||
const id = '1';
|
||||
const commentText = 'Test comment';
|
||||
const mockComment = CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: null, lastPage: 1, links: [], perPage: 10, to: null, total: 0),
|
||||
);
|
||||
const postId = '1';
|
||||
const commentText = 'Тестовый комментарий';
|
||||
|
||||
// Устанавливаем начальное состояние
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: testCommentModel));
|
||||
when(() => mockFeedRepository.sendBlogPostComment(id: id, commentText: commentText, parentCommentId: null))
|
||||
.thenAnswer((_) async => {});
|
||||
when(() => mockFeedRepository.getBlogPostComments(id: id))
|
||||
.thenAnswer((_) async => mockComment);
|
||||
|
||||
when(mockFeedRepository.sendBlogPostComment(
|
||||
id: postId,
|
||||
commentText: commentText,
|
||||
parentCommentId: null,
|
||||
)).thenAnswer((_) async => Future.value());
|
||||
|
||||
when(mockFeedRepository.getBlogPostComments(id: postId))
|
||||
.thenAnswer((_) async => testCommentModel);
|
||||
|
||||
await commentsCubit!.sendComment(
|
||||
id: postId,
|
||||
await cubit.sendComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
commentSourceType: CommentSourceType.blogPost,
|
||||
);
|
||||
|
||||
verify(mockFeedRepository.sendBlogPostComment(
|
||||
id: postId,
|
||||
commentText: commentText,
|
||||
parentCommentId: null,
|
||||
)).called(1);
|
||||
verify(mockFeedRepository.getBlogPostComments(id: postId)).called(1);
|
||||
verify(() => mockFeedRepository.sendBlogPostComment(id: id, commentText: commentText, parentCommentId: null)).called(1);
|
||||
verify(() => mockFeedRepository.getBlogPostComments(id: id)).called(1);
|
||||
});
|
||||
|
||||
test('sendComment - обработка ошибки при отправке', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
test('deleteComment - успешное удаление', () async {
|
||||
final comment = Comment(
|
||||
id: 1,
|
||||
authorName: 'Test',
|
||||
authorSlug: 'test',
|
||||
authorProfileIcon: '',
|
||||
body: 'Test',
|
||||
childrenComments: [],
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
const postId = '1';
|
||||
const commentText = 'Тестовый комментарий';
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: testCommentModel));
|
||||
|
||||
when(mockFeedRepository.sendBlogPostComment(
|
||||
id: postId,
|
||||
commentText: commentText,
|
||||
parentCommentId: null,
|
||||
)).thenThrow(Exception('Ошибка отправки'));
|
||||
|
||||
await commentsCubit!.sendComment(
|
||||
id: postId,
|
||||
commentText: commentText,
|
||||
commentSourceType: CommentSourceType.blogPost,
|
||||
final mockComment = CommentModel(
|
||||
data: [comment],
|
||||
links: const Links(first: '', last: '', prev: null, next: null),
|
||||
meta: const Meta(currentPage: 1, from: null, lastPage: 1, links: [], perPage: 10, to: null, total: 0),
|
||||
);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(commentsCubit!.state, isA<CommentsStateError>());
|
||||
});
|
||||
// Сначала загружаем комментарии
|
||||
when(() => mockFeedRepository.getBlogPostComments(id: any(named: 'id')))
|
||||
.thenAnswer((_) async => mockComment);
|
||||
await cubit.getComments('1', CommentSourceType.blogPost);
|
||||
|
||||
test('deleteComment - успешное удаление комментария', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const commentId = 1;
|
||||
// Затем удаляем
|
||||
when(() => mockCommentsRepository.deleteComment(id: 1))
|
||||
.thenAnswer((_) async => {});
|
||||
|
||||
final commentWithData = testCommentModel.copyWith(
|
||||
data: [
|
||||
Comment(
|
||||
id: commentId,
|
||||
body: 'Комментарий для удаления',
|
||||
authorName: 'Автор',
|
||||
authorSlug: 'author-slug',
|
||||
authorProfileIcon: 'https://example.com/photo.jpg',
|
||||
createdAt: DateTime.now(),
|
||||
childrenComments: [],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: commentWithData));
|
||||
|
||||
when(mockCommentsRepository.deleteComment(id: commentId))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
final result = await commentsCubit!.deleteComment(id: commentId);
|
||||
final result = await cubit.deleteComment(id: 1);
|
||||
|
||||
expect(result, true);
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment.data, isEmpty);
|
||||
verify(mockCommentsRepository.deleteComment(id: commentId)).called(1);
|
||||
verify(() => mockCommentsRepository.deleteComment(id: 1)).called(1);
|
||||
});
|
||||
|
||||
test('deleteComment - возвращает false если состояние не loaded', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
test('updateComment - успешное обновление', () async {
|
||||
final comment = Comment(
|
||||
id: 1,
|
||||
authorName: 'Test',
|
||||
authorSlug: 'test',
|
||||
authorProfileIcon: '',
|
||||
body: 'Old text',
|
||||
childrenComments: [],
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
const commentId = 1;
|
||||
|
||||
commentsCubit!.emit(const CommentsState());
|
||||
|
||||
final result = await commentsCubit!.deleteComment(id: commentId);
|
||||
|
||||
expect(result, false);
|
||||
verifyNever(mockCommentsRepository.deleteComment(id: commentId));
|
||||
});
|
||||
|
||||
test('deleteComment - обработка ошибки при удалении', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const commentId = 1;
|
||||
|
||||
final commentWithData = testCommentModel.copyWith(
|
||||
data: [
|
||||
Comment(
|
||||
id: commentId,
|
||||
body: 'Комментарий',
|
||||
authorName: 'Автор',
|
||||
authorSlug: 'author-slug',
|
||||
authorProfileIcon: 'https://example.com/photo.jpg',
|
||||
createdAt: DateTime.now(),
|
||||
childrenComments: [],
|
||||
),
|
||||
],
|
||||
final mockComment = CommentModel(
|
||||
data: [comment],
|
||||
links: const Links(first: '', last: '', prev: null, next: null),
|
||||
meta: const Meta(currentPage: 1, from: null, lastPage: 1, links: [], perPage: 10, to: null, total: 0),
|
||||
);
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: commentWithData));
|
||||
// Сначала загружаем комментарии
|
||||
when(() => mockFeedRepository.getBlogPostComments(id: any(named: 'id')))
|
||||
.thenAnswer((_) async => mockComment);
|
||||
await cubit.getComments('1', CommentSourceType.blogPost);
|
||||
|
||||
when(mockCommentsRepository.deleteComment(id: commentId))
|
||||
.thenThrow(Exception('Ошибка удаления'));
|
||||
// Затем обновляем
|
||||
when(() => mockCommentsRepository.updateComment(id: 1, body: 'New text'))
|
||||
.thenAnswer((_) async => {});
|
||||
|
||||
final result = await commentsCubit!.deleteComment(id: commentId);
|
||||
|
||||
expect(result, false);
|
||||
// Состояние должно вернуться к исходному
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment.data.length, 1);
|
||||
});
|
||||
|
||||
test('updateComment - успешное обновление комментария', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const commentId = 1;
|
||||
const newBody = 'Обновлённый комментарий';
|
||||
|
||||
final commentWithData = testCommentModel.copyWith(
|
||||
data: [
|
||||
Comment(
|
||||
id: commentId,
|
||||
body: 'Старый комментарий',
|
||||
authorName: 'Автор',
|
||||
authorSlug: 'author-slug',
|
||||
authorProfileIcon: 'https://example.com/photo.jpg',
|
||||
createdAt: DateTime.now(),
|
||||
childrenComments: [],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: commentWithData));
|
||||
|
||||
when(mockCommentsRepository.updateComment(id: commentId, body: newBody))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
final result = await commentsCubit!.updateComment(id: commentId, body: newBody);
|
||||
final result = await cubit.updateComment(id: 1, body: 'New text');
|
||||
|
||||
expect(result, true);
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment.data.first.body, newBody);
|
||||
verify(mockCommentsRepository.updateComment(id: commentId, body: newBody)).called(1);
|
||||
});
|
||||
|
||||
test('updateComment - обработка ошибки при обновлении', () async {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const commentId = 1;
|
||||
const newBody = 'Обновлённый комментарий';
|
||||
const oldBody = 'Старый комментарий';
|
||||
|
||||
final commentWithData = testCommentModel.copyWith(
|
||||
data: [
|
||||
Comment(
|
||||
id: commentId,
|
||||
body: oldBody,
|
||||
authorName: 'Автор',
|
||||
authorSlug: 'author-slug',
|
||||
authorProfileIcon: 'https://example.com/photo.jpg',
|
||||
createdAt: DateTime.now(),
|
||||
childrenComments: [],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: commentWithData));
|
||||
|
||||
when(mockCommentsRepository.updateComment(id: commentId, body: newBody))
|
||||
.thenThrow(Exception('Ошибка обновления'));
|
||||
|
||||
final result = await commentsCubit!.updateComment(id: commentId, body: newBody);
|
||||
|
||||
expect(result, false);
|
||||
// Состояние должно вернуться к исходному
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.comment.data.first.body, oldBody);
|
||||
});
|
||||
|
||||
test('setReplyId - устанавливает ID ответа', () {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
const replyId = 5;
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: testCommentModel));
|
||||
|
||||
commentsCubit!.setReplyId(replyId);
|
||||
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.replyId, replyId);
|
||||
});
|
||||
|
||||
test('setReplyId - сбрасывает ID ответа', () {
|
||||
commentsCubit = CommentsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
commentsRepository: mockCommentsRepository,
|
||||
);
|
||||
|
||||
commentsCubit!.emit(CommentsState.loaded(comment: testCommentModel, replyId: 5));
|
||||
|
||||
commentsCubit!.setReplyId(null);
|
||||
|
||||
expect(commentsCubit!.state, isA<CommentsStateLoaded>());
|
||||
final loadedState = commentsCubit!.state as CommentsStateLoaded;
|
||||
expect(loadedState.replyId, isNull);
|
||||
verify(() => mockCommentsRepository.updateComment(id: 1, body: 'New text')).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
100
test/core/comments/comments_cubit/comments_state_test.dart
Normal file
100
test/core/comments/comments_cubit/comments_state_test.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
||||
import 'package:eatvio_app/core/models/pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/comment_model.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('CommentsState', () {
|
||||
test('creates initial state', () {
|
||||
const state = CommentsState();
|
||||
|
||||
expect(state, isA<CommentsState>());
|
||||
});
|
||||
|
||||
test('creates loading state', () {
|
||||
const state = CommentsState.loading();
|
||||
|
||||
expect(state, isA<CommentsStateLoading>());
|
||||
});
|
||||
|
||||
test('creates loaded state with comment', () {
|
||||
final comment = CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(
|
||||
currentPage: 1,
|
||||
from: null,
|
||||
lastPage: 1,
|
||||
links: [],
|
||||
path: null,
|
||||
perPage: 20,
|
||||
to: null,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final state = CommentsState.loaded(comment: comment);
|
||||
|
||||
expect(state, isA<CommentsStateLoaded>());
|
||||
expect((state as CommentsStateLoaded).comment, comment);
|
||||
});
|
||||
|
||||
test('creates loaded state with replyId', () {
|
||||
final comment = CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(
|
||||
currentPage: 1,
|
||||
from: null,
|
||||
lastPage: 1,
|
||||
links: [],
|
||||
path: null,
|
||||
perPage: 20,
|
||||
to: null,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final state = CommentsState.loaded(
|
||||
comment: comment,
|
||||
replyId: 123,
|
||||
);
|
||||
|
||||
expect((state as CommentsStateLoaded).replyId, 123);
|
||||
});
|
||||
|
||||
test('creates loaded state with editedCommentId', () {
|
||||
final comment = CommentModel(
|
||||
data: [],
|
||||
links: Links(first: '', last: '', prev: null, next: null),
|
||||
meta: Meta(
|
||||
currentPage: 1,
|
||||
from: null,
|
||||
lastPage: 1,
|
||||
links: [],
|
||||
path: null,
|
||||
perPage: 20,
|
||||
to: null,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
final state = CommentsState.loaded(
|
||||
comment: comment,
|
||||
editedCommentId: 456,
|
||||
);
|
||||
|
||||
expect((state as CommentsStateLoaded).editedCommentId, 456);
|
||||
});
|
||||
|
||||
test('creates error state', () {
|
||||
const state = CommentsState.error(
|
||||
errorMessage: 'Test error',
|
||||
);
|
||||
|
||||
expect(state, isA<CommentsStateError>());
|
||||
expect((state as CommentsStateError).errorMessage, 'Test error');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,34 +3,54 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('CommentUpdateModel', () {
|
||||
test('создание модели через конструктор', () {
|
||||
final model = CommentUpdateModel(body: 'Обновлённый текст комментария');
|
||||
test('creates model with body', () {
|
||||
final model = CommentUpdateModel(
|
||||
body: 'Test comment body',
|
||||
);
|
||||
|
||||
expect(model.body, 'Обновлённый текст комментария');
|
||||
expect(model.body, 'Test comment body');
|
||||
});
|
||||
|
||||
test('создание модели из JSON', () {
|
||||
final json = {'body': 'Текст комментария'};
|
||||
final model = CommentUpdateModel.fromJson(json);
|
||||
test('creates model with empty body', () {
|
||||
final model = CommentUpdateModel(
|
||||
body: '',
|
||||
);
|
||||
|
||||
expect(model.body, 'Текст комментария');
|
||||
expect(model.body, '');
|
||||
});
|
||||
|
||||
test('преобразование модели в JSON', () {
|
||||
final model = CommentUpdateModel(body: 'Текст комментария');
|
||||
final json = model.toJson();
|
||||
test('creates model with long body', () {
|
||||
final longBody = 'A' * 1000;
|
||||
final model = CommentUpdateModel(
|
||||
body: longBody,
|
||||
);
|
||||
|
||||
expect(json['body'], 'Текст комментария');
|
||||
expect(model.body, longBody);
|
||||
expect(model.body.length, 1000);
|
||||
});
|
||||
|
||||
test('равенство моделей', () {
|
||||
final model1 = CommentUpdateModel(body: 'Текст комментария');
|
||||
final model2 = CommentUpdateModel(body: 'Текст комментария');
|
||||
final model3 = CommentUpdateModel(body: 'Другой текст');
|
||||
test('can be compared', () {
|
||||
final model1 = CommentUpdateModel(
|
||||
body: 'Test',
|
||||
);
|
||||
|
||||
expect(model1, equals(model2));
|
||||
expect(model1, isNot(equals(model3)));
|
||||
final model2 = CommentUpdateModel(
|
||||
body: 'Test',
|
||||
);
|
||||
|
||||
expect(model1 == model2, true);
|
||||
});
|
||||
|
||||
test('different bodies are not equal', () {
|
||||
final model1 = CommentUpdateModel(
|
||||
body: 'Test 1',
|
||||
);
|
||||
|
||||
final model2 = CommentUpdateModel(
|
||||
body: 'Test 2',
|
||||
);
|
||||
|
||||
expect(model1 == model2, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/comments/presentation/comments_actions_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('CommentsActionsBottomSheet', () {
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: S.delegate.supportedLocales,
|
||||
home: Scaffold(
|
||||
body: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('displays comments actions bottom sheet', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(
|
||||
CommentsActionsBottomSheet(
|
||||
isMyComment: false,
|
||||
reply: () {},
|
||||
copy: () {},
|
||||
edit: (text) {},
|
||||
delete: () {},
|
||||
complain: () {},
|
||||
commentText: 'Test comment',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CommentsActionsBottomSheet), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('displays edit option for my comment', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(
|
||||
CommentsActionsBottomSheet(
|
||||
isMyComment: true,
|
||||
reply: null,
|
||||
copy: () {},
|
||||
edit: (text) {},
|
||||
delete: () {},
|
||||
complain: () {},
|
||||
commentText: 'Test comment',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CommentsActionsBottomSheet), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('displays reply option when reply is not null', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(
|
||||
CommentsActionsBottomSheet(
|
||||
isMyComment: false,
|
||||
reply: () {},
|
||||
copy: () {},
|
||||
edit: (text) {},
|
||||
delete: () {},
|
||||
complain: () {},
|
||||
commentText: 'Test comment',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CommentsActionsBottomSheet), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows edit mode when edit is tapped', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(
|
||||
CommentsActionsBottomSheet(
|
||||
isMyComment: true,
|
||||
reply: null,
|
||||
copy: () {},
|
||||
edit: (text) {},
|
||||
delete: () {},
|
||||
complain: () {},
|
||||
commentText: 'Test comment',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CommentsActionsBottomSheet), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -7,74 +7,60 @@ import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'comments_repository_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<CommentsService>()])
|
||||
@GenerateMocks([CommentsService])
|
||||
void main() {
|
||||
group('CommentsRepository', () {
|
||||
late CommentsRepository commentsRepository;
|
||||
late MockCommentsService mockCommentsService;
|
||||
late MockCommentsService mockService;
|
||||
late CommentsRepository repository;
|
||||
|
||||
setUp(() {
|
||||
mockCommentsService = MockCommentsService();
|
||||
commentsRepository = CommentsRepository(service: mockCommentsService);
|
||||
mockService = MockCommentsService();
|
||||
repository = CommentsRepository(service: mockService);
|
||||
});
|
||||
|
||||
test('deleteComment - успешное удаление комментария', () async {
|
||||
const commentId = 1;
|
||||
test('deleteComment calls service.deleteComment', () async {
|
||||
const id = 1;
|
||||
|
||||
when(mockCommentsService.deleteComment(commentId))
|
||||
when(mockService.deleteComment(id))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
await commentsRepository.deleteComment(id: commentId);
|
||||
await repository.deleteComment(id: id);
|
||||
|
||||
verify(mockCommentsService.deleteComment(commentId)).called(1);
|
||||
verify(mockService.deleteComment(id)).called(1);
|
||||
});
|
||||
|
||||
test('deleteComment - пробрасывает исключение', () async {
|
||||
const commentId = 1;
|
||||
test('deleteComment rethrows exception when service throws', () async {
|
||||
const id = 1;
|
||||
final exception = Exception('Test error');
|
||||
|
||||
when(mockCommentsService.deleteComment(commentId))
|
||||
.thenThrow(Exception('Ошибка удаления'));
|
||||
when(mockService.deleteComment(id))
|
||||
.thenThrow(exception);
|
||||
|
||||
expect(
|
||||
() => commentsRepository.deleteComment(id: commentId),
|
||||
throwsException,
|
||||
);
|
||||
expect(() => repository.deleteComment(id: id), throwsException);
|
||||
});
|
||||
|
||||
test('updateComment - успешное обновление комментария', () async {
|
||||
const commentId = 1;
|
||||
const newBody = 'Обновлённый текст комментария';
|
||||
final updateModel = CommentUpdateModel(body: newBody);
|
||||
test('updateComment calls service.updateComment with correct model', () async {
|
||||
const id = 1;
|
||||
const body = 'Updated comment text';
|
||||
final updateModel = CommentUpdateModel(body: body);
|
||||
|
||||
when(mockCommentsService.updateComment(commentId, updateModel))
|
||||
when(mockService.updateComment(id, updateModel))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
await commentsRepository.updateComment(id: commentId, body: newBody);
|
||||
await repository.updateComment(id: id, body: body);
|
||||
|
||||
verify(mockCommentsService.updateComment(
|
||||
commentId,
|
||||
argThat(
|
||||
predicate<CommentUpdateModel>(
|
||||
(m) => m.body == newBody,
|
||||
),
|
||||
),
|
||||
)).called(1);
|
||||
verify(mockService.updateComment(id, updateModel)).called(1);
|
||||
});
|
||||
|
||||
test('updateComment - пробрасывает исключение', () async {
|
||||
const commentId = 1;
|
||||
const newBody = 'Обновлённый текст';
|
||||
test('updateComment rethrows exception when service throws', () async {
|
||||
const id = 1;
|
||||
const body = 'Updated comment text';
|
||||
final exception = Exception('Test error');
|
||||
|
||||
when(mockCommentsService.updateComment(
|
||||
commentId,
|
||||
any,
|
||||
)).thenThrow(Exception('Ошибка обновления'));
|
||||
when(mockService.updateComment(id, any))
|
||||
.thenThrow(exception);
|
||||
|
||||
expect(
|
||||
() => commentsRepository.updateComment(id: commentId, body: newBody),
|
||||
throwsException,
|
||||
);
|
||||
expect(() => repository.updateComment(id: id, body: body), throwsException);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user