// Version: 1.0.0 | Created: 2026-07-04 // Verifies the chat timeline requests older history when scrolled near the // top (oldest end) of the reverse list. Driven through the real widget tree // with a fake repository, since Playwright cannot scroll the CanvasKit surface. import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:matrix/matrix.dart'; import 'package:m8chat_app/core/network/matrix_client.dart'; import 'package:m8chat_app/features/chat/data/chat_repository.dart'; import 'package:m8chat_app/features/chat/domain/message_model.dart'; import 'package:m8chat_app/features/chat/presentation/chat_controller.dart'; import 'package:m8chat_app/features/chat/presentation/chat_screen.dart'; class _FakeChatRepository extends ChatRepository { _FakeChatRepository() : super(client: Client('test-client')); int loadMoreCalls = 0; bool _hasMore = true; @override bool canLoadMore(String roomId) => _hasMore; @override Future loadMoreMessages(String roomId) async { loadMoreCalls++; _hasMore = false; // one page then exhausted } } void main() { testWidgets('scrolling to the top requests older history once', ( tester, ) async { const roomId = '!paginate:server'; final base = DateTime(2026, 7, 4, 8); final messages = [ for (var i = 40; i >= 1; i--) MessageModel( eventId: '\$e$i', roomId: roomId, senderId: '@me:server', senderDisplayName: 'Me', timestamp: base.add(Duration(seconds: i)), type: MessageType.text, body: 'Message $i', isMine: true, ), ]; final repo = _FakeChatRepository(); await tester.pumpWidget( ProviderScope( overrides: [ matrixClientProvider.overrideWithValue(Client('test-client')), chatRepositoryProvider.overrideWithValue(repo), chatTimelineProvider( roomId, ).overrideWith((ref) => Stream.value(messages)), ], child: const MaterialApp(home: ChatScreen(roomId: roomId)), ), ); await tester.pumpAndSettle(); expect(repo.loadMoreCalls, 0); // Confirm the override is actually in effect. final container = ProviderScope.containerOf( tester.element(find.byType(ChatScreen)), ); expect(identical(container.read(chatRepositoryProvider), repo), isTrue); double offset() => tester .state(find.byType(Scrollable).first) .position .pixels; double maxExtent() => tester .state(find.byType(Scrollable).first) .position .maxScrollExtent; // Scroll up toward the oldest end (reverse list: drag content down). await tester.drag(find.byType(ListView), const Offset(0, 3000)); await tester.pumpAndSettle(); expect( offset(), greaterThan(maxExtent() - 400), reason: 'drag should have reached the oldest end (offset=${offset()}, ' 'max=${maxExtent()})', ); expect( repo.loadMoreCalls, greaterThan(0), reason: 'reaching the top should request older history', ); }); }