add some new features
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:ertl_filament_verwaltung/controller/abfall_controller.dart';
|
||||
import 'package:ertl_filament_verwaltung/helper/storage_keys.dart';
|
||||
import 'package:ertl_filament_verwaltung/main.dart';
|
||||
import 'package:ertl_filament_verwaltung/models/abfall_model.dart';
|
||||
import 'package:ertl_filament_verwaltung/models/backup_model.dart';
|
||||
import 'package:ertl_filament_verwaltung/pages/abfall_view.dart';
|
||||
import 'package:ertl_filament_verwaltung/pages/home_view.dart';
|
||||
import 'package:ertl_filament_verwaltung/services/get_storage_services.dart';
|
||||
import 'package:ertl_filament_verwaltung/theme/app_theme.dart';
|
||||
|
||||
import 'helpers/memory_storage_service.dart';
|
||||
|
||||
void main() {
|
||||
late MemoryStorageService storage;
|
||||
setUp(() {
|
||||
Get.testMode = true;
|
||||
storage = MemoryStorageService();
|
||||
Get.put<GetStorageService>(storage);
|
||||
});
|
||||
tearDown(() => Get.reset());
|
||||
|
||||
Future<AbfallController> show(WidgetTester tester) async {
|
||||
tester.view.physicalSize = const Size(320, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
final controller = Get.put(AbfallController());
|
||||
await tester.pumpWidget(
|
||||
GetMaterialApp(theme: AppTheme.light, home: const AbfallView()),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
return controller;
|
||||
}
|
||||
|
||||
testWidgets('Home opens waste management and back returns home at 320px', (
|
||||
tester,
|
||||
) async {
|
||||
tester.view.physicalSize = const Size(320, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
await tester.pumpWidget(const MyApp());
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byTooltip('Bauteile verwalten'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Abfall verwalten'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(Get.currentRoute, AbfallView.routeName);
|
||||
expect(find.text('Abfallverwaltung'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Zurück'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(Get.currentRoute, HomeView.routeName);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Save, edit, reload and delete keep totals and timestamps consistent',
|
||||
(tester) async {
|
||||
final controller = await show(tester);
|
||||
final date = DateTime(2026, 10, 28, 14, 35);
|
||||
controller.setDatum(date);
|
||||
controller.setUhrzeit(const TimeOfDay(hour: 14, minute: 35));
|
||||
controller.gewichtController.text = '12,5';
|
||||
controller.kostenController.text = '1,25';
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
final first = controller.abfaelle.single;
|
||||
expect(first.datumZeit, date);
|
||||
expect(controller.summeAbfall, 12.5);
|
||||
expect(controller.summeKosten, 1.25);
|
||||
|
||||
controller.datumZeit.value = date.add(const Duration(days: 1));
|
||||
controller.gewichtController.text = '7,5';
|
||||
controller.kostenController.text = '0,75';
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.abfaelle.first.datumZeit.isAfter(date), isTrue);
|
||||
expect(controller.summeAbfall, 20);
|
||||
expect(controller.summeKosten, 2);
|
||||
controller.scrollController.jumpTo(
|
||||
controller.scrollController.position.maxScrollExtent,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Summe Abfall'), findsOneWidget);
|
||||
expect(find.text('20 g'), findsOneWidget);
|
||||
|
||||
final editing = controller.editAbfall(first);
|
||||
await tester.pumpAndSettle();
|
||||
await editing;
|
||||
controller.gewichtController.text = '15';
|
||||
controller.kostenController.text = '1,50';
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
controller.loadAbfaelle();
|
||||
expect(controller.abfaelle.length, 2);
|
||||
expect(controller.summeAbfall, 22.5);
|
||||
expect(controller.summeKosten, 2.25);
|
||||
expect(controller.abfaelle.last.abfallId, first.abfallId);
|
||||
await controller.deleteAbfall(first.abfallId);
|
||||
await tester.pumpAndSettle();
|
||||
final reloaded = AbfallController()..onInit();
|
||||
expect(reloaded.abfaelle.single.gewichtGramm, 7.5);
|
||||
expect(reloaded.summeKosten, .75);
|
||||
reloaded.onClose();
|
||||
expect(tester.takeException(), isNull);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('Kilopreis calculates from grams while costs remain editable', (
|
||||
tester,
|
||||
) async {
|
||||
final controller = await show(tester);
|
||||
Future<void> enter(TextEditingController field, String value) async {
|
||||
final finder = find.byWidgetPredicate(
|
||||
(widget) => widget is TextFormField && widget.controller == field,
|
||||
);
|
||||
await tester.ensureVisible(finder);
|
||||
await tester.enterText(finder, value);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
await enter(controller.kilopreisController, '20');
|
||||
await enter(controller.gewichtController, '125');
|
||||
expect(controller.kostenController.text, '2,50');
|
||||
await enter(controller.kostenController, '3,10');
|
||||
controller.setUhrzeit(const TimeOfDay(hour: 10, minute: 30));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.kostenController.text, '3,10');
|
||||
await enter(controller.gewichtController, '200');
|
||||
expect(controller.kostenController.text, '4,00');
|
||||
await enter(controller.kilopreisController, '30,5');
|
||||
expect(controller.kostenController.text, '6,10');
|
||||
await enter(controller.kostenController, '5,55');
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.abfaelle.single.kosten, 5.55);
|
||||
expect(controller.kilopreisController.text, '30,5');
|
||||
final editing = controller.editAbfall(controller.abfaelle.single);
|
||||
await tester.pumpAndSettle();
|
||||
await editing;
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.kostenController.text, '5,55');
|
||||
await enter(controller.kilopreisController, '0');
|
||||
expect(controller.kostenController.text, '0,00');
|
||||
await enter(controller.kilopreisController, '');
|
||||
await enter(controller.gewichtController, '250');
|
||||
await enter(controller.kostenController, '2,75');
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.abfaelle.single.kosten, 2.75);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
testWidgets('Invalid inputs and failed writes preserve entries and form', (
|
||||
tester,
|
||||
) async {
|
||||
final controller = await show(tester);
|
||||
for (final weight in ['', '-1', '0', 'NaN', 'Infinity']) {
|
||||
controller.gewichtController.text = weight;
|
||||
controller.kostenController.text = '1';
|
||||
await controller.saveAbfall();
|
||||
expect(controller.abfaelle, isEmpty);
|
||||
}
|
||||
controller.gewichtController.text = '10';
|
||||
for (final cost in ['', '-1', 'NaN', 'Infinity']) {
|
||||
controller.kostenController.text = cost;
|
||||
await controller.saveAbfall();
|
||||
expect(controller.abfaelle, isEmpty);
|
||||
}
|
||||
controller.kostenController.text = '0';
|
||||
storage.failWrites = true;
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.hasError.value, isTrue);
|
||||
expect(controller.gewichtController.text, '10');
|
||||
expect(controller.abfaelle, isEmpty);
|
||||
storage.failWrites = false;
|
||||
await controller.saveAbfall();
|
||||
await tester.pumpAndSettle();
|
||||
final saved = controller.abfaelle.single;
|
||||
storage.failWrites = true;
|
||||
await controller.deleteAbfall(saved.abfallId);
|
||||
expect(controller.abfaelle.single.abfallId, saved.abfallId);
|
||||
expect(controller.summeAbfall, 10);
|
||||
expect(storage.getModelList(StorageKeys.abfaelle), hasLength(1));
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'Backups round-trip waste, accept older backups and reject invalid waste',
|
||||
() {
|
||||
final entry = AbfallModel(
|
||||
abfallId: 'w1',
|
||||
datumZeit: DateTime(2026, 10, 29, 9, 5),
|
||||
gewichtGramm: 25,
|
||||
kosten: 1.5,
|
||||
);
|
||||
final data = {
|
||||
for (final key in BackupModel.keys) key: <Map<String, dynamic>>[],
|
||||
};
|
||||
data[StorageKeys.abfaelle] = [entry.toJson()];
|
||||
final restored = BackupModel.decode(BackupModel(data).encode());
|
||||
expect(restored.data[StorageKeys.abfaelle], [entry.toJson()]);
|
||||
final legacy = {...data}..remove(StorageKeys.abfaelle);
|
||||
expect(
|
||||
BackupModel.decode(BackupModel(legacy).encode())
|
||||
.data[StorageKeys.abfaelle],
|
||||
isEmpty,
|
||||
);
|
||||
data[StorageKeys.abfaelle] = [
|
||||
{...entry.toJson(), 'kosten': -1},
|
||||
];
|
||||
expect(
|
||||
() => BackupModel.decode(BackupModel(data).encode()),
|
||||
throwsFormatException,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user