Commit 977bcdd

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-01-20 14:58:45
test value distribution
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent b847b3a
Changed files (2)
lib
components
test
ui
components
lib/components/statistics/value_distribution.dart
@@ -8,6 +8,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 ///
 /// Shows in form of a graph that has centered columns of different height and
 /// labels that indicate min, max and average.
+///
+/// The widgets takes a width of at least 180 units and a height of at least 50.
 class ValueDistribution extends StatelessWidget {
   /// Create a statistic to show how often values occur.
   const ValueDistribution({
@@ -34,6 +36,13 @@ class ValueDistribution extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
+    final localizations = AppLocalizations.of(context)!;
+    if (values.isEmpty) {
+      return Center(
+        child: Text(localizations.errNoData),
+      );
+    }
+
     final distribution = <int, double>{};
     for (final v in values) {
       if(distribution.containsKey(v)) {
@@ -48,11 +57,17 @@ class ValueDistribution extends StatelessWidget {
     }
     assert(distribution[distribution.keys.max]! > 0);
     assert(distribution[distribution.keys.min]! > 0);
-    return CustomPaint(
-      painter: _ValueDistributionPainter(
-        distribution,
-        AppLocalizations.of(context)!,
-        color,
+    return Container(
+      constraints: const BoxConstraints(
+        minWidth: 180,
+        minHeight: 50
+      ),
+      child: CustomPaint(
+        painter: _ValueDistributionPainter(
+          distribution,
+          localizations,
+          color,
+        ),
       ),
     );
   }
@@ -147,12 +162,12 @@ class _ValueDistributionPainter extends CustomPainter {
       textPainter.layout();
       final posX = switch(alignment) {
         Alignment.centerLeft => 0.0,
-        Alignment.centerRight => size.width - textPainter.width,
-        Alignment.center => (size.width / 2) - (textPainter.width / 2),
-        _ => throw ArgumentError('Invalid '),
+        Alignment.center => (size.width / 2) - (textPainter.width / 2).clamp(0, size.width),
+        Alignment.centerRight => (size.width - textPainter.width).clamp(0, size.width),
+        _ => throw ArgumentError('Unsupported alignment'),
       };
       final position = Offset(
-        posX,
+        posX.toDouble(),
         size.height / 2 - textPainter.height, // above center
       );
       textPainter.paint(canvas, position);
@@ -161,7 +176,7 @@ class _ValueDistributionPainter extends CustomPainter {
     drawLabel(localizations.minOf(distribution.keys.min.toString()),
         Alignment.centerLeft,);
     drawLabel(localizations.avgOf(distribution.keys.average.round().toString()),
-        Alignment.center,);
+        Alignment.center,); // FIXME: Average doesn't depend on count
     drawLabel(localizations.maxOf(distribution.keys.max.toString()),
         Alignment.centerRight,);
   }
test/ui/components/statistics/value_distribution_test.dart
@@ -0,0 +1,66 @@
+
+import 'package:blood_pressure_app/components/statistics/value_distribution.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../util.dart';
+
+void main() {
+  testWidgets('should show centered info when values are empty', (widgetTester) async {
+    await widgetTester.pumpWidget(materialApp(const ValueDistribution(
+      color: Colors.red,
+      values: [],
+    ),),);
+    expect(find.byType(ValueDistribution), findsOneWidget);
+    final localizations = await AppLocalizations.delegate.load(const Locale('en'));
+    expect(find.byType(Text), findsOneWidget);
+    expect(find.text(localizations.errNoData), findsOneWidget);
+
+    final errorCenter = widgetTester.getCenter(find.byType(Text));
+    final canvasCenter = widgetTester.getCenter(find.byType(MaterialApp));
+    expect(errorCenter, equals(canvasCenter));;
+  },);
+
+  testWidgets('should draw labels at correct positions', (widgetTester) async {
+    await widgetTester.pumpWidget(materialApp(const SizedBox(
+      height: 50,
+      width: 180,
+      child: ValueDistribution(
+        color: Colors.red,
+        values: [5,6,3,8,8,10], // min 3, max 10, avg 6 + 2/3
+      ),
+    ),),);
+
+    expect(find.byType(ValueDistribution), findsOneWidget);
+    expect(find.byType(ValueDistribution),
+        paintsExactlyCountTimes(#drawParagraph, 3),);
+    const posBelowCenter = 9.0;
+    expect(find.byType(ValueDistribution), paints
+      ..paragraph(offset: const Offset(0.0, posBelowCenter))
+      ..paragraph(offset: const Offset(66.0, posBelowCenter)) // overflows at the end
+      ..paragraph(offset: const Offset(68.0, posBelowCenter)),
+    );
+  },);
+
+  testWidgets('should correct amount of value bars', (widgetTester) async {
+    await widgetTester.pumpWidget(materialApp(const SizedBox(
+      height: 50,
+      width: 180,
+      child: ValueDistribution(
+        color: Colors.red,
+        values: [1,2,3,3,4,5], // min 3, max 10, avg 6 + 2/3
+      ),
+    ),),);
+
+    expect(find.byType(ValueDistribution), findsOneWidget);
+    expect(find.byType(ValueDistribution), paints
+      ..line(color: Colors.red.shade500)
+      ..line(color: Colors.red.shade500)
+      ..line(color: Colors.red.shade500)
+      ..line(color: Colors.red.shade500)
+      ..line(color: Colors.red.shade500)
+      ..line(color: Colors.white70) // start drawing decoration
+    ,);
+  },);
+}