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
+160
View File
@@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:ertl_filament_verwaltung/controller/bauteil_controller.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/kauf_filament_model.dart';
import 'package:ertl_filament_verwaltung/services/get_storage_services.dart';
import 'package:ertl_filament_verwaltung/theme/app_theme.dart';
import 'package:ertl_filament_verwaltung/widgets/bauteil_filament_auswahl.dart';
import 'helpers/memory_storage_service.dart';
void main() {
late MemoryStorageService storage;
late BauteilController controller;
setUp(() {
Get.testMode = true;
storage = MemoryStorageService();
storage.tables[StorageKeys.filamente] = [
for (final entry in {'a': '100', 'b': '150'}.entries)
KaufFilamentModel(
filamentId: entry.key,
kaufdatum: DateTime(2026),
haendler: 'Shop',
marke: 'Rolle ${entry.key}',
material: 'PLA',
preis: 10,
farbe: 'Blau',
gewicht: entry.value,
).toJson(),
];
storage.tables[StorageKeys.bauteile] = [
BauteilModel(
bauteilId: 'vorher',
erstelltAm: DateTime(2026),
name: 'Vorher',
verwendung: 'Test',
druckdauerMinuten: 1,
gewichtGramm: 40,
abfallGramm: 0,
filamente: [
const BauteilFilamentModel(filamentId: 'a', verbrauchGramm: 40),
],
).toJson(),
];
Get.put<GetStorageService>(storage);
controller = Get.put(BauteilController());
});
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: Scaffold(
body: SingleChildScrollView(
child: BauteilFilamentAuswahl(controller: controller),
),
),
),
);
await tester.pumpAndSettle();
}
Future<void> select(WidgetTester tester, String id) async {
await tester.ensureVisible(find.byType(DropdownButtonFormField<String>));
await tester.tap(find.byType(DropdownButtonFormField<String>));
await tester.pumpAndSettle();
await tester.tap(find.text(controller.filamentLabel(id)).last);
await tester.pumpAndSettle();
}
testWidgets(
'Insufficient roll warns with quantities and requires OK; second roll covers combined demand',
(tester) async {
controller.gewichtController.text = '150';
controller.abfallController.text = '10';
await show(tester);
await select(tester, 'a');
expect(find.byType(AlertDialog), findsOneWidget);
expect(find.text('Achtung: zu wenig Filament'), findsOneWidget);
expect(
find.textContaining('Benötigt (inklusive Abfall): 160 g'),
findsOneWidget,
);
expect(find.textContaining('Verfügbar: 60 g'), findsOneWidget);
expect(find.textContaining('Es fehlen: 100 g'), findsOneWidget);
await tester.tapAt(const Offset(2, 2));
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsOneWidget);
await tester.binding.handlePopRoute();
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsNothing);
expect(controller.selectedFilamente.keys, ['a']);
await select(tester, 'b'); // 60g + 150g cover 160g; b alone would not.
expect(find.byType(AlertDialog), findsNothing);
expect(controller.selectedFilamente.keys, ['a', 'b']);
expect(storage.getModelList(StorageKeys.bauteile), hasLength(1));
expect(tester.takeException(), isNull);
},
);
testWidgets(
'Exact remaining amount produces no warning and selecting does not book consumption',
(tester) async {
controller.gewichtController.text = '50';
controller.abfallController.text = '10';
await show(tester);
await select(tester, 'a');
expect(find.byType(AlertDialog), findsNothing);
expect(controller.restgewichtGramm('a'), 60);
expect(storage.getModelList(StorageKeys.bauteile), hasLength(1));
expect(tester.takeException(), isNull);
},
);
test('Combined stock, decimal commas and editing credit use the correct available amount', () {
controller.gewichtController.text = '200,5';
controller.abfallController.text = '9,5';
controller.toggleFilament('a', true);
controller.toggleFilament('b', true);
expect(controller.pruefeFilamentVorrat(), isNull); // Exactly 210g.
controller.abfallController.text = '10';
expect(
controller.pruefeFilamentVorrat(),
contains('ausgewählten Rollen zusammen'),
);
expect(controller.pruefeFilamentVorrat(), contains('Es fehlen: 0,5 g'));
controller.toggleFilament('b', false);
controller.gewichtController.text = '90';
controller.abfallController.text = '0';
expect(controller.pruefeFilamentVorrat(), isNotNull);
controller.editingId.value =
'vorher'; // Replace existing 40g booking, not a new print.
expect(controller.pruefeFilamentVorrat(), isNull);
});
testWidgets(
'Without a valid requirement selection shows no shortage warning',
(tester) async {
await show(tester);
await select(tester, 'a');
expect(find.byType(AlertDialog), findsNothing);
for (final value in ['', 'NaN', 'Infinity', '-1', '0', 'abc']) {
controller.gewichtController.text = value;
expect(controller.pruefeFilamentVorrat(), isNull);
}
expect(tester.takeException(), isNull);
},
);
}