// The charging page's home half. // // Nothing here can talk to a server, so what is worth guarding is what the page // says without one: that both tabs render, that the home tab degrades to the // "import one" invitation rather than a blank column, and that the arrange sheet // offers exactly the tabs and cards the profile can store — those key sets are // shared with the web app and the server, so a card added here and forgotten // there would arrange into nothing. import "package:flutter/material.dart"; import "package:flutter_test/flutter_test.dart"; import "package:intl/date_symbol_data_local.dart"; import "package:shared_preferences/shared_preferences.dart"; import "package:drivervault_phone/i18n.dart"; import "package:drivervault_phone/main.dart"; import "package:drivervault_phone/screens/charging_screen.dart"; void main() { TestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() async { await initializeDateFormatting(); await loadTranslations(); appSettings.locale = "en-GB"; appSettings.dateFormat = "DMY_NUM"; }); setUp(() { // The page reads its folded cards and its last serial from here. SharedPreferences.setMockInitialValues({}); }); // A surface tall enough that the page's lazy list builds the home cards as // well as the header above them. Future pump(WidgetTester tester) async { tester.view.physicalSize = const Size(800, 3000); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); await tester.pumpWidget(const MaterialApp(home: ChargingScreen())); await tester.pumpAndSettle(); } testWidgets("both tabs render, and the home half invites an import", (tester) async { await pump(tester); expect(find.text(t("charging.tabs.public")), findsWidgets); expect(find.text(t("charging.tabs.home")), findsWidgets); // The page opens on the public half; the tab that is not shown is not built, // so the home cards are only findable once it is. expect(find.textContaining(t("charging.stations.heading")), findsWidgets); await tester.tap(find.text(t("charging.tabs.home"))); await tester.pumpAndSettle(); // With no chargers and no control mode, the home tab is the information card // saying there is nothing yet plus the list saying what it is for. expect(find.text(t("charging.info.title")), findsOneWidget); expect(find.text(t("charging.info.empty")), findsOneWidget); expect(find.text(t("charging.home.empty")), findsOneWidget); // No charger service answered, so importing is not on offer — the hint is. expect(find.text(t("charging.home.connectFirst")), findsOneWidget); expect(find.text(t("charging.home.import")), findsNothing); // Control is off, so the cards that act on a charger are absent and the // information card says why. expect(find.text(t("charging.control.title")), findsNothing); expect(find.text(t("charging.control.connectionTitle")), findsNothing); expect(find.text(t("charging.modbus.title")), findsNothing); expect(find.text(t("charging.stations.noControlHint")), findsOneWidget); }); testWidgets("the arrange sheet offers every tab and every card", (tester) async { await pump(tester); await tester.tap(find.byIcon(Icons.swap_vert)); await tester.pumpAndSettle(); expect(find.text(t("charging.arrange.title")), findsOneWidget); for (final key in kChargerTabKeys) { expect(find.text(t("charging.tabs.$key")), findsWidgets, reason: "the $key tab is not on offer"); } // Named by their own headings, so the sheet reads like the page. const cardLabels = { "control": "charging.control.title", "connection": "charging.control.connectionTitle", "readings": "charging.modbus.title", "info": "charging.info.title", }; expect(cardLabels.keys.toSet(), kChargerCardKeys.toSet()); for (final key in kChargerCardKeys) { expect(find.text(t(cardLabels[key]!)), findsWidgets, reason: "the $key card is not on offer"); } // One drag handle per row: both lists rearrange, not just the first. expect(find.byIcon(Icons.drag_handle), findsNWidgets(kChargerTabKeys.length + kChargerCardKeys.length)); }); }