Skip to content

Commit 3312f67

Browse files
test: cover scroll-triggered pagination, controller reattach,
and asserts - Adds a test that overflows the viewport and scrolls near the bottom to verify the real `_onScroll` listener triggers automatic pagination. - Adds coverage for swapping `ScrollInfinity.controller` at runtime, ensuring proper detach/attach behavior. - Adds constructor assert coverage beyond `loadMoreThreshold`, including `initialPageIndex`, `interval`, `interval` + non-nullable `T`, and `maxRetries`.
1 parent 983c710 commit 3312f67

1 file changed

Lines changed: 129 additions & 0 deletions

File tree

test/src/widget/scroll_infinity_test.dart

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,47 @@ void main() {
225225
expect(find.text('Load More'), findsNothing);
226226
},
227227
);
228+
229+
testWidgets(
230+
'Automatically fetches the next page when scrolling within '
231+
'loadMoreThreshold of the end',
232+
(tester) async {
233+
var callCount = 0;
234+
235+
await tester.pumpWidget(
236+
buildTestableWidget(
237+
ScrollInfinity<String>(
238+
maxItems: 5,
239+
loadData: (page) {
240+
callCount++;
241+
return mockLoadData(
242+
page,
243+
totalItems: 15,
244+
maxItemsPerPage: 5,
245+
);
246+
},
247+
itemBuilder: (item, index) {
248+
return SizedBox(height: 300, child: Text(item));
249+
},
250+
),
251+
),
252+
);
253+
254+
// First page fills (and overflows) the viewport, so the
255+
// fill-the-screen loop stops after one fetch.
256+
await tester.pumpAndSettle();
257+
expect(callCount, 1);
258+
expect(find.text('Item 5'), findsNothing);
259+
260+
// Dragging near the bottom must trigger `_onScroll` and fetch
261+
// the next page automatically (no manual "Load More" tap).
262+
await tester.drag(find.byType(ListView), const Offset(0, -1400));
263+
await tester.pumpAndSettle();
264+
265+
expect(callCount, 2);
266+
expect(find.text('Item 5'), findsOneWidget);
267+
},
268+
);
228269
});
229270

230271
/// Tests related to error states and retry logic.
@@ -1027,6 +1068,46 @@ void main() {
10271068
expect(controller.hasError, isFalse);
10281069
},
10291070
);
1071+
1072+
testWidgets(
1073+
'Detaches the old controller and attaches the new one when the '
1074+
'controller property changes',
1075+
(tester) async {
1076+
final controllerA = ScrollInfinityController();
1077+
final controllerB = ScrollInfinityController();
1078+
1079+
Widget buildWith(ScrollInfinityController controller) {
1080+
return buildTestableWidget(
1081+
ScrollInfinity<String>(
1082+
maxItems: 5,
1083+
controller: controller,
1084+
loadData: (page) {
1085+
return mockLoadData(page, totalItems: 5, maxItemsPerPage: 5);
1086+
},
1087+
itemBuilder: (item, index) => Text(item),
1088+
),
1089+
);
1090+
}
1091+
1092+
await tester.pumpWidget(buildWith(controllerA));
1093+
await tester.pumpAndSettle();
1094+
expect(controllerA.isLoading, isFalse);
1095+
1096+
await tester.pumpWidget(buildWith(controllerB));
1097+
await tester.pumpAndSettle();
1098+
1099+
// The old controller is detached: it must report inert defaults
1100+
// instead of forwarding to the (now different) widget state.
1101+
expect(controllerA.isLoading, isFalse);
1102+
expect(controllerA.hasError, isFalse);
1103+
1104+
// The new controller is attached and drives the live state.
1105+
controllerB.refresh();
1106+
await tester.pump();
1107+
expect(controllerB.isLoading, isTrue);
1108+
await tester.pumpAndSettle();
1109+
},
1110+
);
10301111
});
10311112

10321113
/// Tests for the `enablePullToRefresh` property.
@@ -1162,6 +1243,54 @@ void main() {
11621243
throwsAssertionError,
11631244
);
11641245
});
1246+
1247+
test('initialPageIndex cannot be negative', () {
1248+
expect(
1249+
() => ScrollInfinity<String>(
1250+
maxItems: 5,
1251+
initialPageIndex: -1,
1252+
loadData: (page) async => [],
1253+
itemBuilder: (item, index) => Text(item),
1254+
),
1255+
throwsAssertionError,
1256+
);
1257+
});
1258+
1259+
test('interval must be greater than zero', () {
1260+
expect(
1261+
() => ScrollInfinity<String?>(
1262+
maxItems: 5,
1263+
interval: 0,
1264+
loadData: (page) async => [],
1265+
itemBuilder: (item, index) => Text(item ?? ''),
1266+
),
1267+
throwsAssertionError,
1268+
);
1269+
});
1270+
1271+
test('interval requires a nullable generic type T', () {
1272+
expect(
1273+
() => ScrollInfinity<String>(
1274+
maxItems: 5,
1275+
interval: 2,
1276+
loadData: (page) async => [],
1277+
itemBuilder: (item, index) => Text(item),
1278+
),
1279+
throwsAssertionError,
1280+
);
1281+
});
1282+
1283+
test('maxRetries cannot be negative', () {
1284+
expect(
1285+
() => ScrollInfinity<String>(
1286+
maxItems: 5,
1287+
maxRetries: -1,
1288+
loadData: (page) async => [],
1289+
itemBuilder: (item, index) => Text(item),
1290+
),
1291+
throwsAssertionError,
1292+
);
1293+
});
11651294
});
11661295
});
11671296
}

0 commit comments

Comments
 (0)