Passage sur le Redmi contre `.75`. Aucun des trois n'était visible en test. 1. LA PRODUCTION S'AFFICHAIT NÉGATIVE — « -8.9 kW · Solaire » sous 9 kW de soleil. `currentPowerProduction` est publié négatif par nymea et le service garde les signes bruts par politique ; la carte imprimait la valeur telle quelle. Ce qui rend le défaut net plutôt qu'opinable : la même carte normalisait DÉJÀ le réseau et la batterie par `.abs()` — la branche solaire était la seule oubliée, donc un oubli et non une convention. 2. LE LIBELLÉ SE COUPAIT AU MILIEU DU MOT — « Autoconsommat / ion », alors que le commentaire du code disait vouloir l'éviter. La cause n'est pas la longueur mais l'ÉCHELLE du texte : police système au-dessus de 100 %, et `maxLines: 2` ne protège de rien puisque Flutter coupe un mot long sans espace, sans même afficher l'ellipse. `FittedBox(scaleDown)` garde le mot entier et ne fait rien quand la place suffit. 3. MA LIGNE DES RATIOS RÉPÉTAIT LES TUILES — « Instantané depuis 09:23 — autoconsommation 52 %, autonomie 54 % » sous des tuiles affichant 52 % et 54 %. Les deux chemins étaient corrects et les valeurs coïncidaient vraiment (bilan 51,8/54,1 sur 00:00→16:45, box 51,9/53,7 depuis 09:23). Rien de faux, et illisible quand même : je déclenchais sur l'ORIGINE, alors qu'un lecteur compare des NOMBRES. La ligne sort désormais quand l'origine diffère ET que l'écart dépasse 2 points ; le relevé du jour est devenu un test. Vérifié à l'écran après correction : le libellé tient sur une ligne, la ligne des ratios se tait. Le signe, lui, N'A PAS pu être revu — la production du banc était retombée à 0 W à 17:03, et `abs(0) == 0`. Une correction qu'on ne peut pas voir demande une garde qui se voit : `flux_signe_test.dart` la pose. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKM8FDNHZWogAsjjfB8pSw
133 lines
5.6 KiB
Dart
133 lines
5.6 KiB
Dart
/// **Deux grandeurs, deux phrases, aucune ne ment.**
|
|
///
|
|
/// Les tuiles disent la JOURNÉE — différence de compteurs sur `periodStartedAt → maintenant`.
|
|
/// La ligne des ratios dit ce que la BOX publie, qui part de la **baseline** et non du début
|
|
/// de journée.
|
|
///
|
|
/// Le cas est arrivé de lui-même le 2026-09-05 : `nymead` redémarré à 09:23, sonde à 09:26,
|
|
/// et la box publiait **100 % d'autoconsommation**. Vrai sur trois minutes ; absurde intitulé
|
|
/// « aujourd'hui ». C'est ce que le drapeau rend lisible — et c'est pour ça que la ligne ne
|
|
/// s'affiche QUE lorsque l'origine diffère : quand les deux coïncident, une phrase de plus
|
|
/// serait du bruit, et un texte permanent cesse d'être lu.
|
|
library;
|
|
|
|
import 'package:etm_powersync_app/features/dashboard/widgets/kpi_row.dart';
|
|
import 'package:etm_powersync_app/models/daily_balance.dart';
|
|
import 'package:etm_powersync_app/models/energy_data.dart';
|
|
import 'package:etm_powersync_app/models/energy_ratios_snapshot.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// Relevé `.75` du 2026-09-05 09:26, après le redémarrage de 09:23.
|
|
EnergyRatiosSnapshot _releve({int measuredSince = 1788592995}) =>
|
|
EnergyRatiosSnapshot.fromJson({
|
|
'autonomyRate': 28.570199887530475,
|
|
'selfConsumptionRate': 100,
|
|
'periodStartedAt': 1788559200,
|
|
'measuredSince': measuredSince,
|
|
'timeZone': 'Europe/Paris',
|
|
});
|
|
|
|
DailyBalance _bilan() => DailyBalance(
|
|
from: DateTime(2026, 9, 5, 0, 15),
|
|
to: DateTime(2026, 9, 5, 9, 26),
|
|
periodStart: DateTime(2026, 9, 5),
|
|
sampleCount: 37,
|
|
productionKwh: 8.0,
|
|
consumptionKwh: 12.0,
|
|
importKwh: 5.0,
|
|
exportKwh: 1.0,
|
|
);
|
|
|
|
Widget _harness({EnergyRatiosSnapshot? ratios}) => MaterialApp(
|
|
home: Scaffold(
|
|
body: SingleChildScrollView(
|
|
child: KpiRow(
|
|
data: const EnergyData(), balance: _bilan(), ratios: ratios),
|
|
),
|
|
),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('baseline reposée en journée : l\'origine est DITE', (tester) async {
|
|
await tester.binding.setSurfaceSize(const Size(1000, 2000));
|
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
|
await tester.pumpWidget(_harness(ratios: _releve()));
|
|
|
|
expect(find.textContaining('Instantané depuis 09:23'), findsOneWidget);
|
|
// Le 100 % est affiché — il est vrai — mais jamais sous le mot « aujourd'hui ».
|
|
expect(find.textContaining('autoconsommation 100 %'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('les tuiles restent la JOURNÉE, calculée sur la fenêtre de la box',
|
|
(tester) async {
|
|
await tester.binding.setSurfaceSize(const Size(1000, 2000));
|
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
|
await tester.pumpWidget(_harness(ratios: _releve()));
|
|
// Les anneaux animent leur valeur : sans laisser l'animation finir, ils affichent 0 —
|
|
// une absence qui se lirait comme un succès si on cherchait justement une absence.
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Aujourd\'hui'), findsOneWidget);
|
|
// 8 kWh produits, 1 exporté -> 87,5 % ; et surtout PAS le 100 % de la box.
|
|
expect(find.textContaining('88'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('origine confondue avec le début de journée : rien de plus',
|
|
(tester) async {
|
|
await tester.binding.setSurfaceSize(const Size(1000, 2000));
|
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
|
// Le cas normal : la baseline date du début de journée.
|
|
await tester.pumpWidget(_harness(ratios: _releve(measuredSince: 1788559200)));
|
|
expect(find.textContaining('Instantané depuis'), findsNothing);
|
|
});
|
|
|
|
testWidgets('les deux coïncident : la ligne se TAIT — vu sur appareil', (tester) async {
|
|
await tester.binding.setSurfaceSize(const Size(1000, 2000));
|
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
|
// Relevé du 2026-09-05 16:52 : bilan 51,8 / 54,1 sur 00:00→16:45, box 51,9 / 53,7
|
|
// depuis 09:23. L'origine diffère bien, mais les nombres non — et la ligne écrivait
|
|
// « 52 %, 54 % » juste sous des tuiles qui affichaient déjà 52 % et 54 %.
|
|
final coincide = EnergyRatiosSnapshot.fromJson({
|
|
'autonomyRate': 53.67,
|
|
'selfConsumptionRate': 51.92,
|
|
'periodStartedAt': 1788559200,
|
|
'measuredSince': 1788592995,
|
|
'timeZone': 'Europe/Paris',
|
|
});
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
body: SingleChildScrollView(
|
|
child: KpiRow(
|
|
data: const EnergyData(),
|
|
// 8 kWh produits / 1 exporté -> 87,5 % ; 12 consommés / 5 importés -> 58,3 %.
|
|
// On force ici un bilan proche des valeurs de la box pour éprouver le seuil.
|
|
balance: DailyBalance(
|
|
from: DateTime(2026, 9, 5, 0, 15),
|
|
to: DateTime(2026, 9, 5, 16, 45),
|
|
periodStart: DateTime(2026, 9, 5),
|
|
sampleCount: 72,
|
|
productionKwh: 59.89,
|
|
consumptionKwh: 57.31,
|
|
importKwh: 26.31,
|
|
exportKwh: 28.89,
|
|
),
|
|
ratios: coincide,
|
|
),
|
|
),
|
|
),
|
|
));
|
|
await tester.pumpAndSettle();
|
|
expect(find.textContaining('Instantané depuis'), findsNothing,
|
|
reason: 'répéter les mêmes nombres oblige le lecteur à raisonner pour '
|
|
'découvrir que les fenêtres diffèrent');
|
|
});
|
|
|
|
testWidgets('box antérieure : aucune ligne, aucune supposition', (tester) async {
|
|
await tester.binding.setSurfaceSize(const Size(1000, 2000));
|
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
|
await tester.pumpWidget(_harness());
|
|
expect(find.textContaining('Instantané'), findsNothing);
|
|
});
|
|
}
|