test: add responsive tests to all page test files

Add shared screen size constants and configureScreenSize() helper in
test/harness/screen_sizes.dart. Add responsive test groups to all page
tests covering desktop (1920x1080), tablet landscape (1024x768), tablet
portrait (768x1024), mobile (375x812), and small mobile (320x568).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-01-06 19:45:32 +01:00
co-authored by Claude Opus 4.5
parent 71eaccce67
commit ea6914b5f4
8 changed files with 514 additions and 4 deletions
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart' hide Container;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/control_room/containers/presentation/pages/containers_list_page.dart'; import 'package:tatlock_ui/features/control_room/containers/presentation/pages/containers_list_page.dart';
import '../../../../../harness/screen_sizes.dart';
import '../../../../../harness/test_harness.dart'; import '../../../../../harness/test_harness.dart';
void main() { void main() {
@@ -303,4 +304,62 @@ void main() {
expect(port.formatted, '6379'); expect(port.formatted, '6379');
}); });
}); });
group('ContainersListPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenContainers();
await tester.pumpWidget(harness.wrap(const ContainersListPage()));
await tester.pumpAndSettle();
expect(find.text('nginx-proxy'), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenContainers();
await tester.pumpWidget(harness.wrap(const ContainersListPage()));
await tester.pumpAndSettle();
expect(find.text('nginx-proxy'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenContainers();
await tester.pumpWidget(harness.wrap(const ContainersListPage()));
await tester.pumpAndSettle();
expect(find.byType(ContainersListPage), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenContainers();
await tester.pumpWidget(harness.wrap(const ContainersListPage()));
await tester.pumpAndSettle();
expect(find.byType(ContainersListPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenContainers();
await tester.pumpWidget(harness.wrap(const ContainersListPage()));
await tester.pumpAndSettle();
expect(find.byType(ContainersListPage), findsOneWidget);
});
});
} }
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/control_room/npm/presentation/pages/proxy_hosts_page.dart'; import 'package:tatlock_ui/features/control_room/npm/presentation/pages/proxy_hosts_page.dart';
import '../../../../../harness/screen_sizes.dart';
import '../../../../../harness/test_harness.dart'; import '../../../../../harness/test_harness.dart';
void main() { void main() {
@@ -214,4 +215,62 @@ void main() {
} }
}); });
}); });
group('ProxyHostsPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ProxyHostsPage()));
await tester.pumpAndSettle();
expect(find.text('home.schweitz.net'), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ProxyHostsPage()));
await tester.pumpAndSettle();
expect(find.text('home.schweitz.net'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ProxyHostsPage()));
await tester.pumpAndSettle();
expect(find.byType(ProxyHostsPage), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ProxyHostsPage()));
await tester.pumpAndSettle();
expect(find.byType(ProxyHostsPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ProxyHostsPage()));
await tester.pumpAndSettle();
expect(find.byType(ProxyHostsPage), findsOneWidget);
});
});
} }
@@ -1,10 +1,9 @@
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/control_room/presentation/pages/control_room_page.dart'; import 'package:tatlock_ui/features/control_room/presentation/pages/control_room_page.dart';
import 'package:tatlock_ui/features/control_room/router.dart'; import 'package:tatlock_ui/features/control_room/router.dart';
import '../../../../harness/screen_sizes.dart';
import '../../../../harness/test_harness.dart'; import '../../../../harness/test_harness.dart';
void main() { void main() {
@@ -257,4 +256,67 @@ void main() {
expect(navItem.section, 'Stack Management'); expect(navItem.section, 'Stack Management');
}); });
}); });
group('ControlRoomPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenContainers();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ControlRoomPage()));
await tester.pumpAndSettle();
expect(find.text('Sections'), findsOneWidget);
expect(find.text('Containers'), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenContainers();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ControlRoomPage()));
await tester.pumpAndSettle();
expect(find.text('Sections'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenContainers();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ControlRoomPage()));
await tester.pumpAndSettle();
expect(find.byType(ControlRoomPage), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenContainers();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ControlRoomPage()));
await tester.pumpAndSettle();
expect(find.byType(ControlRoomPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenContainers();
harness.givenDomains();
await tester.pumpWidget(harness.wrap(const ControlRoomPage()));
await tester.pumpAndSettle();
expect(find.byType(ControlRoomPage), findsOneWidget);
});
});
} }
@@ -1,9 +1,8 @@
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/front_hall/presentation/pages/front_hall_page.dart'; import 'package:tatlock_ui/features/front_hall/presentation/pages/front_hall_page.dart';
import '../../../../harness/screen_sizes.dart';
import '../../../../harness/test_harness.dart'; import '../../../../harness/test_harness.dart';
void main() { void main() {
@@ -229,4 +228,70 @@ void main() {
expect(find.text('INFRASTRUCTURE'), findsOneWidget); expect(find.text('INFRASTRUCTURE'), findsOneWidget);
}); });
}); });
group('FrontHallPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenQuickLinks();
harness.givenSystemStats();
await tester.pumpWidget(harness.wrap(const FrontHallPage()));
await tester.pumpAndSettle();
expect(find.text('Welcome to Tatlock'), findsOneWidget);
expect(find.text('Quick Links'), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenQuickLinks();
harness.givenSystemStats();
await tester.pumpWidget(harness.wrap(const FrontHallPage()));
await tester.pumpAndSettle();
expect(find.text('Welcome to Tatlock'), findsOneWidget);
expect(find.text('Quick Links'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenQuickLinks();
harness.givenSystemStats();
await tester.pumpWidget(harness.wrap(const FrontHallPage()));
await tester.pumpAndSettle();
expect(find.text('Welcome to Tatlock'), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenQuickLinks();
harness.givenSystemStats();
await tester.pumpWidget(harness.wrap(const FrontHallPage()));
await tester.pumpAndSettle();
// Core content should still be accessible
expect(find.byType(FrontHallPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenQuickLinks();
harness.givenSystemStats();
await tester.pumpWidget(harness.wrap(const FrontHallPage()));
await tester.pumpAndSettle();
// Should render without crashing
expect(find.byType(FrontHallPage), findsOneWidget);
});
});
} }
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/security/groups/groups_list_page.dart'; import 'package:tatlock_ui/features/security/groups/groups_list_page.dart';
import '../../../harness/screen_sizes.dart';
import '../../../harness/test_harness.dart'; import '../../../harness/test_harness.dart';
void main() { void main() {
@@ -229,4 +230,62 @@ void main() {
} }
}); });
}); });
group('GroupsListPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenGroups();
await tester.pumpWidget(harness.wrap(const GroupsListPage()));
await tester.pumpAndSettle();
expect(find.text('admins'), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenGroups();
await tester.pumpWidget(harness.wrap(const GroupsListPage()));
await tester.pumpAndSettle();
expect(find.text('admins'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenGroups();
await tester.pumpWidget(harness.wrap(const GroupsListPage()));
await tester.pumpAndSettle();
expect(find.byType(GroupsListPage), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenGroups();
await tester.pumpWidget(harness.wrap(const GroupsListPage()));
await tester.pumpAndSettle();
expect(find.byType(GroupsListPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenGroups();
await tester.pumpWidget(harness.wrap(const GroupsListPage()));
await tester.pumpAndSettle();
expect(find.byType(GroupsListPage), findsOneWidget);
});
});
} }
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/features/security/users/users_list_page.dart'; import 'package:tatlock_ui/features/security/users/users_list_page.dart';
import '../../../harness/screen_sizes.dart';
import '../../../harness/test_harness.dart'; import '../../../harness/test_harness.dart';
void main() { void main() {
@@ -253,4 +254,62 @@ void main() {
} }
}); });
}); });
group('UsersListPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
harness.givenUsers();
await tester.pumpWidget(harness.wrap(const UsersListPage()));
await tester.pumpAndSettle();
expect(find.text('Admin User'), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
harness.givenUsers();
await tester.pumpWidget(harness.wrap(const UsersListPage()));
await tester.pumpAndSettle();
expect(find.text('Admin User'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
harness.givenUsers();
await tester.pumpWidget(harness.wrap(const UsersListPage()));
await tester.pumpAndSettle();
expect(find.byType(UsersListPage), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
harness.givenUsers();
await tester.pumpWidget(harness.wrap(const UsersListPage()));
await tester.pumpAndSettle();
expect(find.byType(UsersListPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
harness.givenUsers();
await tester.pumpWidget(harness.wrap(const UsersListPage()));
await tester.pumpAndSettle();
expect(find.byType(UsersListPage), findsOneWidget);
});
});
} }
@@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:tatlock_ui/core/theme/theme_provider.dart'; import 'package:tatlock_ui/core/theme/theme_provider.dart';
import 'package:tatlock_ui/features/settings/presentation/pages/settings_page.dart'; import 'package:tatlock_ui/features/settings/presentation/pages/settings_page.dart';
import '../../../../harness/screen_sizes.dart';
import '../../../../harness/test_harness.dart'; import '../../../../harness/test_harness.dart';
void main() { void main() {
@@ -169,4 +170,58 @@ void main() {
expect(find.byType(Card), findsNWidgets(3)); expect(find.byType(Card), findsNWidgets(3));
}); });
}); });
group('SettingsPage Responsive', () {
testWidgets('renders at desktop size (1920x1080)', (tester) async {
configureScreenSize(tester, ScreenSizes.desktopLarge);
harness.givenAuthenticatedUser();
await tester.pumpWidget(harness.wrap(const SettingsPage()));
await tester.pumpAndSettle();
expect(find.text('Settings'), findsOneWidget);
expect(find.byType(Card), findsNWidgets(3));
});
testWidgets('renders at tablet landscape (1024x768)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletLandscape);
harness.givenAuthenticatedUser();
await tester.pumpWidget(harness.wrap(const SettingsPage()));
await tester.pumpAndSettle();
expect(find.text('Settings'), findsOneWidget);
expect(find.text('APPEARANCE'), findsOneWidget);
});
testWidgets('renders at tablet portrait (768x1024)', (tester) async {
configureScreenSize(tester, ScreenSizes.tabletPortrait);
harness.givenAuthenticatedUser();
await tester.pumpWidget(harness.wrap(const SettingsPage()));
await tester.pumpAndSettle();
expect(find.text('Settings'), findsOneWidget);
});
testWidgets('renders at mobile size (375x812)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobile);
harness.givenAuthenticatedUser();
await tester.pumpWidget(harness.wrap(const SettingsPage()));
await tester.pumpAndSettle();
expect(find.byType(SettingsPage), findsOneWidget);
});
testWidgets('renders at small mobile size (320x568)', (tester) async {
configureScreenSize(tester, ScreenSizes.mobileSmall);
harness.givenAuthenticatedUser();
await tester.pumpWidget(harness.wrap(const SettingsPage()));
await tester.pumpAndSettle();
expect(find.byType(SettingsPage), findsOneWidget);
});
});
} }
+92
View File
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
/// Standard screen sizes for responsive testing.
abstract class ScreenSizes {
// Desktop sizes
static const desktopLarge = Size(1920, 1080);
static const desktop = Size(1440, 900);
static const desktopSmall = Size(1280, 800);
// Tablet sizes
static const tabletLandscape = Size(1024, 768);
static const tabletPortrait = Size(768, 1024);
// Mobile sizes
static const mobileLarge = Size(428, 926); // iPhone 14 Pro Max
static const mobile = Size(375, 812); // iPhone X/11/12/13
static const mobileSmall = Size(320, 568); // iPhone SE
/// All sizes for comprehensive testing.
static const all = [
desktopLarge,
desktop,
tabletLandscape,
tabletPortrait,
mobileLarge,
mobile,
];
/// Desktop-only sizes.
static const desktopSizes = [desktopLarge, desktop, desktopSmall];
/// Tablet sizes.
static const tabletSizes = [tabletLandscape, tabletPortrait];
/// Mobile sizes.
static const mobileSizes = [mobileLarge, mobile, mobileSmall];
}
/// Extension to easily set screen size in widget tests.
extension ScreenSizeExtension on WidgetTester {
/// Sets the screen size for the test.
///
/// Remember to call `addTearDown(() => view.resetPhysicalSize())` after.
void setScreenSize(Size size) {
view.physicalSize = size;
view.devicePixelRatio = 1.0;
}
/// Resets the screen size to default.
void resetScreenSize() {
view.resetPhysicalSize();
}
}
/// Configures a widget test for a specific screen size with overflow suppression.
///
/// Usage:
/// ```dart
/// testWidgets('renders on mobile', (tester) async {
/// configureScreenSize(tester, ScreenSizes.mobile);
/// // ... test code
/// });
/// ```
void configureScreenSize(WidgetTester tester, Size size) {
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
addTearDown(() => tester.view.resetPhysicalSize());
// Suppress overflow errors for responsive tests
final originalOnError = FlutterError.onError;
FlutterError.onError = (details) {
if (details.exceptionAsString().contains('overflowed')) {
return; // Ignore overflow errors
}
originalOnError?.call(details);
};
addTearDown(() => FlutterError.onError = originalOnError);
}
/// Returns a descriptive name for a screen size.
String screenSizeName(Size size) {
if (size == ScreenSizes.desktopLarge) return 'desktop-large';
if (size == ScreenSizes.desktop) return 'desktop';
if (size == ScreenSizes.desktopSmall) return 'desktop-small';
if (size == ScreenSizes.tabletLandscape) return 'tablet-landscape';
if (size == ScreenSizes.tabletPortrait) return 'tablet-portrait';
if (size == ScreenSizes.mobileLarge) return 'mobile-large';
if (size == ScreenSizes.mobile) return 'mobile';
if (size == ScreenSizes.mobileSmall) return 'mobile-small';
return '${size.width.toInt()}x${size.height.toInt()}';
}