forked from eatvio/eatvio_app
feat(personalized_feed): fixed photos rendering and fixed profile posts rendering
This commit is contained in:
@@ -150,6 +150,19 @@ void main() {
|
||||
expect(model.videoUrlThumb, 'media/1/thumb.jpg');
|
||||
});
|
||||
|
||||
test('parses profile search_by_author fixture with gallery and video', () {
|
||||
final posts = FeedFixtures.blogPostsByAuthor();
|
||||
|
||||
expect(posts, hasLength(2));
|
||||
expect(posts.first.images, hasLength(2));
|
||||
expect(posts.first.images.first.orientation, 'horizontal');
|
||||
expect(posts.first.images[1].orientation, 'vertical');
|
||||
expect(posts.first.videoEmbedUrl, contains('rutube.ru/play/embed'));
|
||||
expect(posts.first.videoUrlThumb, 'media/1/thumb.jpg');
|
||||
expect(posts[1].images, hasLength(2));
|
||||
expect(posts[1].videoEmbedUrl, isNull);
|
||||
});
|
||||
|
||||
test('creates FeedBlogPostModel with different states', () {
|
||||
const author = Author(
|
||||
authorSlug: 'author',
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import '../../../../../fixtures/feed_fixtures.dart';
|
||||
import 'profile_cubit_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([
|
||||
@@ -267,20 +268,10 @@ void main() {
|
||||
authorPlaceInRating: null,
|
||||
);
|
||||
|
||||
final blogPost = FeedBlogPostModel(
|
||||
id: 1,
|
||||
state: 'published',
|
||||
title: 'Test Post',
|
||||
description: 'Test Content',
|
||||
image: 'https://example.com/image.jpg',
|
||||
final blogPost = FeedFixtures.feedBlogPostGalleryAndVideo().copyWith(
|
||||
author: author,
|
||||
videoUrl: '',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
totalLikes: 10,
|
||||
totalComments: 5,
|
||||
totalViews: 100,
|
||||
wasLikedByMe: false,
|
||||
totalLikes: 10,
|
||||
);
|
||||
|
||||
const profileDetails = ProfileDetailsModel(
|
||||
|
||||
@@ -5,20 +5,20 @@ import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/app_initializer.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_media_content.dart';
|
||||
import 'package:eatvio_app/features/profile/models/profile_model.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/widgets/profile_blog_post_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
|
||||
import '../../../../fixtures/feed_fixtures.dart';
|
||||
import 'profile_blog_post_widget_smoke_test.mocks.dart';
|
||||
|
||||
class _FakeHttpClient implements HttpClient {
|
||||
@@ -35,7 +35,6 @@ class _FakeHttpClient implements HttpClient {
|
||||
@override
|
||||
Future<HttpClientRequest> getUrl(Uri url) async => _FakeHttpClientRequest();
|
||||
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
@@ -51,73 +50,10 @@ class _FakeHttpClientRequest implements HttpClientRequest {
|
||||
class _FakeHttpClientResponse extends Stream<List<int>> implements HttpClientResponse {
|
||||
// 1x1 transparent PNG.
|
||||
static final List<int> _png = <int>[
|
||||
0x89,
|
||||
0x50,
|
||||
0x4E,
|
||||
0x47,
|
||||
0x0D,
|
||||
0x0A,
|
||||
0x1A,
|
||||
0x0A,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0D,
|
||||
0x49,
|
||||
0x48,
|
||||
0x44,
|
||||
0x52,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x08,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1F,
|
||||
0x15,
|
||||
0xC4,
|
||||
0x89,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0A,
|
||||
0x49,
|
||||
0x44,
|
||||
0x41,
|
||||
0x54,
|
||||
0x78,
|
||||
0x9C,
|
||||
0x63,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x01,
|
||||
0x0D,
|
||||
0x0A,
|
||||
0x2D,
|
||||
0xB4,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x49,
|
||||
0x45,
|
||||
0x4E,
|
||||
0x44,
|
||||
0xAE,
|
||||
0x42,
|
||||
0x60,
|
||||
0x82,
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41,
|
||||
0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D, 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49,
|
||||
0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -150,81 +86,53 @@ void main() {
|
||||
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
|
||||
});
|
||||
|
||||
testWidgets('renders blog post card', (tester) async {
|
||||
final post = FeedBlogPostModel(
|
||||
id: 1,
|
||||
state: 'published',
|
||||
image: 'test.png',
|
||||
description: 'Hello',
|
||||
author: const Author(
|
||||
authorSlug: 'author',
|
||||
authorName: 'Author',
|
||||
authorProfileIcon: 'https://example.com/icon.png',
|
||||
authorIsMe: false,
|
||||
isEntitySubscribedByMe: false,
|
||||
),
|
||||
videoUrl: '',
|
||||
createdAt: DateTime(2025, 1, 1),
|
||||
updatedAt: DateTime(2025, 1, 1),
|
||||
title: 'Title',
|
||||
totalLikes: 0,
|
||||
totalViews: 0,
|
||||
totalComments: 0,
|
||||
wasLikedByMe: false,
|
||||
);
|
||||
const profile = ProfileModel(
|
||||
id: 1,
|
||||
name: 'User',
|
||||
email: 'u@e.com',
|
||||
phoneConfirmationState: 'confirmed',
|
||||
slug: 'user',
|
||||
isPublicProfile: true,
|
||||
isBlockedByMe: false,
|
||||
isBlockedByAuthor: false,
|
||||
isBlockedByUser: false,
|
||||
isEntitySubscribedByMe: false,
|
||||
profilePhotoUrl: '',
|
||||
roles: [],
|
||||
badges: [],
|
||||
subscriptions: [],
|
||||
subscribers: [],
|
||||
);
|
||||
|
||||
const profile = ProfileModel(
|
||||
id: 1,
|
||||
name: 'User',
|
||||
email: 'u@e.com',
|
||||
phoneConfirmationState: 'confirmed',
|
||||
slug: 'user',
|
||||
isPublicProfile: true,
|
||||
isBlockedByMe: false,
|
||||
isBlockedByAuthor: false,
|
||||
isBlockedByUser: false,
|
||||
isEntitySubscribedByMe: false,
|
||||
profilePhotoUrl: '',
|
||||
roles: [],
|
||||
badges: [],
|
||||
subscriptions: [],
|
||||
subscribers: [],
|
||||
);
|
||||
|
||||
const details = ProfileDetailsModel(
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
birthdate: '1990-01-01',
|
||||
gender: 'male',
|
||||
country: 'Russia',
|
||||
city: 'Moscow',
|
||||
lang: 'ru',
|
||||
weight: 70.0,
|
||||
height: 175,
|
||||
chestCircumference: 100,
|
||||
waistCircumference: 80,
|
||||
hipCircumference: 90,
|
||||
bloodPressureUp: 120,
|
||||
bloodPressureDown: 80,
|
||||
hasHypo: false,
|
||||
hasHyper: false,
|
||||
aboutAlergy: '',
|
||||
aboutTakingPils: '',
|
||||
aboutSportHobbies: '',
|
||||
favoriteFoods: '',
|
||||
unfavoriteFoods: '',
|
||||
additionalInfo: '',
|
||||
isMoodFillable: true,
|
||||
isBedtimeFillable: true,
|
||||
activityCoefficient: 1.5,
|
||||
);
|
||||
|
||||
final loadedState = ProfileState.loaded(profile: profile, profileDetails: details, blogPosts: [post], isMyProfile: true, certPhotos: []);
|
||||
final cubit = MockProfileCubit();
|
||||
when(cubit.state).thenReturn(loadedState);
|
||||
when(cubit.stream).thenAnswer((_) => const Stream<ProfileState>.empty());
|
||||
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
const details = ProfileDetailsModel(
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
birthdate: '1990-01-01',
|
||||
gender: 'male',
|
||||
country: 'Russia',
|
||||
city: 'Moscow',
|
||||
lang: 'ru',
|
||||
weight: 70.0,
|
||||
height: 175,
|
||||
chestCircumference: 100,
|
||||
waistCircumference: 80,
|
||||
hipCircumference: 90,
|
||||
bloodPressureUp: 120,
|
||||
bloodPressureDown: 80,
|
||||
hasHypo: false,
|
||||
hasHyper: false,
|
||||
aboutAlergy: '',
|
||||
aboutTakingPils: '',
|
||||
aboutSportHobbies: '',
|
||||
favoriteFoods: '',
|
||||
unfavoriteFoods: '',
|
||||
additionalInfo: '',
|
||||
isMoodFillable: true,
|
||||
isBedtimeFillable: true,
|
||||
activityCoefficient: 1.5,
|
||||
);
|
||||
|
||||
Future<void> pumpPost(WidgetTester tester, ProfileCubit cubit) async {
|
||||
await HttpOverrides.runZoned(() async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@@ -236,15 +144,60 @@ void main() {
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Scaffold(body: ProfileBlogPostWidget(index: 0, profileCubit: cubit)),
|
||||
home: Scaffold(
|
||||
body: MediaQuery(
|
||||
data: const MediaQueryData(size: Size(800, 1600)),
|
||||
child: SingleChildScrollView(
|
||||
child: ProfileBlogPostWidget(index: 0, profileCubit: cubit),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}, createHttpClient: (context) => _FakeHttpClient());
|
||||
}, createHttpClient: (_) => _FakeHttpClient());
|
||||
}
|
||||
|
||||
expect(find.text('Регистрация'), findsNothing);
|
||||
expect(find.text('Title'), findsOneWidget);
|
||||
expect(find.text('Author'), findsOneWidget);
|
||||
testWidgets('renders gallery and video media content', (tester) async {
|
||||
final post = FeedFixtures.feedBlogPostGalleryAndVideo();
|
||||
final loadedState = ProfileState.loaded(profile: profile, profileDetails: details, blogPosts: [post], isMyProfile: true, certPhotos: []);
|
||||
final cubit = MockProfileCubit();
|
||||
when(cubit.state).thenReturn(loadedState);
|
||||
when(cubit.stream).thenAnswer((_) => const Stream<ProfileState>.empty());
|
||||
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
|
||||
await pumpPost(tester, cubit);
|
||||
|
||||
expect(find.byType(BlogPostMediaContent), findsOneWidget);
|
||||
expect(find.textContaining('Галерея и видео'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('renders gallery-only media content', (tester) async {
|
||||
final post = FeedFixtures.feedBlogPostWithGallery();
|
||||
final loadedState = ProfileState.loaded(profile: profile, profileDetails: details, blogPosts: [post], isMyProfile: true, certPhotos: []);
|
||||
final cubit = MockProfileCubit();
|
||||
when(cubit.state).thenReturn(loadedState);
|
||||
when(cubit.stream).thenAnswer((_) => const Stream<ProfileState>.empty());
|
||||
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
|
||||
await pumpPost(tester, cubit);
|
||||
|
||||
expect(find.byType(BlogPostMediaContent), findsOneWidget);
|
||||
expect(find.text('Галерея'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders video-only media content', (tester) async {
|
||||
final post = FeedFixtures.feedBlogPostVideoOnly();
|
||||
final loadedState = ProfileState.loaded(profile: profile, profileDetails: details, blogPosts: [post], isMyProfile: true, certPhotos: []);
|
||||
final cubit = MockProfileCubit();
|
||||
when(cubit.state).thenReturn(loadedState);
|
||||
when(cubit.stream).thenAnswer((_) => const Stream<ProfileState>.empty());
|
||||
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
|
||||
await pumpPost(tester, cubit);
|
||||
|
||||
expect(find.byType(BlogPostMediaContent), findsOneWidget);
|
||||
expect(find.text('Видео'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import 'package:eatvio_app/features/profile/models/circumference_model.dart';
|
||||
import 'package:eatvio_app/features/profile/models/profile_model.dart';
|
||||
import 'package:eatvio_app/features/profile/models/weight_model.dart';
|
||||
import 'package:eatvio_app/features/profile/services/profile_service.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../fixtures/feed_fixtures.dart';
|
||||
import 'profile_service_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<ProfileService>()])
|
||||
@@ -72,9 +74,17 @@ void main() {
|
||||
expect(await mockProfileService.getProfileDetailsBySlug(slug: 'slug'), isA<ProfileDetailsModel>());
|
||||
});
|
||||
|
||||
test('getBlogPostByAuthorSlug', () async {
|
||||
when(mockProfileService.getBlogPostByAuthorSlug(slug: '')).thenAnswer((_) async => []);
|
||||
expect(await mockProfileService.getBlogPostByAuthorSlug(slug: 'slug'), isA<List<FeedBlogPostModel>>());
|
||||
test('getBlogPostByAuthorSlug returns posts with gallery and video fields', () async {
|
||||
final posts = FeedFixtures.blogPostsByAuthor();
|
||||
when(mockProfileService.getBlogPostByAuthorSlug(slug: 'slug')).thenAnswer((_) async => posts);
|
||||
|
||||
final result = await mockProfileService.getBlogPostByAuthorSlug(slug: 'slug');
|
||||
|
||||
expect(result, isA<List<FeedBlogPostModel>>());
|
||||
expect(result, hasLength(2));
|
||||
expect(result.first.images, isNotEmpty);
|
||||
expect(result.first.videoEmbedUrl, isNotNull);
|
||||
expect(result.first.videoUrlThumb, isNotNull);
|
||||
});
|
||||
|
||||
test('getWeight', () async {
|
||||
|
||||
6
test/fixtures/feed_fixtures.dart
vendored
6
test/fixtures/feed_fixtures.dart
vendored
@@ -72,4 +72,10 @@ class FeedFixtures {
|
||||
static FeedBlogPostModel feedBlogPostGalleryAndVideo() {
|
||||
return FeedBlogPostModel.fromJson(feedBlogPostGalleryAndVideoResponseJson());
|
||||
}
|
||||
|
||||
static List<FeedBlogPostModel> blogPostsByAuthor() {
|
||||
return FixtureReader.readAsList('profile/blog_posts_by_author_response.json')
|
||||
.map((item) => FeedBlogPostModel.fromJson(item as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
80
test/fixtures/profile/blog_posts_by_author_response.json
vendored
Normal file
80
test/fixtures/profile/blog_posts_by_author_response.json
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"state": "active",
|
||||
"image": "media/1/cover.jpg",
|
||||
"images": [
|
||||
{
|
||||
"id": 10,
|
||||
"url": "media/1/a.jpg",
|
||||
"position": 1,
|
||||
"orientation": "horizontal"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"url": "media/1/b.jpg",
|
||||
"position": 2,
|
||||
"orientation": "vertical"
|
||||
}
|
||||
],
|
||||
"description": "Описание поста профиля",
|
||||
"author": {
|
||||
"author_slug": "test-user",
|
||||
"author_name": "Test User",
|
||||
"author_profile_icon": "https://example.com/photo.jpg",
|
||||
"author_is_me": true,
|
||||
"is_entity_subscribed_by_me": false,
|
||||
"author_place_in_rating": null
|
||||
},
|
||||
"video_url": "https://rutube.ru/video/6a62f217aa99daf00af8fa6cebebf218/",
|
||||
"video_url_embed": "https://rutube.ru/play/embed/6a62f217aa99daf00af8fa6cebebf218/",
|
||||
"video_url_thumb": "media/1/thumb.jpg",
|
||||
"created_at": "2024-01-15T12:00:00Z",
|
||||
"updated_at": "2024-01-15T12:00:00Z",
|
||||
"title": "Пост профиля",
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"was_liked_by_me": false,
|
||||
"is_favorite": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"state": "active",
|
||||
"image": "media/2/a.jpg",
|
||||
"images": [
|
||||
{
|
||||
"id": 20,
|
||||
"url": "media/2/a.jpg",
|
||||
"position": 1,
|
||||
"orientation": "horizontal"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"url": "media/2/b.jpg",
|
||||
"position": 2,
|
||||
"orientation": "horizontal"
|
||||
}
|
||||
],
|
||||
"description": "Галерея без видео",
|
||||
"author": {
|
||||
"author_slug": "test-user",
|
||||
"author_name": "Test User",
|
||||
"author_profile_icon": "https://example.com/photo.jpg",
|
||||
"author_is_me": true,
|
||||
"is_entity_subscribed_by_me": false,
|
||||
"author_place_in_rating": null
|
||||
},
|
||||
"video_url": "",
|
||||
"video_url_embed": null,
|
||||
"video_url_thumb": null,
|
||||
"created_at": "2024-01-16T12:00:00Z",
|
||||
"updated_at": "2024-01-16T12:00:00Z",
|
||||
"title": "Галерея",
|
||||
"total_likes": 3,
|
||||
"total_views": 40,
|
||||
"total_comments": 1,
|
||||
"was_liked_by_me": false,
|
||||
"is_favorite": false
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user