add some new features

This commit is contained in:
2026-10-29 15:03:48 +01:00
parent c876405a8f
commit 20012db615
39 changed files with 2908 additions and 97 deletions
+206
View File
@@ -0,0 +1,206 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:ertl_filament_verwaltung/controller/home_controller.dart';
import 'package:ertl_filament_verwaltung/helper/bauteil_preis_berechnung.dart';
import 'package:ertl_filament_verwaltung/helper/storage_keys.dart';
import 'package:ertl_filament_verwaltung/models/bauteil_model.dart';
import 'package:ertl_filament_verwaltung/models/backup_model.dart';
import 'package:ertl_filament_verwaltung/models/kauf_filament_model.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;
late HomeController controller;
setUp(() {
Get.testMode = true;
storage = MemoryStorageService();
Get.put<GetStorageService>(storage);
controller = Get.put(HomeController());
});
tearDown(() => Get.reset());
Future<void> show(WidgetTester tester) async {
tester.view.physicalSize = const Size(320, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(
GetMaterialApp(theme: AppTheme.light, home: const HomeView()),
);
await tester.pumpAndSettle();
}
void fill() {
controller.haendlerController.text = 'Shop';
controller.markeController.text = 'Marke';
controller.farbeController.text = 'Blau';
controller.preisController.text = '20';
controller.gewichtController.text = '1000';
}
Future<void> edit(WidgetTester tester, KaufFilamentModel filament) async {
final pending = controller.editFilament(filament);
await tester.pumpAndSettle();
await pending;
}
testWidgets(
'Gross weight saves, reloads, edits, duplicates and can be cleared independently',
(tester) async {
await show(tester);
fill();
final field = find.byWidgetPredicate(
(widget) =>
widget is TextFormField &&
widget.controller == controller.bruttoGewichtController,
);
await tester.ensureVisible(field);
await tester.enterText(field, '1250,5');
await controller.saveFilament();
await tester.pumpAndSettle();
var saved = controller.filamente.single;
expect(saved.gewichtGramm, 1000);
expect(saved.bruttoGewichtGramm, 1250.5);
expect(controller.bruttoGewichtController.text, isEmpty);
controller.loadFilamente();
expect(controller.filamente.single.bruttoGewichtGramm, 1250.5);
await edit(tester, saved);
expect(controller.bruttoGewichtController.text, '1250,5');
controller.bruttoGewichtController.text = '1300';
await controller.saveFilament();
await tester.pumpAndSettle();
saved = controller.filamente.single;
await controller.duplicateFilament(saved);
expect(controller.filamente, hasLength(2));
expect(
controller.filamente.map((item) => item.filamentId).toSet(),
hasLength(2),
);
expect(
controller.filamente.every((item) => item.bruttoGewichtGramm == 1300),
isTrue,
);
await edit(tester, saved);
controller.bruttoGewichtController.clear();
await controller.saveFilament();
await tester.pumpAndSettle();
final changed = controller.filamente.firstWhere(
(item) => item.filamentId == saved.filamentId,
);
expect(changed.bruttoGewichtGramm, isNull);
expect(changed.gewichtGramm, 1000);
expect(
controller.filamente
.firstWhere((item) => item.filamentId != saved.filamentId)
.bruttoGewichtGramm,
1300,
);
expect(tester.takeException(), isNull);
},
);
testWidgets(
'Gross weight must be finite, positive and at least net weight; leaving it empty is allowed',
(tester) async {
await show(tester);
fill();
for (final invalid in ['999', '0', '-1', 'NaN', 'Infinity', 'abc']) {
controller.bruttoGewichtController.text = invalid;
await controller.saveFilament();
await tester.pumpAndSettle();
expect(controller.filamente, isEmpty);
expect(storage.getModelList(StorageKeys.filamente), isEmpty);
}
controller.bruttoGewichtController.clear();
await controller.saveFilament();
await tester.pumpAndSettle();
expect(controller.filamente.single.bruttoGewichtGramm, isNull);
expect(tester.takeException(), isNull);
},
);
test('Gross weight is backed up while old backups without gross weight still load', () {
final rolle = KaufFilamentModel(
filamentId: 'a',
kaufdatum: DateTime(2026),
haendler: 'Shop',
marke: 'Marke',
material: 'PLA',
preis: 20,
farbe: 'Blau',
gewicht: '1kg',
bruttoGewichtGramm: 1250,
);
final data = {
for (final key in BackupModel.keys) key: <Map<String, dynamic>>[],
};
data[StorageKeys.filamente] = [rolle.toJson()];
final restored = BackupModel.decode(BackupModel(data).encode());
final model = KaufFilamentModel.fromJson(
restored.data[StorageKeys.filamente]!.single,
);
expect(model.bruttoGewichtGramm, 1250);
expect(model.gewichtGramm, 1000);
data[StorageKeys.filamente]!.single.remove('bruttoGewichtGramm');
expect(
KaufFilamentModel.fromJson(
BackupModel.decode(BackupModel(data).encode())
.data[StorageKeys.filamente]!
.single,
).bruttoGewichtGramm,
isNull,
);
for (final invalid in [999, 0, -1, 'invalid']) {
data[StorageKeys.filamente]!.single['bruttoGewichtGramm'] = invalid;
expect(
() => BackupModel.decode(BackupModel(data).encode()),
throwsFormatException,
);
}
});
test(
'Stock and all price calculations use only original net material weight',
() {
final rolle = KaufFilamentModel(
filamentId: 'a',
kaufdatum: DateTime(2026),
haendler: 'Shop',
marke: 'Marke',
material: 'PLA',
preis: 20,
farbe: 'Blau',
gewicht: '1000',
bruttoGewichtGramm: 1500,
);
controller.verbrauch['a'] = 200;
expect(controller.restgewicht(rolle), 800);
expect(
controller.restgewicht(rolle.copyWith(bruttoGewichtGramm: 2000)),
800,
);
final part = BauteilModel(
bauteilId: 'part',
erstelltAm: DateTime(2026),
name: 'Teil',
verwendung: 'Test',
druckdauerMinuten: 1,
gewichtGramm: 100,
abfallGramm: 10,
filamente: [
const BauteilFilamentModel(filamentId: 'a', verbrauchGramm: 110),
],
);
final price = BauteilPreisBerechnung.berechne(part, [rolle], 50)!;
expect(price.materialKosten, 2);
expect(price.abfallKosten, .2);
expect(price.gesamtpreis, 3.3);
},
);
}