v1.11.0 : inactifs regroupés en bas de la page Traitements + cartes atténuées

- Demande : « les traitements inactifs sont peu discernables — les placer
  à la fin (tout en bas), et peut-être les rendre plus distincts ».
- Helper pur treatmentsForDisplay (tri STABLE : actifs d'abord, inactifs
  après, ordre relatif conservé dans chaque groupe).
- LazyColumn en 3 sections : actifs / en-tête inactive_section (seulement
  si des inactifs existent) / inactifs — cartes alpha 0.55 en plus du
  badge, lisibles et tappables (l'édition d'un archivé reste possible).
- Miroir web : treatmentsForDisplay dans js/data/models.js + rendu
  sections + opacity 0.55 + i18n inactive_section FR/EN (web v1.11.0 sync).
- 5 tests JVM TreatmentsDisplayTest + 4 tests web (miroir).

264 tests JVM + 14 UI + lint verts ; web 184 tests + E2E verts (check.sh).
Validé émulateur sur APK RELEASE avec données réelles seedées (5 actifs en
haut, en-tête + EV-old/EEn-old tout en bas, 0 crash).
This commit is contained in:
Siphonight 2026-09-29 07:31:25 +02:00
parent 5cd1023af1
commit a0f3ac59f9
9 changed files with 188 additions and 17 deletions

View File

@ -18,8 +18,8 @@ or Huawei Health) and JSON backup. **100% local, no account, no server.**
> for informational purposes — they are not measurements. Always rely on your
> blood tests and on your endocrinologist's guidance.
- **Status**: v1.10.0 — Android build ✅, **lint clean** ✅, **259 unit tests** ✅
(225 without the local test data; 6 regressions pinned on real data
- **Status**: v1.11.0 — Android build ✅, **lint clean** ✅, **264 unit tests** ✅
(230 without the local test data; 6 regressions pinned on real data
**not committed to the repo**), **14 Compose UI tests** ✅ (emulator),
smartwatch integration = notifications ✅, **private Gitea repo + releases
with APK** ✅
@ -70,7 +70,8 @@ or Huawei Health) and JSON backup. **100% local, no account, no server.**
**interval in days between doses** displayed
- **Inactive treatments**: an archived treatment disappears from input and
reminders, but **its whole history stays simulated and calibrated** — useful
for a valerate → enanthate transition
for a valerate → enanthate transition; **grouped at the very bottom** of the
Treatments page under a dedicated header, dimmed cards (v1.11.0)
- **Blood tests**: **E2 + T in a single entry** (each optional), displayed
**side by side** when they share the same date/time, editable (tap → pick the
entry); T units: ng/mL, ng/dL, ng/L, nmol/L
@ -129,7 +130,7 @@ Gradle 9.7.1.
git clone <repo> && cd HormoneTrack
echo "sdk.dir=/path/to/android-sdk" > local.properties # or ANDROID_HOME
./gradlew assembleDebug # APK: app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest # 259 tests (225 without local test data)
./gradlew testDebugUnitTest # 264 tests (230 without local test data)
./gradlew connectedDebugAndroidTest # 14 UI tests (emulator/device required)
./gradlew lint # clean lint required before a release
```

View File

@ -18,7 +18,7 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
> sang et aux consignes de ton endocrinologue.
- **Statut** : v1.10.0 — build Android ✅, **lint vert** ✅, **259 tests unitaires** ✅ (225 sans les données de test locales ; 6 régressions épinglées sur données réelles **non versionnées**), **14 tests UI Compose** ✅ (émulateur), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
- **Statut** : v1.11.0 — build Android ✅, **lint vert** ✅, **264 tests unitaires** ✅ (230 sans les données de test locales ; 6 régressions épinglées sur données réelles **non versionnées**), **14 tests UI Compose** ✅ (émulateur), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
@ -70,7 +70,8 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
**intervalle en jours entre dosages** affiché
- **Traitements inactifs** : un traitement archivé n'apparaît plus dans la saisie
ni dans les rappels, mais **tout son historique reste simulé et calibré** —
utile pour une transition valerate → enanthate
utile pour une transition valerate → enanthate ; **regroupés tout en bas** de la
page Traitements sous un en-tête dédié, cartes atténuées (v1.11.0)
- **Analyses de sang** : **E2 + T en une seule entrée** (chacune optionnelle), affichées
**côte à côte** quand elles partagent la même date/heure, éditables (tap → choix de
l'entrée) ; unités T : ng/mL, ng/dL, ng/L, nmol/L
@ -128,7 +129,7 @@ par AGP si les licences sont signées). Le wrapper télécharge Gradle 9.7.1.
git clone <repo> && cd HormoneTrack
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest # 259 tests (225 sans les données locales)
./gradlew testDebugUnitTest # 264 tests (230 sans les données locales)
./gradlew connectedDebugAndroidTest # 14 tests UI (émulateur/appareil requis)
./gradlew lint # lint vert obligatoire avant release
```

View File

@ -33,8 +33,8 @@ android {
// tag : v1.3.0→v1.3.2 contenaient tous versionCode 14 / "1.3.0"
// (bump jamais commité) → BuildConfig.VERSION_NAME était faux dans
// les APK publiés (Paramètres + titre du dialog « Nouveautés »).
versionCode = 47
versionName = "1.10.1"
versionCode = 48
versionName = "1.11.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@ -27,6 +27,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.hormonetrack.R
@ -36,6 +37,21 @@ import com.hormonetrack.data.model.modelLabelRes
import com.hormonetrack.ui.LocalAppContainer
import com.hormonetrack.ui.components.formatDose
/**
* ORDRE D'AFFICHAGE de la page Traitements (v1.11.0, demande utilisatrice) :
* les ACTIFS d'abord, les INACTIFS regroupés TOUT EN BAS — l'ancien ordre
* brut du DAO mélangeait les deux groupes et le badge discret ne suffisait
* plus à les discerner (« peu discernables », remontée).
*
* Tri STABLE : l'ordre relatif À L'INTÉRIEUR de chaque groupe est conservé
* (le tri ne réordonne jamais les actifs entre eux, ni les inactifs entre
* eux). PUR (aucune dépendance Android) → testable en JVM
* (TreatmentsDisplayTest) ; miroir web `treatmentsForDisplay`
* (js/data/models.js, mêmes sémantiques).
*/
fun treatmentsForDisplay(treatments: List<Treatment>): List<Treatment> =
treatments.sortedBy { !it.isActive }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TreatmentsScreen(
@ -45,6 +61,11 @@ fun TreatmentsScreen(
val container = LocalAppContainer.current
val repo = container.repository
val treatments by repo.allTreatments.collectAsState(initial = emptyList())
// v1.11.0 : actifs d'abord, inactifs regroupés tout en bas
// (cf [treatmentsForDisplay]) — mémoïsé sur la liste brute.
val displayTreatments = remember(treatments) { treatmentsForDisplay(treatments) }
val activeTreatments = displayTreatments.filter { it.isActive }
val inactiveTreatments = displayTreatments.filter { !it.isActive }
Scaffold(
floatingActionButton = {
@ -58,22 +79,44 @@ fun TreatmentsScreen(
title = { Text(stringResource(R.string.nav_treatments)) }
)
LazyColumn(Modifier.fillMaxSize()) {
items(treatments, key = { it.id }) { tr ->
items(activeTreatments, key = { it.id }) { tr ->
TreatmentCard(tr, onClick = { onEdit(tr.id) })
}
// En-tête de section SEULEMENT s'il existe des inactifs —
// la séparation visuelle (en-tête + atténuation, cf
// TreatmentCard) rend le groupe lisible d'un coup d'œil.
if (inactiveTreatments.isNotEmpty()) {
item(key = "inactive_section") {
Text(
stringResource(R.string.inactive_section),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp)
)
}
}
items(inactiveTreatments, key = { it.id }) { tr ->
TreatmentCard(tr, dimmed = true, onClick = { onEdit(tr.id) })
}
}
}
}
}
/**
* Carte d'un traitement. @param dimmed : carte INACTIVE (v1.11.0) → contenu
* atténué (alpha réduit) en PLUS du badge : second signal visuel demandé
* (« plus distincts des actifs ») — la carte reste lisible et tappable.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TreatmentCard(tr: Treatment, onClick: () -> Unit) {
private fun TreatmentCard(tr: Treatment, dimmed: Boolean = false, onClick: () -> Unit) {
Card(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 6.dp)
.alpha(if (dimmed) 0.55f else 1f)
) {
Column(Modifier.padding(12.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {

View File

@ -144,6 +144,9 @@
<string name="dose_unit">Unité</string>
<string name="active">Actif</string>
<string name="inactive">inactif</string>
<!-- v1.11.0 : en-tête de la section des traitements inactifs (regroupés
en bas de la page Traitements) -->
<string name="inactive_section">Traitements inactifs</string>
<string name="no_treatment_hint">Crée d\'abord un traitement (onglet Traitements).</string>
<string name="type_estradiol">Œstradiol</string>

View File

@ -144,6 +144,9 @@
<string name="dose_unit">Unit</string>
<string name="active">Active</string>
<string name="inactive">inactive</string>
<!-- v1.11.0 : en-tête de la section des traitements inactifs (regroupés
en bas de la page Traitements) -->
<string name="inactive_section">Inactive treatments</string>
<string name="no_treatment_hint">Create a treatment first (Treatments tab).</string>
<string name="type_estradiol">Estradiol</string>

View File

@ -0,0 +1,86 @@
package com.hormonetrack.ui
import com.hormonetrack.data.model.AdministrationRoute
import com.hormonetrack.data.model.Treatment
import com.hormonetrack.data.model.TreatmentType
import com.hormonetrack.ui.screens.treatmentsForDisplay
import org.junit.Assert.assertEquals
import org.junit.Test
/**
* Tests de l'ORDRE D'AFFICHAGE de la page Traitements (v1.11.0, demande
* utilisatrice : « les traitements inactifs faudrait les placer à la fin
* (tout en bas) de la liste ») — [treatmentsForDisplay] :
* - les ACTIFS d'abord, les INACTIFS regroupés après ;
* - tri STABLE : l'ordre relatif DANS chaque groupe est conservé (le tri
* ne réordonne jamais les actifs entre eux, ni les inactifs entre eux) ;
* - cas limites : liste vide, tout actif, tout inactif (identité).
*
* PUR (aucune dépendance Android) ; miroir web `treatmentsForDisplay`
* (js/data/models.js, tests/treatments-display.test.js).
*/
class TreatmentsDisplayTest {
private fun tr(id: Long, name: String, isActive: Boolean) = Treatment(
id = id, name = name, type = TreatmentType.ESTRADIOL,
route = AdministrationRoute.INJECTION_IM,
doseAmount = 5.0, doseUnit = "mg",
isActive = isActive, createdAt = 0
)
@Test
fun `inactive treatments are grouped at the end`() {
val mixed = listOf(
tr(1, "EEn", true),
tr(2, "EV-old", false),
tr(3, "CPA", true),
tr(4, "EEn-old", false),
tr(5, "Ritaline", true)
)
val display = treatmentsForDisplay(mixed)
assertEquals(
"actifs d'abord (ordre conservé), inactifs après (ordre conservé)",
listOf(1L, 3L, 5L, 2L, 4L),
display.map { it.id }
)
assertEquals(
"aucun inactif avant un actif",
0,
display.indexOfFirst { !it.isActive }.let { firstInactive ->
if (firstInactive < 0) 0
else display.subList(firstInactive, display.size).count { it.isActive }
}
)
}
@Test
fun `ordering inside each group is preserved (stable sort)`() {
val mixed = listOf(
tr(10, "A-inactif", false),
tr(11, "B-actif", true),
tr(12, "C-inactif", false),
tr(13, "D-actif", true)
)
assertEquals(
listOf(11L, 13L, 10L, 12L),
treatmentsForDisplay(mixed).map { it.id }
)
}
@Test
fun `all active - order unchanged`() {
val all = listOf(tr(3, "C", true), tr(1, "A", true), tr(2, "B", true))
assertEquals(listOf(3L, 1L, 2L), treatmentsForDisplay(all).map { it.id })
}
@Test
fun `all inactive - order unchanged`() {
val all = listOf(tr(2, "B", false), tr(1, "A", false))
assertEquals(listOf(2L, 1L), treatmentsForDisplay(all).map { it.id })
}
@Test
fun `empty list stays empty`() {
assertEquals(0, treatmentsForDisplay(emptyList()).size)
}
}

View File

@ -3,6 +3,31 @@
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.4.10`, …).
## [1.11.0] — 2026-09-29 (versionCode 48)
### Ajouté — les traitements inactifs regroupés en bas de la page Traitements
- **Demande** : « les traitements inactifs sont peu discernables par rapport
aux traitements actifs — les placer à la fin (tout en bas) de la liste
pour éviter les confusions ».
- **Tri** : les ACTIFS d'abord, les INACTIFS regroupés **tout en bas**, sous
un en-tête de section « Traitements inactifs » (n'apparaît que s'il
existe des inactifs). Tri STABLE : l'ordre relatif à l'intérieur de
chaque groupe est conservé — le tri ne réordonne jamais les actifs entre
eux, ni les inactifs entre eux.
- **Distinction visuelle renforcée** : les cartes inactives sont ATTÉNUÉES
(alpha 0,55) en plus du badge « inactif » — second signal demandé
(« peut-être les rendre plus distincts ») ; elles restent lisibles et
tappables (l'édition d'un traitement archivé doit rester possible).
- Helper pur `treatmentsForDisplay` (Kotlin + **miroir web** en
`js/data/models.js`) — 5 tests JVM (`TreatmentsDisplayTest`) + 4 tests
web (`tests/treatments-display.test.js`).
- Vérifié émulateur avec données réelles seedées : les 5 actifs en haut,
en-tête puis les 2 inactifs (EV-old, EEn-old) tout en bas.
Vérifié : **264 tests JVM** (230 sans données locales) + **14 tests UI** +
lint verts ; **web 184 tests + E2E verts** (`check.sh`).
## [1.10.1] — 2026-09-28 (versionCode 47)
### Amélioré — durcissement du système de rappels (revue de code post-v1.10.0)

View File

@ -63,6 +63,7 @@ Fonctionnalités v1 :
| Date | Événement |
|---|---|
| 29 sept. 2026 (session v1.11.0) | **Page Traitements : inactifs regroupés en bas + atténués** (demande : « peu discernables — les placer à la fin, et peut-être les rendre plus distincts »). Helper PUR `treatmentsForDisplay` (tri STABLE : actifs d'abord, inactifs après, ordre relatif conservé DANS chaque groupe) + LazyColumn en 3 sections (actifs / en-tête `inactive_section` SEULEMENT si des inactifs existent / inactifs `dimmed = true`) + cartes inactives **alpha 0,55** en plus du badge (lisibles et tappables — l'édition d'un archivé doit rester possible). **Miroir web** : `treatmentsForDisplay` dans `js/data/models.js` (node-safe, pas de DOM) + rendu sections + `opacity:0.55` + clés i18n `inactive_section` FR/EN. Tests : **5 JVM** (`TreatmentsDisplayTest` : regroupement, stabilité, tout-actif, tout-inactif, vide) + **4 web** (`tests/treatments-display.test.js`) — pas de test UI instrumenté pour l'en-tête (dépendrait de données inactives, contre la convention data-indépendance §8.ter) : couverture JVM + validation émulateur (5 actifs en haut, en-tête + EV-old/EEn-old tout en bas, dumps tr1/tr2). 264 tests JVM + lint ; web 184 tests + E2E verts (check.sh). |
| 28 sept. 2026 (session v1.10.1) | **Durcissement rappels — revue de code post-v1.10.0** (audit demandé : « optimisations ou problèmes, web et Android »). (a) **`launchMode="singleTop"`** : le combo `standard`+`CLEAR_TOP` recréait l'activité au tap notification app-ouverte et rendait le `onNewIntent` de la v1.10.0 inopérant (code mort) — désormais délivré SANS recreation (état UI préservé). (b) **`EXTRA_PLANNED_AT`** : le garde « créneau déjà honoré » évaluait le jour au moment du FEU — une alarme inexacte (`setWindow`/Doze) glissant 23:58→00:02 évaluait le mauvais jour ; l'instant planifié voyage dans l'intent (scheduleFor), le snooze le FORWARDE, le garde évalue le jour du CRÉNEAU ; vérifié émulateur (broadcast `planned_at` hier → notif sautée). (c) **`BootReceiver` consolidé** (dette §20.bis #2 FAIT) : `goAsync`+coroutine, `runBlocking` supprimés. (d) Style (import `mutableStateOf`) + audit doc (comptes 223 résiduels §14/#64 et §16.ter → 259 ; §9 et §20.bis à jour). Web NON concerné (miroir lab-timing laissé tel quel — boucle correcte et testée ; `importJson` par lots reste en dette). 259 tests JVM + 14 UI + lint verts. **Publication** : commit d531eca + tag v1.10.1 poussés sur les 2 Gitea, APK release (2,7 Mo) + debug (21 Mo) publiés et vérifiés par téléchargement (cloudyfy id 78, farewell id 1588586) ; validation §16.ter sur l'APK RELEASE AVANT tag (accueil + données, singleTop, planned_at A/B, 0 crash). |
| 27-28 sept. 2026 (session v1.10.0) | **2 fixes + 1 amélioration rappels** (remontées + nouvelles données réelles → 6ᵉ régression épinglée `backup-v1.9.8.json`). (a) **#68 — reco de prise de sang antérieure à la stabilisation** : le « creux » = minimum de la FENÊTRE ENTIÈRE tombait juste après l'injection PRÉCÉDENTE pour les esters à montée lente (EEn pic ~J+5 ≈ intervalle 7 j → creux d'absorption ~3 h post-injection) — date gouvernée par l'injection N−1 (antérieure à la stab) et « juste avant ton injection du … » faux de plusieurs jours (réel : creux 5 oct 02:32 < stab 7 oct 04:03). FIX : creux = niveau PRÉ-INJECTION du créneau (restaure le principe v1.8.0 documenté ; EV inchangé — le point pré-injection EST son min de fenêtre) ; **miroir web appliqué** (lab-timing.js). Tests : RegressionUserCase6Test data-driven dont balayage de `now` ±60 j (invariant « creux ≥ stab » à TOUT INSTANT) ; les 17 LabTimingTest passent inchangés. (b) **#69 — dialog fantôme** : tap notification → dialog ; le fermer + changer d'onglet + revenir sur l'accueil le ROUVRAIT à chaque fois (paramètre d'activité vs consommation dans les remember de Home, détruits à chaque sortie d'onglet — reproduit sur émulateur AVANT fix). FIX : `MutableState<Long?>` détenu par MainActivity, consommé UNE fois (extras retirés de l'intent après lecture ; bonus : tap notification app-ouverte ouvre aussi le dialog). Test UI `LogDoseRequestTest` + vérification émulateur. (c) **Rappel sauté si dose déjà loggée** (demande) : garde au déclenchement — `hasDoseLoggedOnDay` (pur, jour calendaire LOCAL) dans ReminderReceiver → notif sautée + re-programmation créneau suivant dans tous les cas ; 6 tests JVM + vérification A/B émulateur (EEn dose-du-jour → 0 notif, Fluoxetine sans dose → 1 notif). **259 tests JVM + 14 UI + lint** ; **web 180 tests + E2E verts** (check.sh). **Publication** : commit a4f859f + tag v1.10.0 poussés sur les 2 Gitea, APK release (2,7 Mo) + debug (21 Mo) publiés et vérifiés par téléchargement (cloudyfy id 77, farewell id 1588583). **Web miroir v1.10.0 sync** (fix #68 lab-timing, WEB_VERSION alignée, commit b865b16 + tag poussés ; #69/garde rappel = Android-only, notés dans le changelog web). |
| 22 sept. 2026 (session v1.9.8) | **Socle qualité : tests UI Compose + signature release dédiée + découpage Settings** (revue de code du 21 sept. : « pas de CI, pas de tests UI, APK signé clé debug, écrans qui grandissent »). (a) **13 tests UI instrumentés** (`app/src/androidTest/uitest/`, §8.ter) : navigation 5 onglets (preuves = éléments inconditionnels : chips 24 h, FAB), dialog Nouveautés (épinglage du fix v1.3.2 : ne réapparaît pas après dismiss+restart), sections Paramètres + version installée = BuildConfig (le bug #46 ne peut plus revenir) ; déterminisme = pm grant shell pré-launch + markChangelogSeen + assertions via targetContext (indépendant FR/EN) + zéro dépendance aux données Room ; deps androidTest (BOM + ui-test-junit4 + ext:junit-ktx + junit explicite). (b) **Signature release conditionnelle** (§16.quater) : `scripts/make-release-keystore.sh` (shellcheck+shfmt) génère keystore + keystore.properties (gitignorés), `assembleRelease` l'utilise s'il existe, sinon fallback clé debug inchangé ; migration backup→désinstall→réimport documentée. (c) **SettingsScreen découpé** (840 → 377 + 11 fichiers 33–164 lignes dans `ui/screens/settings/`, §10) — déplacement pur, commentaires historiques conservés, ChartScreen volontairement entier (garde de source #54). **Vérifications** : 249 tests JVM + 13 UI verts, lint vert, assembleDebug/Release OK, **smoke test RELEASE émulateur** (accueil, 5 onglets, Paramètres complet scroll compris — version 1.9.8 visible, éditeur traitement, dialog dose + garde « crée un traitement », 0 crash logcat — anti-#64). **README EN** (README.en.md, liens FR/EN croisés ; DEVELOPPEMENT.md reste FR par choix assumé). **Publication** : commit 09cddfc + tag v1.9.8 poussés sur les 2 Gitea, APK release (2,7 Mo) + debug (21 Mo) publiés et vérifiés par téléchargement (cloudyfy id 75, farewell id 1588581). Web miroir NON concerné (v1.9.8 = socle Android uniquement : tests instrumentés, signature APK, découpage Compose, README du dépôt Android — rien à porter côté web, qui reste à v1.9.7). |
@ -754,7 +755,7 @@ devient à son tour**.
## 8. Tests unitaires
**259 tests JVM, tous verts** (`./gradlew testDebugUnitTest`) — **225 sans
**264 tests JVM, tous verts** (`./gradlew testDebugUnitTest`) — **230 sans
les données de test locales** (cf §8.bis : les 6 classes de régression sont
skippées via `Assume`). Dépendance : JUnit 4.13.2.
Emplacement : `app/src/test/java/com/hormonetrack/`. Répertoire de travail d'exécution =
@ -987,9 +988,9 @@ dépôt (ni maintenant, ni si le repo devient public un jour).
dans le code (le scanner refuserait, et ce serait une fuite).
- les tests font `Assume.assumeTrue(file.exists())` dans le `@Before` : **sans le
fichier, la classe est IGNORÉE** (skipped, pas failed) — un clone neuf ou une CI
exécute 225 tests au lieu de 259 (v1.10.0 ; chiffres historiques : 44/87 à v1.3.x,
exécute 230 tests au lieu de 264 (v1.11.0 ; chiffres historiques : 44/87 à v1.3.x,
150/172 à v1.4.x, 160/183 à v1.5.0, 168/191 à v1.6.0, 177/200 à v1.7.0,
181/211 à v1.7.1, 190/220 à v1.8.0, 219/249 à v1.9.x) ;
181/211 à v1.7.1, 190/220 à v1.8.0, 219/249 à v1.9.x, 225/259 à v1.10.x) ;
- le workdir des tests Gradle est le dossier du module (`app/`) → les tests
cherchent les fichiers à plusieurs chemins (`../local-test-data/…` en premier) ;
- **pour les lancer** : exporter un backup JSON depuis l'app → l'enregistrer sous
@ -1199,7 +1200,12 @@ Sur la montre : remontée par Gadgetbridge **ou** Huawei Health (cf §17).
les valeurs — choix de design : les labs sont prélevés ensemble) ; suggestions
d'unités pg/mL, ng/mL, ng/dL, ng/L, nmol/L, mIU/L
- `TreatmentsScreen` : cartes (nom, route, dose, chips ester·modèle / Tmax / ×scale / ⏰,
badge inactif), FAB → éditeur
badge inactif), FAB → éditeur. **v1.11.0** : ordre d'affichage =
`treatmentsForDisplay` (pur, tri STABLE — actifs d'abord, inactifs
regroupés TOUT EN BAS sous un en-tête `inactive_section` qui n'apparaît
que s'il existe des inactifs) + cartes inactives **atténuées** (alpha
0,55) — 5 tests `TreatmentsDisplayTest` ; miroir web
(`js/data/models.js` + `tests/treatments-display.test.js`)
- `TreatmentEditorScreen` : 12 presets (`PKPresets`, cf `nameRes`) pré-remplissent tout ;
champs conditionnels (ester+modèle si injection, Bateman sinon) ; carte Calibration
(scaleFactor + « Calibrer avec les analyses ») ; **section « Posologie » (v1.2 :
@ -2036,8 +2042,8 @@ de signature est requise — elle est désormais câblée :
1. **Bumper la version** dans `app/build.gradle.kts` : `versionCode = N+1`,
`versionName = "X.Y.Z+1"` (SemVer : fix = Z, feature = Y).
2. **Tests verts obligatoires** : `./gradlew testDebugUnitTest` — 259 au
total, 225 si `local-test-data/` est absent (les 6 classes de régression
2. **Tests verts obligatoires** : `./gradlew testDebugUnitTest` — 264 au
total, 230 si `local-test-data/` est absent (les 6 classes de régression
réelles sont skippées via `Assume`) ; **lint vert
obligatoire** : `./gradlew lint` (v1.3.4 — `StringFormatMatches` aurait
attrapé les crashs #47/#48 dès v1.3.1).
@ -2334,6 +2340,9 @@ Sur le téléphone de test (à compléter par l'utilisatrice) :
cohérente avec le créneau affiché (~juste avant)
- [ ] **v1.10.0** : logger la dose du jour AVANT l'heure de rappel → la
notification ne sonne pas ; le rappel suivant repart au créneau suivant
- [ ] **v1.11.0** : page Traitements — les inactifs sont TOUT EN BAS sous
l'en-tête « Traitements inactifs », cartes atténuées ; éditer un
inactif fonctionne toujours
- [ ] **v1.2.x** : graphique panoramique (glisser → passé, bouton « Revenir à maintenant »)
- [ ] **v1.2.x** : toggles Estrannaise/TFS indépendants (les deux courbes superposées)
- [ ] **v1.2.x** : chip « Prévision » (configurer la Posologie d'un traitement d'abord)