add ticker page bloc and avatar disk cache
This commit is contained in:
@@ -23,15 +23,11 @@ Widget _host({
|
||||
List<TickerNavSection>? sections,
|
||||
String? initialSlug,
|
||||
void Function(String? slug)? onSelectionChanged,
|
||||
String? homePublishedAt,
|
||||
VoidCallback? onRefreshHome,
|
||||
}) => MaterialApp(
|
||||
home: TickerScaffold(
|
||||
sections: sections ?? _sections(),
|
||||
initialSlug: initialSlug,
|
||||
onSelectionChanged: onSelectionChanged,
|
||||
homePublishedAt: homePublishedAt,
|
||||
onRefreshHome: onRefreshHome,
|
||||
homeBuilder: (context, onLinkTap) => const Text('HOME'),
|
||||
pageBuilder: (context, slug, onLinkTap) => Text('PAGE:$slug'),
|
||||
),
|
||||
@@ -231,50 +227,6 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('home date button', () {
|
||||
testWidgets('shows the publish date on home and refreshes on tap', (
|
||||
tester,
|
||||
) async {
|
||||
var refreshed = 0;
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
homePublishedAt: '2026-02-17T14:30:00',
|
||||
onRefreshHome: () => refreshed++,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('17.02.2026, 14:30'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('17.02.2026, 14:30'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(refreshed, 1);
|
||||
});
|
||||
|
||||
testWidgets('hides the date button when there is no date', (tester) async {
|
||||
await tester.pumpWidget(_host());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('hides the date button once a page is open', (tester) async {
|
||||
tester.view.physicalSize = const Size(1200, 2000);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
await tester.pumpWidget(_host(homePublishedAt: '2026-02-17T14:30:00'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
});
|
||||
});
|
||||
|
||||
group('back gesture', () {
|
||||
testWidgets('pops from a sub-page back to home via the tab navigator', (
|
||||
tester,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:marianum_mobile/view/pages/ticker/widgets/ticker_updated_bar.dart';
|
||||
|
||||
Widget _host(String? publishedAt) =>
|
||||
MaterialApp(home: Scaffold(body: TickerUpdatedBar(publishedAt: publishedAt)));
|
||||
|
||||
void main() {
|
||||
setUpAll(() async {
|
||||
await Jiffy.setLocale('de');
|
||||
});
|
||||
|
||||
testWidgets('renders the formatted publish date', (tester) async {
|
||||
await tester.pumpWidget(_host('2026-02-17T14:30:00'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Aktualisiert am 17.02.2026 14:30'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.schedule), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders nothing without a date', (tester) async {
|
||||
await tester.pumpWidget(_host(null));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
expect(find.textContaining('Aktualisiert'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('renders nothing for an unparseable date', (tester) async {
|
||||
await tester.pumpWidget(_host('not-a-date'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:marianum_mobile/widget/avatar_disk_cache.dart';
|
||||
|
||||
void main() {
|
||||
group('AvatarDiskCache.fileName', () {
|
||||
test('user files carry the size, group files do not', () {
|
||||
expect(
|
||||
AvatarDiskCache.fileName(id: 'alice', isGroup: false, size: 256),
|
||||
'u_616c696365_256',
|
||||
);
|
||||
expect(
|
||||
AvatarDiskCache.fileName(id: 'alice', isGroup: false, size: 64),
|
||||
'u_616c696365_64',
|
||||
);
|
||||
// Groups are served at one fixed size — the size argument is ignored.
|
||||
expect(
|
||||
AvatarDiskCache.fileName(id: 'room1', isGroup: true, size: 512),
|
||||
AvatarDiskCache.fileName(id: 'room1', isGroup: true, size: 64),
|
||||
);
|
||||
expect(
|
||||
AvatarDiskCache.fileName(id: 'room1', isGroup: true, size: 0),
|
||||
startsWith('g_'),
|
||||
);
|
||||
});
|
||||
|
||||
test('names are file-safe even for exotic ids', () {
|
||||
final name = AvatarDiskCache.fileName(
|
||||
id: 'a/b c@d',
|
||||
isGroup: false,
|
||||
size: 128,
|
||||
);
|
||||
expect(name, matches(RegExp(r'^u_[0-9a-f]+_128$')));
|
||||
});
|
||||
|
||||
test(
|
||||
'a short id evict prefix never matches a longer id file (hex + _ '
|
||||
'separator keeps the boundary unambiguous)',
|
||||
() {
|
||||
// Eviction deletes files starting with `u_<hex(id)>_`.
|
||||
final shortPrefix =
|
||||
'u_${AvatarDiskCache.fileName(id: 'a', isGroup: false, size: 1).split('_')[1]}_';
|
||||
final longFile = AvatarDiskCache.fileName(
|
||||
id: 'a_b',
|
||||
isGroup: false,
|
||||
size: 256,
|
||||
);
|
||||
expect(longFile.startsWith(shortPrefix), isFalse);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user