fix: issue-942, issue-967, fixed images positioning for blog posts

This commit is contained in:
misha
2026-08-28 10:05:40 +03:00
parent 8f933118a3
commit 10af279d87
22 changed files with 484 additions and 84 deletions

View File

@@ -120,6 +120,29 @@ void main() {
expect(find.text('Заблокировать автора'), findsOneWidget);
});
testWidgets('CustomPopup for non-author admin hides complain but keeps block', (tester) async {
await tester.pumpWidget(
wrap(
const CustomPopup(
isAuthor: false,
isAuthorAdmin: true,
complainedContentType: ComplainedContent.plan,
path: 'plans/slug',
slug: 'slug',
userSlug: 'admin-user',
),
),
);
await tester.tap(find.byIcon(Icons.more_horiz_rounded));
await tester.pumpAndSettle();
expect(find.text('Скопировать ссылку'), findsOneWidget);
expect(find.text('Поделиться'), findsOneWidget);
expect(find.text('Пожаловаться'), findsNothing);
expect(find.text('Заблокировать автора'), findsOneWidget);
});
testWidgets('CustomPopup for author recipe does not show edit action', (tester) async {
await tester.pumpWidget(
wrap(

View File

@@ -24,6 +24,7 @@ void main() {
String? slug,
int? id,
bool? disableActions,
bool isAuthorAdmin = false,
}) {
return MaterialApp(
theme: ThemeHelper().themeData(),
@@ -42,6 +43,7 @@ void main() {
slug: slug,
id: id,
disableActions: disableActions,
isAuthorAdmin: isAuthorAdmin,
),
),
);
@@ -71,7 +73,23 @@ void main() {
);
await tester.pumpAndSettle();
expect(find.byType(MoreOptions), findsOneWidget);
expect(find.text('Пожаловаться'), findsOneWidget);
});
testWidgets('hides complain option when author is admin', (tester) async {
await tester.pumpWidget(
createTestWidget(
complainedContentType: ComplainedContent.diet,
id: 1,
slug: 'test-slug',
isAuthorAdmin: true,
),
);
await tester.pumpAndSettle();
expect(find.text('Пожаловаться'), findsNothing);
expect(find.text('Скопировать ссылку'), findsOneWidget);
expect(find.text('Поделиться'), findsOneWidget);
});
testWidgets('disables actions when disableActions is true', (tester) async {

View File

@@ -56,6 +56,33 @@ void main() {
});
});
group('Author', () {
test('fromJson читает author_roles и определяет админа', () {
final author = Author.fromJson({
'author_slug': 'admin-user',
'author_name': 'Admin',
'author_profile_icon': 'icon.jpg',
'author_is_me': false,
'author_roles': ['admin', 'consultant'],
});
expect(author.authorRoles, ['admin', 'consultant']);
expect(author.isAdmin, isTrue);
});
test('fromJson без author_roles не считает автора админом', () {
final author = Author.fromJson({
'author_slug': 'regular-user',
'author_name': 'User',
'author_profile_icon': 'icon.jpg',
'author_is_me': false,
});
expect(author.authorRoles, isEmpty);
expect(author.isAdmin, isFalse);
});
});
group('DietState', () {
test('toString возвращает правильные значения', () {
expect(DietState.active.toString(), 'active');

View File

@@ -0,0 +1,88 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/features/feed/models/post_feed_pagination_model.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_media_content.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';
void main() {
const gallery = [
FeedGalleryImage(
url: 'https://example.com/a.jpg',
orientation: 'horizontal',
),
FeedGalleryImage(url: 'https://example.com/b.jpg', orientation: 'vertical'),
];
Widget wrap(Widget child) {
return MaterialApp(
home: MediaQuery(
data: const MediaQueryData(size: Size(390, 844)),
child: Scaffold(body: child),
),
);
}
BlogPostMediaContent media({
List<FeedGalleryImage> images = gallery,
String imageUrl = '',
}) {
return BlogPostMediaContent(
galleryImages: images,
imageUrl: imageUrl,
videoEmbedUrl: null,
videoUrlThumb: null,
currentGalleryIndex: 0,
onOpenGallery: (_, _) {},
onGalleryChanged: (_) {},
);
}
testWidgets('gallery slides use fitWidth so photos fill the card width', (
tester,
) async {
await mockNetworkImagesFor(() async {
await tester.pumpWidget(wrap(media()));
await tester.pump();
final images = tester.widgetList<CachedNetworkImage>(
find.byType(CachedNetworkImage),
);
expect(images, isNotEmpty);
for (final image in images) {
expect(image.fit, BoxFit.fitWidth);
}
});
});
testWidgets('single gallery image uses fitWidth without a forced height', (
tester,
) async {
await mockNetworkImagesFor(() async {
await tester.pumpWidget(wrap(media(images: [gallery.first])));
await tester.pump();
final image = tester.widget<CachedNetworkImage>(
find.byType(CachedNetworkImage),
);
expect(image.fit, BoxFit.fitWidth);
expect(image.height, isNull);
});
});
testWidgets('cover-only image keeps fitWidth', (tester) async {
await mockNetworkImagesFor(() async {
await tester.pumpWidget(
wrap(
media(images: const [], imageUrl: 'https://example.com/cover.jpg'),
),
);
await tester.pump();
final image = tester.widget<CachedNetworkImage>(
find.byType(CachedNetworkImage),
);
expect(image.fit, BoxFit.fitWidth);
});
});
}

View File

@@ -1,6 +1,7 @@
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/widgets/loader.dart';
import 'package:eatvio_app/features/glossary/presentation/pages/glossary_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -46,6 +47,7 @@ void main() {
expect(find.byType(GlossaryScreen), findsOneWidget);
expect(find.byType(Scaffold), findsOneWidget);
expect(find.text('Глоссарий'), findsOneWidget);
expect(find.byType(Loader), findsOneWidget);
});
testWidgets('GlossaryScreen создается с правильным passKey', (WidgetTester tester) async {
@@ -71,6 +73,66 @@ void main() {
expect(find.byType(GlossaryScreen), findsOneWidget);
});
testWidgets('показывает стандартный лоадер при загрузке WebView', (WidgetTester tester) async {
const passKey = 'test-pass-key';
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const GlossaryScreen(passKey: passKey),
),
],
);
await tester.pumpWidget(
MaterialApp.router(
routerConfig: router,
),
);
await tester.pump();
expect(find.byType(Loader), findsOneWidget);
});
});
group('shouldHideGlossaryLoader', () {
test('не скрывает лоадер на странице login_with_pass_key', () {
expect(
shouldHideGlossaryLoader(
url: 'https://eatvio.ru/login_with_pass_key?id=1',
progress: 100,
),
isFalse,
);
});
test('не скрывает лоадер пока URL ещё не известен', () {
expect(shouldHideGlossaryLoader(url: '', progress: 80), isFalse);
});
test('не скрывает лоадер пока прогресс глоссария ниже порога', () {
expect(
shouldHideGlossaryLoader(
url: 'https://eatvio.ru/product_nutritional_value',
progress: 39,
),
isFalse,
);
});
test('скрывает лоадер когда страница глоссария уже начала отрисовываться', () {
expect(
shouldHideGlossaryLoader(
url: 'https://eatvio.ru/product_nutritional_value',
progress: 40,
),
isTrue,
);
});
});
}

View File

@@ -165,6 +165,11 @@ class FakePlatformNavigationDelegate extends PlatformNavigationDelegate {
// No-op implementation for testing
}
@override
Future<void> setOnProgress(dynamic params) async {
// No-op implementation for testing
}
@override
Future<void> setOnWebResourceError(dynamic params) async {
// No-op implementation for testing